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);
7129 * @class Roo.bootstrap.TableCell
7130 * @extends Roo.bootstrap.Component
7131 * Bootstrap TableCell class
7132 * @cfg {String} html cell contain text
7133 * @cfg {String} cls cell class
7134 * @cfg {String} tag cell tag (td|th) default td
7135 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7136 * @cfg {String} align Aligns the content in a cell
7137 * @cfg {String} axis Categorizes cells
7138 * @cfg {String} bgcolor Specifies the background color of a cell
7139 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7140 * @cfg {Number} colspan Specifies the number of columns a cell should span
7141 * @cfg {String} headers Specifies one or more header cells a cell is related to
7142 * @cfg {Number} height Sets the height of a cell
7143 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7144 * @cfg {Number} rowspan Sets the number of rows a cell should span
7145 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7146 * @cfg {String} valign Vertical aligns the content in a cell
7147 * @cfg {Number} width Specifies the width of a cell
7150 * Create a new TableCell
7151 * @param {Object} config The config object
7154 Roo.bootstrap.TableCell = function(config){
7155 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7158 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7178 getAutoCreate : function(){
7179 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7199 cfg.align=this.align
7205 cfg.bgcolor=this.bgcolor
7208 cfg.charoff=this.charoff
7211 cfg.colspan=this.colspan
7214 cfg.headers=this.headers
7217 cfg.height=this.height
7220 cfg.nowrap=this.nowrap
7223 cfg.rowspan=this.rowspan
7226 cfg.scope=this.scope
7229 cfg.valign=this.valign
7232 cfg.width=this.width
7251 * @class Roo.bootstrap.TableRow
7252 * @extends Roo.bootstrap.Component
7253 * Bootstrap TableRow class
7254 * @cfg {String} cls row class
7255 * @cfg {String} align Aligns the content in a table row
7256 * @cfg {String} bgcolor Specifies a background color for a table row
7257 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7258 * @cfg {String} valign Vertical aligns the content in a table row
7261 * Create a new TableRow
7262 * @param {Object} config The config object
7265 Roo.bootstrap.TableRow = function(config){
7266 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7269 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7277 getAutoCreate : function(){
7278 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7288 cfg.align = this.align;
7291 cfg.bgcolor = this.bgcolor;
7294 cfg.charoff = this.charoff;
7297 cfg.valign = this.valign;
7315 * @class Roo.bootstrap.TableBody
7316 * @extends Roo.bootstrap.Component
7317 * Bootstrap TableBody class
7318 * @cfg {String} cls element class
7319 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7320 * @cfg {String} align Aligns the content inside the element
7321 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7322 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7325 * Create a new TableBody
7326 * @param {Object} config The config object
7329 Roo.bootstrap.TableBody = function(config){
7330 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7333 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7341 getAutoCreate : function(){
7342 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7356 cfg.align = this.align;
7359 cfg.charoff = this.charoff;
7362 cfg.valign = this.valign;
7369 // initEvents : function()
7376 // this.store = Roo.factory(this.store, Roo.data);
7377 // this.store.on('load', this.onLoad, this);
7379 // this.store.load();
7383 // onLoad: function ()
7385 // this.fireEvent('load', this);
7395 * Ext JS Library 1.1.1
7396 * Copyright(c) 2006-2007, Ext JS, LLC.
7398 * Originally Released Under LGPL - original licence link has changed is not relivant.
7401 * <script type="text/javascript">
7404 // as we use this in bootstrap.
7405 Roo.namespace('Roo.form');
7407 * @class Roo.form.Action
7408 * Internal Class used to handle form actions
7410 * @param {Roo.form.BasicForm} el The form element or its id
7411 * @param {Object} config Configuration options
7416 // define the action interface
7417 Roo.form.Action = function(form, options){
7419 this.options = options || {};
7422 * Client Validation Failed
7425 Roo.form.Action.CLIENT_INVALID = 'client';
7427 * Server Validation Failed
7430 Roo.form.Action.SERVER_INVALID = 'server';
7432 * Connect to Server Failed
7435 Roo.form.Action.CONNECT_FAILURE = 'connect';
7437 * Reading Data from Server Failed
7440 Roo.form.Action.LOAD_FAILURE = 'load';
7442 Roo.form.Action.prototype = {
7444 failureType : undefined,
7445 response : undefined,
7449 run : function(options){
7454 success : function(response){
7459 handleResponse : function(response){
7463 // default connection failure
7464 failure : function(response){
7466 this.response = response;
7467 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7468 this.form.afterAction(this, false);
7471 processResponse : function(response){
7472 this.response = response;
7473 if(!response.responseText){
7476 this.result = this.handleResponse(response);
7480 // utility functions used internally
7481 getUrl : function(appendParams){
7482 var url = this.options.url || this.form.url || this.form.el.dom.action;
7484 var p = this.getParams();
7486 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7492 getMethod : function(){
7493 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7496 getParams : function(){
7497 var bp = this.form.baseParams;
7498 var p = this.options.params;
7500 if(typeof p == "object"){
7501 p = Roo.urlEncode(Roo.applyIf(p, bp));
7502 }else if(typeof p == 'string' && bp){
7503 p += '&' + Roo.urlEncode(bp);
7506 p = Roo.urlEncode(bp);
7511 createCallback : function(){
7513 success: this.success,
7514 failure: this.failure,
7516 timeout: (this.form.timeout*1000),
7517 upload: this.form.fileUpload ? this.success : undefined
7522 Roo.form.Action.Submit = function(form, options){
7523 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7526 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7529 haveProgress : false,
7530 uploadComplete : false,
7532 // uploadProgress indicator.
7533 uploadProgress : function()
7535 if (!this.form.progressUrl) {
7539 if (!this.haveProgress) {
7540 Roo.MessageBox.progress("Uploading", "Uploading");
7542 if (this.uploadComplete) {
7543 Roo.MessageBox.hide();
7547 this.haveProgress = true;
7549 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7551 var c = new Roo.data.Connection();
7553 url : this.form.progressUrl,
7558 success : function(req){
7559 //console.log(data);
7563 rdata = Roo.decode(req.responseText)
7565 Roo.log("Invalid data from server..");
7569 if (!rdata || !rdata.success) {
7571 Roo.MessageBox.alert(Roo.encode(rdata));
7574 var data = rdata.data;
7576 if (this.uploadComplete) {
7577 Roo.MessageBox.hide();
7582 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7583 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7586 this.uploadProgress.defer(2000,this);
7589 failure: function(data) {
7590 Roo.log('progress url failed ');
7601 // run get Values on the form, so it syncs any secondary forms.
7602 this.form.getValues();
7604 var o = this.options;
7605 var method = this.getMethod();
7606 var isPost = method == 'POST';
7607 if(o.clientValidation === false || this.form.isValid()){
7609 if (this.form.progressUrl) {
7610 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7611 (new Date() * 1) + '' + Math.random());
7616 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7617 form:this.form.el.dom,
7618 url:this.getUrl(!isPost),
7620 params:isPost ? this.getParams() : null,
7621 isUpload: this.form.fileUpload
7624 this.uploadProgress();
7626 }else if (o.clientValidation !== false){ // client validation failed
7627 this.failureType = Roo.form.Action.CLIENT_INVALID;
7628 this.form.afterAction(this, false);
7632 success : function(response)
7634 this.uploadComplete= true;
7635 if (this.haveProgress) {
7636 Roo.MessageBox.hide();
7640 var result = this.processResponse(response);
7641 if(result === true || result.success){
7642 this.form.afterAction(this, true);
7646 this.form.markInvalid(result.errors);
7647 this.failureType = Roo.form.Action.SERVER_INVALID;
7649 this.form.afterAction(this, false);
7651 failure : function(response)
7653 this.uploadComplete= true;
7654 if (this.haveProgress) {
7655 Roo.MessageBox.hide();
7658 this.response = response;
7659 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7660 this.form.afterAction(this, false);
7663 handleResponse : function(response){
7664 if(this.form.errorReader){
7665 var rs = this.form.errorReader.read(response);
7668 for(var i = 0, len = rs.records.length; i < len; i++) {
7669 var r = rs.records[i];
7673 if(errors.length < 1){
7677 success : rs.success,
7683 ret = Roo.decode(response.responseText);
7687 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7697 Roo.form.Action.Load = function(form, options){
7698 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7699 this.reader = this.form.reader;
7702 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7707 Roo.Ajax.request(Roo.apply(
7708 this.createCallback(), {
7709 method:this.getMethod(),
7710 url:this.getUrl(false),
7711 params:this.getParams()
7715 success : function(response){
7717 var result = this.processResponse(response);
7718 if(result === true || !result.success || !result.data){
7719 this.failureType = Roo.form.Action.LOAD_FAILURE;
7720 this.form.afterAction(this, false);
7723 this.form.clearInvalid();
7724 this.form.setValues(result.data);
7725 this.form.afterAction(this, true);
7728 handleResponse : function(response){
7729 if(this.form.reader){
7730 var rs = this.form.reader.read(response);
7731 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7733 success : rs.success,
7737 return Roo.decode(response.responseText);
7741 Roo.form.Action.ACTION_TYPES = {
7742 'load' : Roo.form.Action.Load,
7743 'submit' : Roo.form.Action.Submit
7752 * @class Roo.bootstrap.Form
7753 * @extends Roo.bootstrap.Component
7754 * Bootstrap Form class
7755 * @cfg {String} method GET | POST (default POST)
7756 * @cfg {String} labelAlign top | left (default top)
7757 * @cfg {String} align left | right - for navbars
7758 * @cfg {Boolean} loadMask load mask when submit (default true)
7763 * @param {Object} config The config object
7767 Roo.bootstrap.Form = function(config){
7769 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7771 Roo.bootstrap.Form.popover.apply();
7775 * @event clientvalidation
7776 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7777 * @param {Form} this
7778 * @param {Boolean} valid true if the form has passed client-side validation
7780 clientvalidation: true,
7782 * @event beforeaction
7783 * Fires before any action is performed. Return false to cancel the action.
7784 * @param {Form} this
7785 * @param {Action} action The action to be performed
7789 * @event actionfailed
7790 * Fires when an action fails.
7791 * @param {Form} this
7792 * @param {Action} action The action that failed
7794 actionfailed : true,
7796 * @event actioncomplete
7797 * Fires when an action is completed.
7798 * @param {Form} this
7799 * @param {Action} action The action that completed
7801 actioncomplete : true
7805 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7808 * @cfg {String} method
7809 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7814 * The URL to use for form actions if one isn't supplied in the action options.
7817 * @cfg {Boolean} fileUpload
7818 * Set to true if this form is a file upload.
7822 * @cfg {Object} baseParams
7823 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7827 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7831 * @cfg {Sting} align (left|right) for navbar forms
7836 activeAction : null,
7839 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7840 * element by passing it or its id or mask the form itself by passing in true.
7843 waitMsgTarget : false,
7848 * @cfg {Boolean} errorMask (true|false) default false
7853 * @cfg {Number} maskOffset Default 100
7858 * @cfg {Boolean} maskBody
7862 getAutoCreate : function(){
7866 method : this.method || 'POST',
7867 id : this.id || Roo.id(),
7870 if (this.parent().xtype.match(/^Nav/)) {
7871 cfg.cls = 'navbar-form navbar-' + this.align;
7875 if (this.labelAlign == 'left' ) {
7876 cfg.cls += ' form-horizontal';
7882 initEvents : function()
7884 this.el.on('submit', this.onSubmit, this);
7885 // this was added as random key presses on the form where triggering form submit.
7886 this.el.on('keypress', function(e) {
7887 if (e.getCharCode() != 13) {
7890 // we might need to allow it for textareas.. and some other items.
7891 // check e.getTarget().
7893 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7897 Roo.log("keypress blocked");
7905 onSubmit : function(e){
7910 * Returns true if client-side validation on the form is successful.
7913 isValid : function(){
7914 var items = this.getItems();
7918 items.each(function(f){
7924 Roo.log('invalid field: ' + f.name);
7928 if(!target && f.el.isVisible(true)){
7934 if(this.errorMask && !valid){
7935 Roo.bootstrap.Form.popover.mask(this, target);
7942 * Returns true if any fields in this form have changed since their original load.
7945 isDirty : function(){
7947 var items = this.getItems();
7948 items.each(function(f){
7958 * Performs a predefined action (submit or load) or custom actions you define on this form.
7959 * @param {String} actionName The name of the action type
7960 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7961 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7962 * accept other config options):
7964 Property Type Description
7965 ---------------- --------------- ----------------------------------------------------------------------------------
7966 url String The url for the action (defaults to the form's url)
7967 method String The form method to use (defaults to the form's method, or POST if not defined)
7968 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7969 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7970 validate the form on the client (defaults to false)
7972 * @return {BasicForm} this
7974 doAction : function(action, options){
7975 if(typeof action == 'string'){
7976 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7978 if(this.fireEvent('beforeaction', this, action) !== false){
7979 this.beforeAction(action);
7980 action.run.defer(100, action);
7986 beforeAction : function(action){
7987 var o = action.options;
7992 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7994 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7997 // not really supported yet.. ??
7999 //if(this.waitMsgTarget === true){
8000 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8001 //}else if(this.waitMsgTarget){
8002 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8003 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8005 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8011 afterAction : function(action, success){
8012 this.activeAction = null;
8013 var o = action.options;
8018 Roo.get(document.body).unmask();
8024 //if(this.waitMsgTarget === true){
8025 // this.el.unmask();
8026 //}else if(this.waitMsgTarget){
8027 // this.waitMsgTarget.unmask();
8029 // Roo.MessageBox.updateProgress(1);
8030 // Roo.MessageBox.hide();
8037 Roo.callback(o.success, o.scope, [this, action]);
8038 this.fireEvent('actioncomplete', this, action);
8042 // failure condition..
8043 // we have a scenario where updates need confirming.
8044 // eg. if a locking scenario exists..
8045 // we look for { errors : { needs_confirm : true }} in the response.
8047 (typeof(action.result) != 'undefined') &&
8048 (typeof(action.result.errors) != 'undefined') &&
8049 (typeof(action.result.errors.needs_confirm) != 'undefined')
8052 Roo.log("not supported yet");
8055 Roo.MessageBox.confirm(
8056 "Change requires confirmation",
8057 action.result.errorMsg,
8062 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8072 Roo.callback(o.failure, o.scope, [this, action]);
8073 // show an error message if no failed handler is set..
8074 if (!this.hasListener('actionfailed')) {
8075 Roo.log("need to add dialog support");
8077 Roo.MessageBox.alert("Error",
8078 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8079 action.result.errorMsg :
8080 "Saving Failed, please check your entries or try again"
8085 this.fireEvent('actionfailed', this, action);
8090 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8091 * @param {String} id The value to search for
8094 findField : function(id){
8095 var items = this.getItems();
8096 var field = items.get(id);
8098 items.each(function(f){
8099 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8106 return field || null;
8109 * Mark fields in this form invalid in bulk.
8110 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8111 * @return {BasicForm} this
8113 markInvalid : function(errors){
8114 if(errors instanceof Array){
8115 for(var i = 0, len = errors.length; i < len; i++){
8116 var fieldError = errors[i];
8117 var f = this.findField(fieldError.id);
8119 f.markInvalid(fieldError.msg);
8125 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8126 field.markInvalid(errors[id]);
8130 //Roo.each(this.childForms || [], function (f) {
8131 // f.markInvalid(errors);
8138 * Set values for fields in this form in bulk.
8139 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8140 * @return {BasicForm} this
8142 setValues : function(values){
8143 if(values instanceof Array){ // array of objects
8144 for(var i = 0, len = values.length; i < len; i++){
8146 var f = this.findField(v.id);
8148 f.setValue(v.value);
8149 if(this.trackResetOnLoad){
8150 f.originalValue = f.getValue();
8154 }else{ // object hash
8157 if(typeof values[id] != 'function' && (field = this.findField(id))){
8159 if (field.setFromData &&
8161 field.displayField &&
8162 // combos' with local stores can
8163 // be queried via setValue()
8164 // to set their value..
8165 (field.store && !field.store.isLocal)
8169 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8170 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8171 field.setFromData(sd);
8173 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8175 field.setFromData(values);
8178 field.setValue(values[id]);
8182 if(this.trackResetOnLoad){
8183 field.originalValue = field.getValue();
8189 //Roo.each(this.childForms || [], function (f) {
8190 // f.setValues(values);
8197 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8198 * they are returned as an array.
8199 * @param {Boolean} asString
8202 getValues : function(asString){
8203 //if (this.childForms) {
8204 // copy values from the child forms
8205 // Roo.each(this.childForms, function (f) {
8206 // this.setValues(f.getValues());
8212 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8213 if(asString === true){
8216 return Roo.urlDecode(fs);
8220 * Returns the fields in this form as an object with key/value pairs.
8221 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8224 getFieldValues : function(with_hidden)
8226 var items = this.getItems();
8228 items.each(function(f){
8234 var v = f.getValue();
8236 if (f.inputType =='radio') {
8237 if (typeof(ret[f.getName()]) == 'undefined') {
8238 ret[f.getName()] = ''; // empty..
8241 if (!f.el.dom.checked) {
8249 if(f.xtype == 'MoneyField'){
8250 ret[f.currencyName] = f.getCurrency();
8253 // not sure if this supported any more..
8254 if ((typeof(v) == 'object') && f.getRawValue) {
8255 v = f.getRawValue() ; // dates..
8257 // combo boxes where name != hiddenName...
8258 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8259 ret[f.name] = f.getRawValue();
8261 ret[f.getName()] = v;
8268 * Clears all invalid messages in this form.
8269 * @return {BasicForm} this
8271 clearInvalid : function(){
8272 var items = this.getItems();
8274 items.each(function(f){
8283 * @return {BasicForm} this
8286 var items = this.getItems();
8287 items.each(function(f){
8291 Roo.each(this.childForms || [], function (f) {
8299 getItems : function()
8301 var r=new Roo.util.MixedCollection(false, function(o){
8302 return o.id || (o.id = Roo.id());
8304 var iter = function(el) {
8311 Roo.each(el.items,function(e) {
8320 hideFields : function(items)
8322 Roo.each(items, function(i){
8324 var f = this.findField(i);
8335 showFields : function(items)
8337 Roo.each(items, function(i){
8339 var f = this.findField(i);
8352 Roo.apply(Roo.bootstrap.Form, {
8379 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8380 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8381 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8382 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8385 this.maskEl.top.enableDisplayMode("block");
8386 this.maskEl.left.enableDisplayMode("block");
8387 this.maskEl.bottom.enableDisplayMode("block");
8388 this.maskEl.right.enableDisplayMode("block");
8390 this.toolTip = new Roo.bootstrap.Tooltip({
8391 cls : 'roo-form-error-popover',
8393 'left' : ['r-l', [-2,0], 'right'],
8394 'right' : ['l-r', [2,0], 'left'],
8395 'bottom' : ['tl-bl', [0,2], 'top'],
8396 'top' : [ 'bl-tl', [0,-2], 'bottom']
8400 this.toolTip.render(Roo.get(document.body));
8402 this.toolTip.el.enableDisplayMode("block");
8404 Roo.get(document.body).on('click', function(){
8408 Roo.get(document.body).on('touchstart', function(){
8412 this.isApplied = true
8415 mask : function(form, target)
8419 this.target = target;
8421 if(!this.form.errorMask || !target.el){
8425 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8427 Roo.log(scrollable);
8429 var ot = this.target.el.calcOffsetsTo(scrollable);
8431 var scrollTo = ot[1] - this.form.maskOffset;
8433 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8435 scrollable.scrollTo('top', scrollTo);
8437 var box = this.target.el.getBox();
8439 var zIndex = Roo.bootstrap.Modal.zIndex++;
8442 this.maskEl.top.setStyle('position', 'absolute');
8443 this.maskEl.top.setStyle('z-index', zIndex);
8444 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8445 this.maskEl.top.setLeft(0);
8446 this.maskEl.top.setTop(0);
8447 this.maskEl.top.show();
8449 this.maskEl.left.setStyle('position', 'absolute');
8450 this.maskEl.left.setStyle('z-index', zIndex);
8451 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8452 this.maskEl.left.setLeft(0);
8453 this.maskEl.left.setTop(box.y - this.padding);
8454 this.maskEl.left.show();
8456 this.maskEl.bottom.setStyle('position', 'absolute');
8457 this.maskEl.bottom.setStyle('z-index', zIndex);
8458 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8459 this.maskEl.bottom.setLeft(0);
8460 this.maskEl.bottom.setTop(box.bottom + this.padding);
8461 this.maskEl.bottom.show();
8463 this.maskEl.right.setStyle('position', 'absolute');
8464 this.maskEl.right.setStyle('z-index', zIndex);
8465 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8466 this.maskEl.right.setLeft(box.right + this.padding);
8467 this.maskEl.right.setTop(box.y - this.padding);
8468 this.maskEl.right.show();
8470 this.toolTip.bindEl = this.target.el;
8472 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8474 var tip = this.target.blankText;
8476 if(this.target.getValue() !== '' ) {
8478 if (this.target.invalidText.length) {
8479 tip = this.target.invalidText;
8480 } else if (this.target.regexText.length){
8481 tip = this.target.regexText;
8485 this.toolTip.show(tip);
8487 this.intervalID = window.setInterval(function() {
8488 Roo.bootstrap.Form.popover.unmask();
8491 window.onwheel = function(){ return false;};
8493 (function(){ this.isMasked = true; }).defer(500, this);
8499 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8503 this.maskEl.top.setStyle('position', 'absolute');
8504 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8505 this.maskEl.top.hide();
8507 this.maskEl.left.setStyle('position', 'absolute');
8508 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8509 this.maskEl.left.hide();
8511 this.maskEl.bottom.setStyle('position', 'absolute');
8512 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8513 this.maskEl.bottom.hide();
8515 this.maskEl.right.setStyle('position', 'absolute');
8516 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8517 this.maskEl.right.hide();
8519 this.toolTip.hide();
8521 this.toolTip.el.hide();
8523 window.onwheel = function(){ return true;};
8525 if(this.intervalID){
8526 window.clearInterval(this.intervalID);
8527 this.intervalID = false;
8530 this.isMasked = false;
8540 * Ext JS Library 1.1.1
8541 * Copyright(c) 2006-2007, Ext JS, LLC.
8543 * Originally Released Under LGPL - original licence link has changed is not relivant.
8546 * <script type="text/javascript">
8549 * @class Roo.form.VTypes
8550 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8553 Roo.form.VTypes = function(){
8554 // closure these in so they are only created once.
8555 var alpha = /^[a-zA-Z_]+$/;
8556 var alphanum = /^[a-zA-Z0-9_]+$/;
8557 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8558 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8560 // All these messages and functions are configurable
8563 * The function used to validate email addresses
8564 * @param {String} value The email address
8566 'email' : function(v){
8567 return email.test(v);
8570 * The error text to display when the email validation function returns false
8573 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8575 * The keystroke filter mask to be applied on email input
8578 'emailMask' : /[a-z0-9_\.\-@]/i,
8581 * The function used to validate URLs
8582 * @param {String} value The URL
8584 'url' : function(v){
8588 * The error text to display when the url validation function returns false
8591 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8594 * The function used to validate alpha values
8595 * @param {String} value The value
8597 'alpha' : function(v){
8598 return alpha.test(v);
8601 * The error text to display when the alpha validation function returns false
8604 'alphaText' : 'This field should only contain letters and _',
8606 * The keystroke filter mask to be applied on alpha input
8609 'alphaMask' : /[a-z_]/i,
8612 * The function used to validate alphanumeric values
8613 * @param {String} value The value
8615 'alphanum' : function(v){
8616 return alphanum.test(v);
8619 * The error text to display when the alphanumeric validation function returns false
8622 'alphanumText' : 'This field should only contain letters, numbers and _',
8624 * The keystroke filter mask to be applied on alphanumeric input
8627 'alphanumMask' : /[a-z0-9_]/i
8637 * @class Roo.bootstrap.Input
8638 * @extends Roo.bootstrap.Component
8639 * Bootstrap Input class
8640 * @cfg {Boolean} disabled is it disabled
8641 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8642 * @cfg {String} name name of the input
8643 * @cfg {string} fieldLabel - the label associated
8644 * @cfg {string} placeholder - placeholder to put in text.
8645 * @cfg {string} before - input group add on before
8646 * @cfg {string} after - input group add on after
8647 * @cfg {string} size - (lg|sm) or leave empty..
8648 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8649 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8650 * @cfg {Number} md colspan out of 12 for computer-sized screens
8651 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8652 * @cfg {string} value default value of the input
8653 * @cfg {Number} labelWidth set the width of label
8654 * @cfg {Number} labellg set the width of label (1-12)
8655 * @cfg {Number} labelmd set the width of label (1-12)
8656 * @cfg {Number} labelsm set the width of label (1-12)
8657 * @cfg {Number} labelxs set the width of label (1-12)
8658 * @cfg {String} labelAlign (top|left)
8659 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8660 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8661 * @cfg {String} indicatorpos (left|right) default left
8662 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8663 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8665 * @cfg {String} align (left|center|right) Default left
8666 * @cfg {Boolean} forceFeedback (true|false) Default false
8669 * Create a new Input
8670 * @param {Object} config The config object
8673 Roo.bootstrap.Input = function(config){
8675 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8680 * Fires when this field receives input focus.
8681 * @param {Roo.form.Field} this
8686 * Fires when this field loses input focus.
8687 * @param {Roo.form.Field} this
8692 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8693 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8694 * @param {Roo.form.Field} this
8695 * @param {Roo.EventObject} e The event object
8700 * Fires just before the field blurs if the field value has changed.
8701 * @param {Roo.form.Field} this
8702 * @param {Mixed} newValue The new value
8703 * @param {Mixed} oldValue The original value
8708 * Fires after the field has been marked as invalid.
8709 * @param {Roo.form.Field} this
8710 * @param {String} msg The validation message
8715 * Fires after the field has been validated with no errors.
8716 * @param {Roo.form.Field} this
8721 * Fires after the key up
8722 * @param {Roo.form.Field} this
8723 * @param {Roo.EventObject} e The event Object
8729 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8731 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8732 automatic validation (defaults to "keyup").
8734 validationEvent : "keyup",
8736 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8738 validateOnBlur : true,
8740 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8742 validationDelay : 250,
8744 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8746 focusClass : "x-form-focus", // not needed???
8750 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8752 invalidClass : "has-warning",
8755 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8757 validClass : "has-success",
8760 * @cfg {Boolean} hasFeedback (true|false) default true
8765 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8767 invalidFeedbackClass : "glyphicon-warning-sign",
8770 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8772 validFeedbackClass : "glyphicon-ok",
8775 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8777 selectOnFocus : false,
8780 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8784 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8789 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8791 disableKeyFilter : false,
8794 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8798 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8802 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8804 blankText : "Please complete this mandatory field",
8807 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8811 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8813 maxLength : Number.MAX_VALUE,
8815 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8817 minLengthText : "The minimum length for this field is {0}",
8819 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8821 maxLengthText : "The maximum length for this field is {0}",
8825 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8826 * If available, this function will be called only after the basic validators all return true, and will be passed the
8827 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8831 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8832 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8833 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8837 * @cfg {String} regexText -- Depricated - use Invalid Text
8842 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8848 autocomplete: false,
8867 formatedValue : false,
8868 forceFeedback : false,
8870 indicatorpos : 'left',
8880 parentLabelAlign : function()
8883 while (parent.parent()) {
8884 parent = parent.parent();
8885 if (typeof(parent.labelAlign) !='undefined') {
8886 return parent.labelAlign;
8893 getAutoCreate : function()
8895 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8901 if(this.inputType != 'hidden'){
8902 cfg.cls = 'form-group' //input-group
8908 type : this.inputType,
8910 cls : 'form-control',
8911 placeholder : this.placeholder || '',
8912 autocomplete : this.autocomplete || 'new-password'
8915 if(this.capture.length){
8916 input.capture = this.capture;
8919 if(this.accept.length){
8920 input.accept = this.accept + "/*";
8924 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8927 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8928 input.maxLength = this.maxLength;
8931 if (this.disabled) {
8932 input.disabled=true;
8935 if (this.readOnly) {
8936 input.readonly=true;
8940 input.name = this.name;
8944 input.cls += ' input-' + this.size;
8948 ['xs','sm','md','lg'].map(function(size){
8949 if (settings[size]) {
8950 cfg.cls += ' col-' + size + '-' + settings[size];
8954 var inputblock = input;
8958 cls: 'glyphicon form-control-feedback'
8961 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8964 cls : 'has-feedback',
8972 if (this.before || this.after) {
8975 cls : 'input-group',
8979 if (this.before && typeof(this.before) == 'string') {
8981 inputblock.cn.push({
8983 cls : 'roo-input-before input-group-addon',
8987 if (this.before && typeof(this.before) == 'object') {
8988 this.before = Roo.factory(this.before);
8990 inputblock.cn.push({
8992 cls : 'roo-input-before input-group-' +
8993 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8997 inputblock.cn.push(input);
8999 if (this.after && typeof(this.after) == 'string') {
9000 inputblock.cn.push({
9002 cls : 'roo-input-after input-group-addon',
9006 if (this.after && typeof(this.after) == 'object') {
9007 this.after = Roo.factory(this.after);
9009 inputblock.cn.push({
9011 cls : 'roo-input-after input-group-' +
9012 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9016 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9017 inputblock.cls += ' has-feedback';
9018 inputblock.cn.push(feedback);
9022 if (align ==='left' && this.fieldLabel.length) {
9024 cfg.cls += ' roo-form-group-label-left';
9029 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9030 tooltip : 'This field is required'
9035 cls : 'control-label',
9036 html : this.fieldLabel
9047 var labelCfg = cfg.cn[1];
9048 var contentCfg = cfg.cn[2];
9050 if(this.indicatorpos == 'right'){
9055 cls : 'control-label',
9059 html : this.fieldLabel
9063 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9064 tooltip : 'This field is required'
9077 labelCfg = cfg.cn[0];
9078 contentCfg = cfg.cn[1];
9082 if(this.labelWidth > 12){
9083 labelCfg.style = "width: " + this.labelWidth + 'px';
9086 if(this.labelWidth < 13 && this.labelmd == 0){
9087 this.labelmd = this.labelWidth;
9090 if(this.labellg > 0){
9091 labelCfg.cls += ' col-lg-' + this.labellg;
9092 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9095 if(this.labelmd > 0){
9096 labelCfg.cls += ' col-md-' + this.labelmd;
9097 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9100 if(this.labelsm > 0){
9101 labelCfg.cls += ' col-sm-' + this.labelsm;
9102 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9105 if(this.labelxs > 0){
9106 labelCfg.cls += ' col-xs-' + this.labelxs;
9107 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9111 } else if ( this.fieldLabel.length) {
9116 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9117 tooltip : 'This field is required'
9121 //cls : 'input-group-addon',
9122 html : this.fieldLabel
9130 if(this.indicatorpos == 'right'){
9135 //cls : 'input-group-addon',
9136 html : this.fieldLabel
9141 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9142 tooltip : 'This field is required'
9162 if (this.parentType === 'Navbar' && this.parent().bar) {
9163 cfg.cls += ' navbar-form';
9166 if (this.parentType === 'NavGroup') {
9167 cfg.cls += ' navbar-form';
9175 * return the real input element.
9177 inputEl: function ()
9179 return this.el.select('input.form-control',true).first();
9182 tooltipEl : function()
9184 return this.inputEl();
9187 indicatorEl : function()
9189 var indicator = this.el.select('i.roo-required-indicator',true).first();
9199 setDisabled : function(v)
9201 var i = this.inputEl().dom;
9203 i.removeAttribute('disabled');
9207 i.setAttribute('disabled','true');
9209 initEvents : function()
9212 this.inputEl().on("keydown" , this.fireKey, this);
9213 this.inputEl().on("focus", this.onFocus, this);
9214 this.inputEl().on("blur", this.onBlur, this);
9216 this.inputEl().relayEvent('keyup', this);
9218 this.indicator = this.indicatorEl();
9221 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9224 // reference to original value for reset
9225 this.originalValue = this.getValue();
9226 //Roo.form.TextField.superclass.initEvents.call(this);
9227 if(this.validationEvent == 'keyup'){
9228 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9229 this.inputEl().on('keyup', this.filterValidation, this);
9231 else if(this.validationEvent !== false){
9232 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9235 if(this.selectOnFocus){
9236 this.on("focus", this.preFocus, this);
9239 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9240 this.inputEl().on("keypress", this.filterKeys, this);
9242 this.inputEl().relayEvent('keypress', this);
9245 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9246 this.el.on("click", this.autoSize, this);
9249 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9250 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9253 if (typeof(this.before) == 'object') {
9254 this.before.render(this.el.select('.roo-input-before',true).first());
9256 if (typeof(this.after) == 'object') {
9257 this.after.render(this.el.select('.roo-input-after',true).first());
9260 this.inputEl().on('change', this.onChange, this);
9263 filterValidation : function(e){
9264 if(!e.isNavKeyPress()){
9265 this.validationTask.delay(this.validationDelay);
9269 * Validates the field value
9270 * @return {Boolean} True if the value is valid, else false
9272 validate : function(){
9273 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9274 if(this.disabled || this.validateValue(this.getRawValue())){
9285 * Validates a value according to the field's validation rules and marks the field as invalid
9286 * if the validation fails
9287 * @param {Mixed} value The value to validate
9288 * @return {Boolean} True if the value is valid, else false
9290 validateValue : function(value)
9292 if(this.getVisibilityEl().hasClass('hidden')){
9296 if(value.length < 1) { // if it's blank
9297 if(this.allowBlank){
9303 if(value.length < this.minLength){
9306 if(value.length > this.maxLength){
9310 var vt = Roo.form.VTypes;
9311 if(!vt[this.vtype](value, this)){
9315 if(typeof this.validator == "function"){
9316 var msg = this.validator(value);
9320 if (typeof(msg) == 'string') {
9321 this.invalidText = msg;
9325 if(this.regex && !this.regex.test(value)){
9333 fireKey : function(e){
9334 //Roo.log('field ' + e.getKey());
9335 if(e.isNavKeyPress()){
9336 this.fireEvent("specialkey", this, e);
9339 focus : function (selectText){
9341 this.inputEl().focus();
9342 if(selectText === true){
9343 this.inputEl().dom.select();
9349 onFocus : function(){
9350 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9351 // this.el.addClass(this.focusClass);
9354 this.hasFocus = true;
9355 this.startValue = this.getValue();
9356 this.fireEvent("focus", this);
9360 beforeBlur : Roo.emptyFn,
9364 onBlur : function(){
9366 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9367 //this.el.removeClass(this.focusClass);
9369 this.hasFocus = false;
9370 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9373 var v = this.getValue();
9374 if(String(v) !== String(this.startValue)){
9375 this.fireEvent('change', this, v, this.startValue);
9377 this.fireEvent("blur", this);
9380 onChange : function(e)
9382 var v = this.getValue();
9383 if(String(v) !== String(this.startValue)){
9384 this.fireEvent('change', this, v, this.startValue);
9390 * Resets the current field value to the originally loaded value and clears any validation messages
9393 this.setValue(this.originalValue);
9397 * Returns the name of the field
9398 * @return {Mixed} name The name field
9400 getName: function(){
9404 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9405 * @return {Mixed} value The field value
9407 getValue : function(){
9409 var v = this.inputEl().getValue();
9414 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9415 * @return {Mixed} value The field value
9417 getRawValue : function(){
9418 var v = this.inputEl().getValue();
9424 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9425 * @param {Mixed} value The value to set
9427 setRawValue : function(v){
9428 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9431 selectText : function(start, end){
9432 var v = this.getRawValue();
9434 start = start === undefined ? 0 : start;
9435 end = end === undefined ? v.length : end;
9436 var d = this.inputEl().dom;
9437 if(d.setSelectionRange){
9438 d.setSelectionRange(start, end);
9439 }else if(d.createTextRange){
9440 var range = d.createTextRange();
9441 range.moveStart("character", start);
9442 range.moveEnd("character", v.length-end);
9449 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9450 * @param {Mixed} value The value to set
9452 setValue : function(v){
9455 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9461 processValue : function(value){
9462 if(this.stripCharsRe){
9463 var newValue = value.replace(this.stripCharsRe, '');
9464 if(newValue !== value){
9465 this.setRawValue(newValue);
9472 preFocus : function(){
9474 if(this.selectOnFocus){
9475 this.inputEl().dom.select();
9478 filterKeys : function(e){
9480 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9483 var c = e.getCharCode(), cc = String.fromCharCode(c);
9484 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9487 if(!this.maskRe.test(cc)){
9492 * Clear any invalid styles/messages for this field
9494 clearInvalid : function(){
9496 if(!this.el || this.preventMark){ // not rendered
9501 this.el.removeClass(this.invalidClass);
9503 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9505 var feedback = this.el.select('.form-control-feedback', true).first();
9508 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9514 this.indicator.removeClass('visible');
9515 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9518 this.fireEvent('valid', this);
9522 * Mark this field as valid
9524 markValid : function()
9526 if(!this.el || this.preventMark){ // not rendered...
9530 this.el.removeClass([this.invalidClass, this.validClass]);
9532 var feedback = this.el.select('.form-control-feedback', true).first();
9535 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9539 this.indicator.removeClass('visible');
9540 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9547 if(this.allowBlank && !this.getRawValue().length){
9551 this.el.addClass(this.validClass);
9553 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9555 var feedback = this.el.select('.form-control-feedback', true).first();
9558 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9559 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9564 this.fireEvent('valid', this);
9568 * Mark this field as invalid
9569 * @param {String} msg The validation message
9571 markInvalid : function(msg)
9573 if(!this.el || this.preventMark){ // not rendered
9577 this.el.removeClass([this.invalidClass, this.validClass]);
9579 var feedback = this.el.select('.form-control-feedback', true).first();
9582 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9589 if(this.allowBlank && !this.getRawValue().length){
9594 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9595 this.indicator.addClass('visible');
9598 this.el.addClass(this.invalidClass);
9600 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9602 var feedback = this.el.select('.form-control-feedback', true).first();
9605 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9607 if(this.getValue().length || this.forceFeedback){
9608 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9615 this.fireEvent('invalid', this, msg);
9618 SafariOnKeyDown : function(event)
9620 // this is a workaround for a password hang bug on chrome/ webkit.
9621 if (this.inputEl().dom.type != 'password') {
9625 var isSelectAll = false;
9627 if(this.inputEl().dom.selectionEnd > 0){
9628 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9630 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9631 event.preventDefault();
9636 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9638 event.preventDefault();
9639 // this is very hacky as keydown always get's upper case.
9641 var cc = String.fromCharCode(event.getCharCode());
9642 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9646 adjustWidth : function(tag, w){
9647 tag = tag.toLowerCase();
9648 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9649 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9653 if(tag == 'textarea'){
9656 }else if(Roo.isOpera){
9660 if(tag == 'textarea'){
9668 setFieldLabel : function(v)
9675 var ar = this.el.select('label > span',true);
9677 if (ar.elements.length) {
9678 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9679 this.fieldLabel = v;
9683 var br = this.el.select('label',true);
9685 if(br.elements.length) {
9686 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9687 this.fieldLabel = v;
9691 Roo.log('Cannot Found any of label > span || label in input');
9695 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9696 this.fieldLabel = v;
9711 * @class Roo.bootstrap.TextArea
9712 * @extends Roo.bootstrap.Input
9713 * Bootstrap TextArea class
9714 * @cfg {Number} cols Specifies the visible width of a text area
9715 * @cfg {Number} rows Specifies the visible number of lines in a text area
9716 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9717 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9718 * @cfg {string} html text
9721 * Create a new TextArea
9722 * @param {Object} config The config object
9725 Roo.bootstrap.TextArea = function(config){
9726 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9730 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9740 getAutoCreate : function(){
9742 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9748 if(this.inputType != 'hidden'){
9749 cfg.cls = 'form-group' //input-group
9757 value : this.value || '',
9758 html: this.html || '',
9759 cls : 'form-control',
9760 placeholder : this.placeholder || ''
9764 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9765 input.maxLength = this.maxLength;
9769 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9773 input.cols = this.cols;
9776 if (this.readOnly) {
9777 input.readonly = true;
9781 input.name = this.name;
9785 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9789 ['xs','sm','md','lg'].map(function(size){
9790 if (settings[size]) {
9791 cfg.cls += ' col-' + size + '-' + settings[size];
9795 var inputblock = input;
9797 if(this.hasFeedback && !this.allowBlank){
9801 cls: 'glyphicon form-control-feedback'
9805 cls : 'has-feedback',
9814 if (this.before || this.after) {
9817 cls : 'input-group',
9821 inputblock.cn.push({
9823 cls : 'input-group-addon',
9828 inputblock.cn.push(input);
9830 if(this.hasFeedback && !this.allowBlank){
9831 inputblock.cls += ' has-feedback';
9832 inputblock.cn.push(feedback);
9836 inputblock.cn.push({
9838 cls : 'input-group-addon',
9845 if (align ==='left' && this.fieldLabel.length) {
9850 cls : 'control-label',
9851 html : this.fieldLabel
9862 if(this.labelWidth > 12){
9863 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9866 if(this.labelWidth < 13 && this.labelmd == 0){
9867 this.labelmd = this.labelWidth;
9870 if(this.labellg > 0){
9871 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9872 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9875 if(this.labelmd > 0){
9876 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9877 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9880 if(this.labelsm > 0){
9881 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9882 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9885 if(this.labelxs > 0){
9886 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9887 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9890 } else if ( this.fieldLabel.length) {
9895 //cls : 'input-group-addon',
9896 html : this.fieldLabel
9914 if (this.disabled) {
9915 input.disabled=true;
9922 * return the real textarea element.
9924 inputEl: function ()
9926 return this.el.select('textarea.form-control',true).first();
9930 * Clear any invalid styles/messages for this field
9932 clearInvalid : function()
9935 if(!this.el || this.preventMark){ // not rendered
9939 var label = this.el.select('label', true).first();
9940 var icon = this.el.select('i.fa-star', true).first();
9946 this.el.removeClass(this.invalidClass);
9948 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9950 var feedback = this.el.select('.form-control-feedback', true).first();
9953 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9958 this.fireEvent('valid', this);
9962 * Mark this field as valid
9964 markValid : function()
9966 if(!this.el || this.preventMark){ // not rendered
9970 this.el.removeClass([this.invalidClass, this.validClass]);
9972 var feedback = this.el.select('.form-control-feedback', true).first();
9975 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9978 if(this.disabled || this.allowBlank){
9982 var label = this.el.select('label', true).first();
9983 var icon = this.el.select('i.fa-star', true).first();
9989 this.el.addClass(this.validClass);
9991 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9993 var feedback = this.el.select('.form-control-feedback', true).first();
9996 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9997 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10002 this.fireEvent('valid', this);
10006 * Mark this field as invalid
10007 * @param {String} msg The validation message
10009 markInvalid : function(msg)
10011 if(!this.el || this.preventMark){ // not rendered
10015 this.el.removeClass([this.invalidClass, this.validClass]);
10017 var feedback = this.el.select('.form-control-feedback', true).first();
10020 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10023 if(this.disabled || this.allowBlank){
10027 var label = this.el.select('label', true).first();
10028 var icon = this.el.select('i.fa-star', true).first();
10030 if(!this.getValue().length && label && !icon){
10031 this.el.createChild({
10033 cls : 'text-danger fa fa-lg fa-star',
10034 tooltip : 'This field is required',
10035 style : 'margin-right:5px;'
10039 this.el.addClass(this.invalidClass);
10041 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10043 var feedback = this.el.select('.form-control-feedback', true).first();
10046 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10048 if(this.getValue().length || this.forceFeedback){
10049 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10056 this.fireEvent('invalid', this, msg);
10064 * trigger field - base class for combo..
10069 * @class Roo.bootstrap.TriggerField
10070 * @extends Roo.bootstrap.Input
10071 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10072 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10073 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10074 * for which you can provide a custom implementation. For example:
10076 var trigger = new Roo.bootstrap.TriggerField();
10077 trigger.onTriggerClick = myTriggerFn;
10078 trigger.applyTo('my-field');
10081 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10082 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10083 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10084 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10085 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10088 * Create a new TriggerField.
10089 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10090 * to the base TextField)
10092 Roo.bootstrap.TriggerField = function(config){
10093 this.mimicing = false;
10094 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10097 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10099 * @cfg {String} triggerClass A CSS class to apply to the trigger
10102 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10107 * @cfg {Boolean} removable (true|false) special filter default false
10111 /** @cfg {Boolean} grow @hide */
10112 /** @cfg {Number} growMin @hide */
10113 /** @cfg {Number} growMax @hide */
10119 autoSize: Roo.emptyFn,
10123 deferHeight : true,
10126 actionMode : 'wrap',
10131 getAutoCreate : function(){
10133 var align = this.labelAlign || this.parentLabelAlign();
10138 cls: 'form-group' //input-group
10145 type : this.inputType,
10146 cls : 'form-control',
10147 autocomplete: 'new-password',
10148 placeholder : this.placeholder || ''
10152 input.name = this.name;
10155 input.cls += ' input-' + this.size;
10158 if (this.disabled) {
10159 input.disabled=true;
10162 var inputblock = input;
10164 if(this.hasFeedback && !this.allowBlank){
10168 cls: 'glyphicon form-control-feedback'
10171 if(this.removable && !this.editable && !this.tickable){
10173 cls : 'has-feedback',
10179 cls : 'roo-combo-removable-btn close'
10186 cls : 'has-feedback',
10195 if(this.removable && !this.editable && !this.tickable){
10197 cls : 'roo-removable',
10203 cls : 'roo-combo-removable-btn close'
10210 if (this.before || this.after) {
10213 cls : 'input-group',
10217 inputblock.cn.push({
10219 cls : 'input-group-addon',
10224 inputblock.cn.push(input);
10226 if(this.hasFeedback && !this.allowBlank){
10227 inputblock.cls += ' has-feedback';
10228 inputblock.cn.push(feedback);
10232 inputblock.cn.push({
10234 cls : 'input-group-addon',
10247 cls: 'form-hidden-field'
10261 cls: 'form-hidden-field'
10265 cls: 'roo-select2-choices',
10269 cls: 'roo-select2-search-field',
10282 cls: 'roo-select2-container input-group',
10287 // cls: 'typeahead typeahead-long dropdown-menu',
10288 // style: 'display:none'
10293 if(!this.multiple && this.showToggleBtn){
10299 if (this.caret != false) {
10302 cls: 'fa fa-' + this.caret
10309 cls : 'input-group-addon btn dropdown-toggle',
10314 cls: 'combobox-clear',
10328 combobox.cls += ' roo-select2-container-multi';
10331 if (align ==='left' && this.fieldLabel.length) {
10333 cfg.cls += ' roo-form-group-label-left';
10338 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10339 tooltip : 'This field is required'
10344 cls : 'control-label',
10345 html : this.fieldLabel
10357 var labelCfg = cfg.cn[1];
10358 var contentCfg = cfg.cn[2];
10360 if(this.indicatorpos == 'right'){
10365 cls : 'control-label',
10369 html : this.fieldLabel
10373 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10374 tooltip : 'This field is required'
10387 labelCfg = cfg.cn[0];
10388 contentCfg = cfg.cn[1];
10391 if(this.labelWidth > 12){
10392 labelCfg.style = "width: " + this.labelWidth + 'px';
10395 if(this.labelWidth < 13 && this.labelmd == 0){
10396 this.labelmd = this.labelWidth;
10399 if(this.labellg > 0){
10400 labelCfg.cls += ' col-lg-' + this.labellg;
10401 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10404 if(this.labelmd > 0){
10405 labelCfg.cls += ' col-md-' + this.labelmd;
10406 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10409 if(this.labelsm > 0){
10410 labelCfg.cls += ' col-sm-' + this.labelsm;
10411 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10414 if(this.labelxs > 0){
10415 labelCfg.cls += ' col-xs-' + this.labelxs;
10416 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10419 } else if ( this.fieldLabel.length) {
10420 // Roo.log(" label");
10424 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10425 tooltip : 'This field is required'
10429 //cls : 'input-group-addon',
10430 html : this.fieldLabel
10438 if(this.indicatorpos == 'right'){
10446 html : this.fieldLabel
10450 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10451 tooltip : 'This field is required'
10464 // Roo.log(" no label && no align");
10471 ['xs','sm','md','lg'].map(function(size){
10472 if (settings[size]) {
10473 cfg.cls += ' col-' + size + '-' + settings[size];
10484 onResize : function(w, h){
10485 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10486 // if(typeof w == 'number'){
10487 // var x = w - this.trigger.getWidth();
10488 // this.inputEl().setWidth(this.adjustWidth('input', x));
10489 // this.trigger.setStyle('left', x+'px');
10494 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10497 getResizeEl : function(){
10498 return this.inputEl();
10502 getPositionEl : function(){
10503 return this.inputEl();
10507 alignErrorIcon : function(){
10508 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10512 initEvents : function(){
10516 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10517 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10518 if(!this.multiple && this.showToggleBtn){
10519 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10520 if(this.hideTrigger){
10521 this.trigger.setDisplayed(false);
10523 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10527 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10530 if(this.removable && !this.editable && !this.tickable){
10531 var close = this.closeTriggerEl();
10534 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10535 close.on('click', this.removeBtnClick, this, close);
10539 //this.trigger.addClassOnOver('x-form-trigger-over');
10540 //this.trigger.addClassOnClick('x-form-trigger-click');
10543 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10547 closeTriggerEl : function()
10549 var close = this.el.select('.roo-combo-removable-btn', true).first();
10550 return close ? close : false;
10553 removeBtnClick : function(e, h, el)
10555 e.preventDefault();
10557 if(this.fireEvent("remove", this) !== false){
10559 this.fireEvent("afterremove", this)
10563 createList : function()
10565 this.list = Roo.get(document.body).createChild({
10567 cls: 'typeahead typeahead-long dropdown-menu',
10568 style: 'display:none'
10571 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10576 initTrigger : function(){
10581 onDestroy : function(){
10583 this.trigger.removeAllListeners();
10584 // this.trigger.remove();
10587 // this.wrap.remove();
10589 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10593 onFocus : function(){
10594 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10596 if(!this.mimicing){
10597 this.wrap.addClass('x-trigger-wrap-focus');
10598 this.mimicing = true;
10599 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10600 if(this.monitorTab){
10601 this.el.on("keydown", this.checkTab, this);
10608 checkTab : function(e){
10609 if(e.getKey() == e.TAB){
10610 this.triggerBlur();
10615 onBlur : function(){
10620 mimicBlur : function(e, t){
10622 if(!this.wrap.contains(t) && this.validateBlur()){
10623 this.triggerBlur();
10629 triggerBlur : function(){
10630 this.mimicing = false;
10631 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10632 if(this.monitorTab){
10633 this.el.un("keydown", this.checkTab, this);
10635 //this.wrap.removeClass('x-trigger-wrap-focus');
10636 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10640 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10641 validateBlur : function(e, t){
10646 onDisable : function(){
10647 this.inputEl().dom.disabled = true;
10648 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10650 // this.wrap.addClass('x-item-disabled');
10655 onEnable : function(){
10656 this.inputEl().dom.disabled = false;
10657 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10659 // this.el.removeClass('x-item-disabled');
10664 onShow : function(){
10665 var ae = this.getActionEl();
10668 ae.dom.style.display = '';
10669 ae.dom.style.visibility = 'visible';
10675 onHide : function(){
10676 var ae = this.getActionEl();
10677 ae.dom.style.display = 'none';
10681 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10682 * by an implementing function.
10684 * @param {EventObject} e
10686 onTriggerClick : Roo.emptyFn
10690 * Ext JS Library 1.1.1
10691 * Copyright(c) 2006-2007, Ext JS, LLC.
10693 * Originally Released Under LGPL - original licence link has changed is not relivant.
10696 * <script type="text/javascript">
10701 * @class Roo.data.SortTypes
10703 * Defines the default sorting (casting?) comparison functions used when sorting data.
10705 Roo.data.SortTypes = {
10707 * Default sort that does nothing
10708 * @param {Mixed} s The value being converted
10709 * @return {Mixed} The comparison value
10711 none : function(s){
10716 * The regular expression used to strip tags
10720 stripTagsRE : /<\/?[^>]+>/gi,
10723 * Strips all HTML tags to sort on text only
10724 * @param {Mixed} s The value being converted
10725 * @return {String} The comparison value
10727 asText : function(s){
10728 return String(s).replace(this.stripTagsRE, "");
10732 * Strips all HTML tags to sort on text only - Case insensitive
10733 * @param {Mixed} s The value being converted
10734 * @return {String} The comparison value
10736 asUCText : function(s){
10737 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10741 * Case insensitive string
10742 * @param {Mixed} s The value being converted
10743 * @return {String} The comparison value
10745 asUCString : function(s) {
10746 return String(s).toUpperCase();
10751 * @param {Mixed} s The value being converted
10752 * @return {Number} The comparison value
10754 asDate : function(s) {
10758 if(s instanceof Date){
10759 return s.getTime();
10761 return Date.parse(String(s));
10766 * @param {Mixed} s The value being converted
10767 * @return {Float} The comparison value
10769 asFloat : function(s) {
10770 var val = parseFloat(String(s).replace(/,/g, ""));
10779 * @param {Mixed} s The value being converted
10780 * @return {Number} The comparison value
10782 asInt : function(s) {
10783 var val = parseInt(String(s).replace(/,/g, ""));
10791 * Ext JS Library 1.1.1
10792 * Copyright(c) 2006-2007, Ext JS, LLC.
10794 * Originally Released Under LGPL - original licence link has changed is not relivant.
10797 * <script type="text/javascript">
10801 * @class Roo.data.Record
10802 * Instances of this class encapsulate both record <em>definition</em> information, and record
10803 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10804 * to access Records cached in an {@link Roo.data.Store} object.<br>
10806 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10807 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10810 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10812 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10813 * {@link #create}. The parameters are the same.
10814 * @param {Array} data An associative Array of data values keyed by the field name.
10815 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10816 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10817 * not specified an integer id is generated.
10819 Roo.data.Record = function(data, id){
10820 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10825 * Generate a constructor for a specific record layout.
10826 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10827 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10828 * Each field definition object may contain the following properties: <ul>
10829 * <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,
10830 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10831 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10832 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10833 * is being used, then this is a string containing the javascript expression to reference the data relative to
10834 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10835 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10836 * this may be omitted.</p></li>
10837 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10838 * <ul><li>auto (Default, implies no conversion)</li>
10843 * <li>date</li></ul></p></li>
10844 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10845 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10846 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10847 * by the Reader into an object that will be stored in the Record. It is passed the
10848 * following parameters:<ul>
10849 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10851 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10853 * <br>usage:<br><pre><code>
10854 var TopicRecord = Roo.data.Record.create(
10855 {name: 'title', mapping: 'topic_title'},
10856 {name: 'author', mapping: 'username'},
10857 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10858 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10859 {name: 'lastPoster', mapping: 'user2'},
10860 {name: 'excerpt', mapping: 'post_text'}
10863 var myNewRecord = new TopicRecord({
10864 title: 'Do my job please',
10867 lastPost: new Date(),
10868 lastPoster: 'Animal',
10869 excerpt: 'No way dude!'
10871 myStore.add(myNewRecord);
10876 Roo.data.Record.create = function(o){
10877 var f = function(){
10878 f.superclass.constructor.apply(this, arguments);
10880 Roo.extend(f, Roo.data.Record);
10881 var p = f.prototype;
10882 p.fields = new Roo.util.MixedCollection(false, function(field){
10885 for(var i = 0, len = o.length; i < len; i++){
10886 p.fields.add(new Roo.data.Field(o[i]));
10888 f.getField = function(name){
10889 return p.fields.get(name);
10894 Roo.data.Record.AUTO_ID = 1000;
10895 Roo.data.Record.EDIT = 'edit';
10896 Roo.data.Record.REJECT = 'reject';
10897 Roo.data.Record.COMMIT = 'commit';
10899 Roo.data.Record.prototype = {
10901 * Readonly flag - true if this record has been modified.
10910 join : function(store){
10911 this.store = store;
10915 * Set the named field to the specified value.
10916 * @param {String} name The name of the field to set.
10917 * @param {Object} value The value to set the field to.
10919 set : function(name, value){
10920 if(this.data[name] == value){
10924 if(!this.modified){
10925 this.modified = {};
10927 if(typeof this.modified[name] == 'undefined'){
10928 this.modified[name] = this.data[name];
10930 this.data[name] = value;
10931 if(!this.editing && this.store){
10932 this.store.afterEdit(this);
10937 * Get the value of the named field.
10938 * @param {String} name The name of the field to get the value of.
10939 * @return {Object} The value of the field.
10941 get : function(name){
10942 return this.data[name];
10946 beginEdit : function(){
10947 this.editing = true;
10948 this.modified = {};
10952 cancelEdit : function(){
10953 this.editing = false;
10954 delete this.modified;
10958 endEdit : function(){
10959 this.editing = false;
10960 if(this.dirty && this.store){
10961 this.store.afterEdit(this);
10966 * Usually called by the {@link Roo.data.Store} which owns the Record.
10967 * Rejects all changes made to the Record since either creation, or the last commit operation.
10968 * Modified fields are reverted to their original values.
10970 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10971 * of reject operations.
10973 reject : function(){
10974 var m = this.modified;
10976 if(typeof m[n] != "function"){
10977 this.data[n] = m[n];
10980 this.dirty = false;
10981 delete this.modified;
10982 this.editing = false;
10984 this.store.afterReject(this);
10989 * Usually called by the {@link Roo.data.Store} which owns the Record.
10990 * Commits all changes made to the Record since either creation, or the last commit operation.
10992 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10993 * of commit operations.
10995 commit : function(){
10996 this.dirty = false;
10997 delete this.modified;
10998 this.editing = false;
11000 this.store.afterCommit(this);
11005 hasError : function(){
11006 return this.error != null;
11010 clearError : function(){
11015 * Creates a copy of this record.
11016 * @param {String} id (optional) A new record id if you don't want to use this record's id
11019 copy : function(newId) {
11020 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11024 * Ext JS Library 1.1.1
11025 * Copyright(c) 2006-2007, Ext JS, LLC.
11027 * Originally Released Under LGPL - original licence link has changed is not relivant.
11030 * <script type="text/javascript">
11036 * @class Roo.data.Store
11037 * @extends Roo.util.Observable
11038 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11039 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11041 * 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
11042 * has no knowledge of the format of the data returned by the Proxy.<br>
11044 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11045 * instances from the data object. These records are cached and made available through accessor functions.
11047 * Creates a new Store.
11048 * @param {Object} config A config object containing the objects needed for the Store to access data,
11049 * and read the data into Records.
11051 Roo.data.Store = function(config){
11052 this.data = new Roo.util.MixedCollection(false);
11053 this.data.getKey = function(o){
11056 this.baseParams = {};
11058 this.paramNames = {
11063 "multisort" : "_multisort"
11066 if(config && config.data){
11067 this.inlineData = config.data;
11068 delete config.data;
11071 Roo.apply(this, config);
11073 if(this.reader){ // reader passed
11074 this.reader = Roo.factory(this.reader, Roo.data);
11075 this.reader.xmodule = this.xmodule || false;
11076 if(!this.recordType){
11077 this.recordType = this.reader.recordType;
11079 if(this.reader.onMetaChange){
11080 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11084 if(this.recordType){
11085 this.fields = this.recordType.prototype.fields;
11087 this.modified = [];
11091 * @event datachanged
11092 * Fires when the data cache has changed, and a widget which is using this Store
11093 * as a Record cache should refresh its view.
11094 * @param {Store} this
11096 datachanged : true,
11098 * @event metachange
11099 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11100 * @param {Store} this
11101 * @param {Object} meta The JSON metadata
11106 * Fires when Records have been added to the Store
11107 * @param {Store} this
11108 * @param {Roo.data.Record[]} records The array of Records added
11109 * @param {Number} index The index at which the record(s) were added
11114 * Fires when a Record has been removed from the Store
11115 * @param {Store} this
11116 * @param {Roo.data.Record} record The Record that was removed
11117 * @param {Number} index The index at which the record was removed
11122 * Fires when a Record has been updated
11123 * @param {Store} this
11124 * @param {Roo.data.Record} record The Record that was updated
11125 * @param {String} operation The update operation being performed. Value may be one of:
11127 Roo.data.Record.EDIT
11128 Roo.data.Record.REJECT
11129 Roo.data.Record.COMMIT
11135 * Fires when the data cache has been cleared.
11136 * @param {Store} this
11140 * @event beforeload
11141 * Fires before a request is made for a new data object. If the beforeload handler returns false
11142 * the load action will be canceled.
11143 * @param {Store} this
11144 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11148 * @event beforeloadadd
11149 * Fires after a new set of Records has been loaded.
11150 * @param {Store} this
11151 * @param {Roo.data.Record[]} records The Records that were loaded
11152 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11154 beforeloadadd : true,
11157 * Fires after a new set of Records has been loaded, before they are added to the store.
11158 * @param {Store} this
11159 * @param {Roo.data.Record[]} records The Records that were loaded
11160 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11161 * @params {Object} return from reader
11165 * @event loadexception
11166 * Fires if an exception occurs in the Proxy during loading.
11167 * Called with the signature of the Proxy's "loadexception" event.
11168 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11171 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11172 * @param {Object} load options
11173 * @param {Object} jsonData from your request (normally this contains the Exception)
11175 loadexception : true
11179 this.proxy = Roo.factory(this.proxy, Roo.data);
11180 this.proxy.xmodule = this.xmodule || false;
11181 this.relayEvents(this.proxy, ["loadexception"]);
11183 this.sortToggle = {};
11184 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11186 Roo.data.Store.superclass.constructor.call(this);
11188 if(this.inlineData){
11189 this.loadData(this.inlineData);
11190 delete this.inlineData;
11194 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11196 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11197 * without a remote query - used by combo/forms at present.
11201 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11204 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11207 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11208 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11211 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11212 * on any HTTP request
11215 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11218 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11222 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11223 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11225 remoteSort : false,
11228 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11229 * loaded or when a record is removed. (defaults to false).
11231 pruneModifiedRecords : false,
11234 lastOptions : null,
11237 * Add Records to the Store and fires the add event.
11238 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11240 add : function(records){
11241 records = [].concat(records);
11242 for(var i = 0, len = records.length; i < len; i++){
11243 records[i].join(this);
11245 var index = this.data.length;
11246 this.data.addAll(records);
11247 this.fireEvent("add", this, records, index);
11251 * Remove a Record from the Store and fires the remove event.
11252 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11254 remove : function(record){
11255 var index = this.data.indexOf(record);
11256 this.data.removeAt(index);
11258 if(this.pruneModifiedRecords){
11259 this.modified.remove(record);
11261 this.fireEvent("remove", this, record, index);
11265 * Remove all Records from the Store and fires the clear event.
11267 removeAll : function(){
11269 if(this.pruneModifiedRecords){
11270 this.modified = [];
11272 this.fireEvent("clear", this);
11276 * Inserts Records to the Store at the given index and fires the add event.
11277 * @param {Number} index The start index at which to insert the passed Records.
11278 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11280 insert : function(index, records){
11281 records = [].concat(records);
11282 for(var i = 0, len = records.length; i < len; i++){
11283 this.data.insert(index, records[i]);
11284 records[i].join(this);
11286 this.fireEvent("add", this, records, index);
11290 * Get the index within the cache of the passed Record.
11291 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11292 * @return {Number} The index of the passed Record. Returns -1 if not found.
11294 indexOf : function(record){
11295 return this.data.indexOf(record);
11299 * Get the index within the cache of the Record with the passed id.
11300 * @param {String} id The id of the Record to find.
11301 * @return {Number} The index of the Record. Returns -1 if not found.
11303 indexOfId : function(id){
11304 return this.data.indexOfKey(id);
11308 * Get the Record with the specified id.
11309 * @param {String} id The id of the Record to find.
11310 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11312 getById : function(id){
11313 return this.data.key(id);
11317 * Get the Record at the specified index.
11318 * @param {Number} index The index of the Record to find.
11319 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11321 getAt : function(index){
11322 return this.data.itemAt(index);
11326 * Returns a range of Records between specified indices.
11327 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11328 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11329 * @return {Roo.data.Record[]} An array of Records
11331 getRange : function(start, end){
11332 return this.data.getRange(start, end);
11336 storeOptions : function(o){
11337 o = Roo.apply({}, o);
11340 this.lastOptions = o;
11344 * Loads the Record cache from the configured Proxy using the configured Reader.
11346 * If using remote paging, then the first load call must specify the <em>start</em>
11347 * and <em>limit</em> properties in the options.params property to establish the initial
11348 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11350 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11351 * and this call will return before the new data has been loaded. Perform any post-processing
11352 * in a callback function, or in a "load" event handler.</strong>
11354 * @param {Object} options An object containing properties which control loading options:<ul>
11355 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11356 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11357 * passed the following arguments:<ul>
11358 * <li>r : Roo.data.Record[]</li>
11359 * <li>options: Options object from the load call</li>
11360 * <li>success: Boolean success indicator</li></ul></li>
11361 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11362 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11365 load : function(options){
11366 options = options || {};
11367 if(this.fireEvent("beforeload", this, options) !== false){
11368 this.storeOptions(options);
11369 var p = Roo.apply(options.params || {}, this.baseParams);
11370 // if meta was not loaded from remote source.. try requesting it.
11371 if (!this.reader.metaFromRemote) {
11372 p._requestMeta = 1;
11374 if(this.sortInfo && this.remoteSort){
11375 var pn = this.paramNames;
11376 p[pn["sort"]] = this.sortInfo.field;
11377 p[pn["dir"]] = this.sortInfo.direction;
11379 if (this.multiSort) {
11380 var pn = this.paramNames;
11381 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11384 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11389 * Reloads the Record cache from the configured Proxy using the configured Reader and
11390 * the options from the last load operation performed.
11391 * @param {Object} options (optional) An object containing properties which may override the options
11392 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11393 * the most recently used options are reused).
11395 reload : function(options){
11396 this.load(Roo.applyIf(options||{}, this.lastOptions));
11400 // Called as a callback by the Reader during a load operation.
11401 loadRecords : function(o, options, success){
11402 if(!o || success === false){
11403 if(success !== false){
11404 this.fireEvent("load", this, [], options, o);
11406 if(options.callback){
11407 options.callback.call(options.scope || this, [], options, false);
11411 // if data returned failure - throw an exception.
11412 if (o.success === false) {
11413 // show a message if no listener is registered.
11414 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11415 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11417 // loadmask wil be hooked into this..
11418 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11421 var r = o.records, t = o.totalRecords || r.length;
11423 this.fireEvent("beforeloadadd", this, r, options, o);
11425 if(!options || options.add !== true){
11426 if(this.pruneModifiedRecords){
11427 this.modified = [];
11429 for(var i = 0, len = r.length; i < len; i++){
11433 this.data = this.snapshot;
11434 delete this.snapshot;
11437 this.data.addAll(r);
11438 this.totalLength = t;
11440 this.fireEvent("datachanged", this);
11442 this.totalLength = Math.max(t, this.data.length+r.length);
11446 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11448 var e = new Roo.data.Record({});
11450 e.set(this.parent.displayField, this.parent.emptyTitle);
11451 e.set(this.parent.valueField, '');
11456 this.fireEvent("load", this, r, options, o);
11457 if(options.callback){
11458 options.callback.call(options.scope || this, r, options, true);
11464 * Loads data from a passed data block. A Reader which understands the format of the data
11465 * must have been configured in the constructor.
11466 * @param {Object} data The data block from which to read the Records. The format of the data expected
11467 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11468 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11470 loadData : function(o, append){
11471 var r = this.reader.readRecords(o);
11472 this.loadRecords(r, {add: append}, true);
11476 * Gets the number of cached records.
11478 * <em>If using paging, this may not be the total size of the dataset. If the data object
11479 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11480 * the data set size</em>
11482 getCount : function(){
11483 return this.data.length || 0;
11487 * Gets the total number of records in the dataset as returned by the server.
11489 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11490 * the dataset size</em>
11492 getTotalCount : function(){
11493 return this.totalLength || 0;
11497 * Returns the sort state of the Store as an object with two properties:
11499 field {String} The name of the field by which the Records are sorted
11500 direction {String} The sort order, "ASC" or "DESC"
11503 getSortState : function(){
11504 return this.sortInfo;
11508 applySort : function(){
11509 if(this.sortInfo && !this.remoteSort){
11510 var s = this.sortInfo, f = s.field;
11511 var st = this.fields.get(f).sortType;
11512 var fn = function(r1, r2){
11513 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11514 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11516 this.data.sort(s.direction, fn);
11517 if(this.snapshot && this.snapshot != this.data){
11518 this.snapshot.sort(s.direction, fn);
11524 * Sets the default sort column and order to be used by the next load operation.
11525 * @param {String} fieldName The name of the field to sort by.
11526 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11528 setDefaultSort : function(field, dir){
11529 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11533 * Sort the Records.
11534 * If remote sorting is used, the sort is performed on the server, and the cache is
11535 * reloaded. If local sorting is used, the cache is sorted internally.
11536 * @param {String} fieldName The name of the field to sort by.
11537 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11539 sort : function(fieldName, dir){
11540 var f = this.fields.get(fieldName);
11542 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11544 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11545 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11550 this.sortToggle[f.name] = dir;
11551 this.sortInfo = {field: f.name, direction: dir};
11552 if(!this.remoteSort){
11554 this.fireEvent("datachanged", this);
11556 this.load(this.lastOptions);
11561 * Calls the specified function for each of the Records in the cache.
11562 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11563 * Returning <em>false</em> aborts and exits the iteration.
11564 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11566 each : function(fn, scope){
11567 this.data.each(fn, scope);
11571 * Gets all records modified since the last commit. Modified records are persisted across load operations
11572 * (e.g., during paging).
11573 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11575 getModifiedRecords : function(){
11576 return this.modified;
11580 createFilterFn : function(property, value, anyMatch){
11581 if(!value.exec){ // not a regex
11582 value = String(value);
11583 if(value.length == 0){
11586 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11588 return function(r){
11589 return value.test(r.data[property]);
11594 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11595 * @param {String} property A field on your records
11596 * @param {Number} start The record index to start at (defaults to 0)
11597 * @param {Number} end The last record index to include (defaults to length - 1)
11598 * @return {Number} The sum
11600 sum : function(property, start, end){
11601 var rs = this.data.items, v = 0;
11602 start = start || 0;
11603 end = (end || end === 0) ? end : rs.length-1;
11605 for(var i = start; i <= end; i++){
11606 v += (rs[i].data[property] || 0);
11612 * Filter the records by a specified property.
11613 * @param {String} field A field on your records
11614 * @param {String/RegExp} value Either a string that the field
11615 * should start with or a RegExp to test against the field
11616 * @param {Boolean} anyMatch True to match any part not just the beginning
11618 filter : function(property, value, anyMatch){
11619 var fn = this.createFilterFn(property, value, anyMatch);
11620 return fn ? this.filterBy(fn) : this.clearFilter();
11624 * Filter by a function. The specified function will be called with each
11625 * record in this data source. If the function returns true the record is included,
11626 * otherwise it is filtered.
11627 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11628 * @param {Object} scope (optional) The scope of the function (defaults to this)
11630 filterBy : function(fn, scope){
11631 this.snapshot = this.snapshot || this.data;
11632 this.data = this.queryBy(fn, scope||this);
11633 this.fireEvent("datachanged", this);
11637 * Query the records by a specified property.
11638 * @param {String} field A field on your records
11639 * @param {String/RegExp} value Either a string that the field
11640 * should start with or a RegExp to test against the field
11641 * @param {Boolean} anyMatch True to match any part not just the beginning
11642 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11644 query : function(property, value, anyMatch){
11645 var fn = this.createFilterFn(property, value, anyMatch);
11646 return fn ? this.queryBy(fn) : this.data.clone();
11650 * Query by a function. The specified function will be called with each
11651 * record in this data source. If the function returns true the record is included
11653 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11654 * @param {Object} scope (optional) The scope of the function (defaults to this)
11655 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11657 queryBy : function(fn, scope){
11658 var data = this.snapshot || this.data;
11659 return data.filterBy(fn, scope||this);
11663 * Collects unique values for a particular dataIndex from this store.
11664 * @param {String} dataIndex The property to collect
11665 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11666 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11667 * @return {Array} An array of the unique values
11669 collect : function(dataIndex, allowNull, bypassFilter){
11670 var d = (bypassFilter === true && this.snapshot) ?
11671 this.snapshot.items : this.data.items;
11672 var v, sv, r = [], l = {};
11673 for(var i = 0, len = d.length; i < len; i++){
11674 v = d[i].data[dataIndex];
11676 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11685 * Revert to a view of the Record cache with no filtering applied.
11686 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11688 clearFilter : function(suppressEvent){
11689 if(this.snapshot && this.snapshot != this.data){
11690 this.data = this.snapshot;
11691 delete this.snapshot;
11692 if(suppressEvent !== true){
11693 this.fireEvent("datachanged", this);
11699 afterEdit : function(record){
11700 if(this.modified.indexOf(record) == -1){
11701 this.modified.push(record);
11703 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11707 afterReject : function(record){
11708 this.modified.remove(record);
11709 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11713 afterCommit : function(record){
11714 this.modified.remove(record);
11715 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11719 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11720 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11722 commitChanges : function(){
11723 var m = this.modified.slice(0);
11724 this.modified = [];
11725 for(var i = 0, len = m.length; i < len; i++){
11731 * Cancel outstanding changes on all changed records.
11733 rejectChanges : function(){
11734 var m = this.modified.slice(0);
11735 this.modified = [];
11736 for(var i = 0, len = m.length; i < len; i++){
11741 onMetaChange : function(meta, rtype, o){
11742 this.recordType = rtype;
11743 this.fields = rtype.prototype.fields;
11744 delete this.snapshot;
11745 this.sortInfo = meta.sortInfo || this.sortInfo;
11746 this.modified = [];
11747 this.fireEvent('metachange', this, this.reader.meta);
11750 moveIndex : function(data, type)
11752 var index = this.indexOf(data);
11754 var newIndex = index + type;
11758 this.insert(newIndex, data);
11763 * Ext JS Library 1.1.1
11764 * Copyright(c) 2006-2007, Ext JS, LLC.
11766 * Originally Released Under LGPL - original licence link has changed is not relivant.
11769 * <script type="text/javascript">
11773 * @class Roo.data.SimpleStore
11774 * @extends Roo.data.Store
11775 * Small helper class to make creating Stores from Array data easier.
11776 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11777 * @cfg {Array} fields An array of field definition objects, or field name strings.
11778 * @cfg {Array} data The multi-dimensional array of data
11780 * @param {Object} config
11782 Roo.data.SimpleStore = function(config){
11783 Roo.data.SimpleStore.superclass.constructor.call(this, {
11785 reader: new Roo.data.ArrayReader({
11788 Roo.data.Record.create(config.fields)
11790 proxy : new Roo.data.MemoryProxy(config.data)
11794 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11796 * Ext JS Library 1.1.1
11797 * Copyright(c) 2006-2007, Ext JS, LLC.
11799 * Originally Released Under LGPL - original licence link has changed is not relivant.
11802 * <script type="text/javascript">
11807 * @extends Roo.data.Store
11808 * @class Roo.data.JsonStore
11809 * Small helper class to make creating Stores for JSON data easier. <br/>
11811 var store = new Roo.data.JsonStore({
11812 url: 'get-images.php',
11814 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11817 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11818 * JsonReader and HttpProxy (unless inline data is provided).</b>
11819 * @cfg {Array} fields An array of field definition objects, or field name strings.
11821 * @param {Object} config
11823 Roo.data.JsonStore = function(c){
11824 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11825 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11826 reader: new Roo.data.JsonReader(c, c.fields)
11829 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11831 * Ext JS Library 1.1.1
11832 * Copyright(c) 2006-2007, Ext JS, LLC.
11834 * Originally Released Under LGPL - original licence link has changed is not relivant.
11837 * <script type="text/javascript">
11841 Roo.data.Field = function(config){
11842 if(typeof config == "string"){
11843 config = {name: config};
11845 Roo.apply(this, config);
11848 this.type = "auto";
11851 var st = Roo.data.SortTypes;
11852 // named sortTypes are supported, here we look them up
11853 if(typeof this.sortType == "string"){
11854 this.sortType = st[this.sortType];
11857 // set default sortType for strings and dates
11858 if(!this.sortType){
11861 this.sortType = st.asUCString;
11864 this.sortType = st.asDate;
11867 this.sortType = st.none;
11872 var stripRe = /[\$,%]/g;
11874 // prebuilt conversion function for this field, instead of
11875 // switching every time we're reading a value
11877 var cv, dateFormat = this.dateFormat;
11882 cv = function(v){ return v; };
11885 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11889 return v !== undefined && v !== null && v !== '' ?
11890 parseInt(String(v).replace(stripRe, ""), 10) : '';
11895 return v !== undefined && v !== null && v !== '' ?
11896 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11901 cv = function(v){ return v === true || v === "true" || v == 1; };
11908 if(v instanceof Date){
11912 if(dateFormat == "timestamp"){
11913 return new Date(v*1000);
11915 return Date.parseDate(v, dateFormat);
11917 var parsed = Date.parse(v);
11918 return parsed ? new Date(parsed) : null;
11927 Roo.data.Field.prototype = {
11935 * Ext JS Library 1.1.1
11936 * Copyright(c) 2006-2007, Ext JS, LLC.
11938 * Originally Released Under LGPL - original licence link has changed is not relivant.
11941 * <script type="text/javascript">
11944 // Base class for reading structured data from a data source. This class is intended to be
11945 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11948 * @class Roo.data.DataReader
11949 * Base class for reading structured data from a data source. This class is intended to be
11950 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11953 Roo.data.DataReader = function(meta, recordType){
11957 this.recordType = recordType instanceof Array ?
11958 Roo.data.Record.create(recordType) : recordType;
11961 Roo.data.DataReader.prototype = {
11963 * Create an empty record
11964 * @param {Object} data (optional) - overlay some values
11965 * @return {Roo.data.Record} record created.
11967 newRow : function(d) {
11969 this.recordType.prototype.fields.each(function(c) {
11971 case 'int' : da[c.name] = 0; break;
11972 case 'date' : da[c.name] = new Date(); break;
11973 case 'float' : da[c.name] = 0.0; break;
11974 case 'boolean' : da[c.name] = false; break;
11975 default : da[c.name] = ""; break;
11979 return new this.recordType(Roo.apply(da, d));
11984 * Ext JS Library 1.1.1
11985 * Copyright(c) 2006-2007, Ext JS, LLC.
11987 * Originally Released Under LGPL - original licence link has changed is not relivant.
11990 * <script type="text/javascript">
11994 * @class Roo.data.DataProxy
11995 * @extends Roo.data.Observable
11996 * This class is an abstract base class for implementations which provide retrieval of
11997 * unformatted data objects.<br>
11999 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12000 * (of the appropriate type which knows how to parse the data object) to provide a block of
12001 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12003 * Custom implementations must implement the load method as described in
12004 * {@link Roo.data.HttpProxy#load}.
12006 Roo.data.DataProxy = function(){
12009 * @event beforeload
12010 * Fires before a network request is made to retrieve a data object.
12011 * @param {Object} This DataProxy object.
12012 * @param {Object} params The params parameter to the load function.
12017 * Fires before the load method's callback is called.
12018 * @param {Object} This DataProxy object.
12019 * @param {Object} o The data object.
12020 * @param {Object} arg The callback argument object passed to the load function.
12024 * @event loadexception
12025 * Fires if an Exception occurs during data retrieval.
12026 * @param {Object} This DataProxy object.
12027 * @param {Object} o The data object.
12028 * @param {Object} arg The callback argument object passed to the load function.
12029 * @param {Object} e The Exception.
12031 loadexception : true
12033 Roo.data.DataProxy.superclass.constructor.call(this);
12036 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12039 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12043 * Ext JS Library 1.1.1
12044 * Copyright(c) 2006-2007, Ext JS, LLC.
12046 * Originally Released Under LGPL - original licence link has changed is not relivant.
12049 * <script type="text/javascript">
12052 * @class Roo.data.MemoryProxy
12053 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12054 * to the Reader when its load method is called.
12056 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12058 Roo.data.MemoryProxy = function(data){
12062 Roo.data.MemoryProxy.superclass.constructor.call(this);
12066 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12069 * Load data from the requested source (in this case an in-memory
12070 * data object passed to the constructor), read the data object into
12071 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12072 * process that block using the passed callback.
12073 * @param {Object} params This parameter is not used by the MemoryProxy class.
12074 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12075 * object into a block of Roo.data.Records.
12076 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12077 * The function must be passed <ul>
12078 * <li>The Record block object</li>
12079 * <li>The "arg" argument from the load function</li>
12080 * <li>A boolean success indicator</li>
12082 * @param {Object} scope The scope in which to call the callback
12083 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12085 load : function(params, reader, callback, scope, arg){
12086 params = params || {};
12089 result = reader.readRecords(this.data);
12091 this.fireEvent("loadexception", this, arg, null, e);
12092 callback.call(scope, null, arg, false);
12095 callback.call(scope, result, arg, true);
12099 update : function(params, records){
12104 * Ext JS Library 1.1.1
12105 * Copyright(c) 2006-2007, Ext JS, LLC.
12107 * Originally Released Under LGPL - original licence link has changed is not relivant.
12110 * <script type="text/javascript">
12113 * @class Roo.data.HttpProxy
12114 * @extends Roo.data.DataProxy
12115 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12116 * configured to reference a certain URL.<br><br>
12118 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12119 * from which the running page was served.<br><br>
12121 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12123 * Be aware that to enable the browser to parse an XML document, the server must set
12124 * the Content-Type header in the HTTP response to "text/xml".
12126 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12127 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12128 * will be used to make the request.
12130 Roo.data.HttpProxy = function(conn){
12131 Roo.data.HttpProxy.superclass.constructor.call(this);
12132 // is conn a conn config or a real conn?
12134 this.useAjax = !conn || !conn.events;
12138 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12139 // thse are take from connection...
12142 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12145 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12146 * extra parameters to each request made by this object. (defaults to undefined)
12149 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12150 * to each request made by this object. (defaults to undefined)
12153 * @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)
12156 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12159 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12165 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12169 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12170 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12171 * a finer-grained basis than the DataProxy events.
12173 getConnection : function(){
12174 return this.useAjax ? Roo.Ajax : this.conn;
12178 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12179 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12180 * process that block using the passed callback.
12181 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12182 * for the request to the remote server.
12183 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12184 * object into a block of Roo.data.Records.
12185 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12186 * The function must be passed <ul>
12187 * <li>The Record block object</li>
12188 * <li>The "arg" argument from the load function</li>
12189 * <li>A boolean success indicator</li>
12191 * @param {Object} scope The scope in which to call the callback
12192 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12194 load : function(params, reader, callback, scope, arg){
12195 if(this.fireEvent("beforeload", this, params) !== false){
12197 params : params || {},
12199 callback : callback,
12204 callback : this.loadResponse,
12208 Roo.applyIf(o, this.conn);
12209 if(this.activeRequest){
12210 Roo.Ajax.abort(this.activeRequest);
12212 this.activeRequest = Roo.Ajax.request(o);
12214 this.conn.request(o);
12217 callback.call(scope||this, null, arg, false);
12222 loadResponse : function(o, success, response){
12223 delete this.activeRequest;
12225 this.fireEvent("loadexception", this, o, response);
12226 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12231 result = o.reader.read(response);
12233 this.fireEvent("loadexception", this, o, response, e);
12234 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12238 this.fireEvent("load", this, o, o.request.arg);
12239 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12243 update : function(dataSet){
12248 updateResponse : function(dataSet){
12253 * Ext JS Library 1.1.1
12254 * Copyright(c) 2006-2007, Ext JS, LLC.
12256 * Originally Released Under LGPL - original licence link has changed is not relivant.
12259 * <script type="text/javascript">
12263 * @class Roo.data.ScriptTagProxy
12264 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12265 * other than the originating domain of the running page.<br><br>
12267 * <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
12268 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12270 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12271 * source code that is used as the source inside a <script> tag.<br><br>
12273 * In order for the browser to process the returned data, the server must wrap the data object
12274 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12275 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12276 * depending on whether the callback name was passed:
12279 boolean scriptTag = false;
12280 String cb = request.getParameter("callback");
12283 response.setContentType("text/javascript");
12285 response.setContentType("application/x-json");
12287 Writer out = response.getWriter();
12289 out.write(cb + "(");
12291 out.print(dataBlock.toJsonString());
12298 * @param {Object} config A configuration object.
12300 Roo.data.ScriptTagProxy = function(config){
12301 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12302 Roo.apply(this, config);
12303 this.head = document.getElementsByTagName("head")[0];
12306 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12308 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12310 * @cfg {String} url The URL from which to request the data object.
12313 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12317 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12318 * the server the name of the callback function set up by the load call to process the returned data object.
12319 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12320 * javascript output which calls this named function passing the data object as its only parameter.
12322 callbackParam : "callback",
12324 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12325 * name to the request.
12330 * Load data from the configured URL, read the data object into
12331 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12332 * process that block using the passed callback.
12333 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12334 * for the request to the remote server.
12335 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12336 * object into a block of Roo.data.Records.
12337 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12338 * The function must be passed <ul>
12339 * <li>The Record block object</li>
12340 * <li>The "arg" argument from the load function</li>
12341 * <li>A boolean success indicator</li>
12343 * @param {Object} scope The scope in which to call the callback
12344 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12346 load : function(params, reader, callback, scope, arg){
12347 if(this.fireEvent("beforeload", this, params) !== false){
12349 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12351 var url = this.url;
12352 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12354 url += "&_dc=" + (new Date().getTime());
12356 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12359 cb : "stcCallback"+transId,
12360 scriptId : "stcScript"+transId,
12364 callback : callback,
12370 window[trans.cb] = function(o){
12371 conn.handleResponse(o, trans);
12374 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12376 if(this.autoAbort !== false){
12380 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12382 var script = document.createElement("script");
12383 script.setAttribute("src", url);
12384 script.setAttribute("type", "text/javascript");
12385 script.setAttribute("id", trans.scriptId);
12386 this.head.appendChild(script);
12388 this.trans = trans;
12390 callback.call(scope||this, null, arg, false);
12395 isLoading : function(){
12396 return this.trans ? true : false;
12400 * Abort the current server request.
12402 abort : function(){
12403 if(this.isLoading()){
12404 this.destroyTrans(this.trans);
12409 destroyTrans : function(trans, isLoaded){
12410 this.head.removeChild(document.getElementById(trans.scriptId));
12411 clearTimeout(trans.timeoutId);
12413 window[trans.cb] = undefined;
12415 delete window[trans.cb];
12418 // if hasn't been loaded, wait for load to remove it to prevent script error
12419 window[trans.cb] = function(){
12420 window[trans.cb] = undefined;
12422 delete window[trans.cb];
12429 handleResponse : function(o, trans){
12430 this.trans = false;
12431 this.destroyTrans(trans, true);
12434 result = trans.reader.readRecords(o);
12436 this.fireEvent("loadexception", this, o, trans.arg, e);
12437 trans.callback.call(trans.scope||window, null, trans.arg, false);
12440 this.fireEvent("load", this, o, trans.arg);
12441 trans.callback.call(trans.scope||window, result, trans.arg, true);
12445 handleFailure : function(trans){
12446 this.trans = false;
12447 this.destroyTrans(trans, false);
12448 this.fireEvent("loadexception", this, null, trans.arg);
12449 trans.callback.call(trans.scope||window, null, trans.arg, false);
12453 * Ext JS Library 1.1.1
12454 * Copyright(c) 2006-2007, Ext JS, LLC.
12456 * Originally Released Under LGPL - original licence link has changed is not relivant.
12459 * <script type="text/javascript">
12463 * @class Roo.data.JsonReader
12464 * @extends Roo.data.DataReader
12465 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12466 * based on mappings in a provided Roo.data.Record constructor.
12468 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12469 * in the reply previously.
12474 var RecordDef = Roo.data.Record.create([
12475 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12476 {name: 'occupation'} // This field will use "occupation" as the mapping.
12478 var myReader = new Roo.data.JsonReader({
12479 totalProperty: "results", // The property which contains the total dataset size (optional)
12480 root: "rows", // The property which contains an Array of row objects
12481 id: "id" // The property within each row object that provides an ID for the record (optional)
12485 * This would consume a JSON file like this:
12487 { 'results': 2, 'rows': [
12488 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12489 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12492 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12493 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12494 * paged from the remote server.
12495 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12496 * @cfg {String} root name of the property which contains the Array of row objects.
12497 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12498 * @cfg {Array} fields Array of field definition objects
12500 * Create a new JsonReader
12501 * @param {Object} meta Metadata configuration options
12502 * @param {Object} recordType Either an Array of field definition objects,
12503 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12505 Roo.data.JsonReader = function(meta, recordType){
12508 // set some defaults:
12509 Roo.applyIf(meta, {
12510 totalProperty: 'total',
12511 successProperty : 'success',
12516 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12518 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12521 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12522 * Used by Store query builder to append _requestMeta to params.
12525 metaFromRemote : false,
12527 * This method is only used by a DataProxy which has retrieved data from a remote server.
12528 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12529 * @return {Object} data A data block which is used by an Roo.data.Store object as
12530 * a cache of Roo.data.Records.
12532 read : function(response){
12533 var json = response.responseText;
12535 var o = /* eval:var:o */ eval("("+json+")");
12537 throw {message: "JsonReader.read: Json object not found"};
12543 this.metaFromRemote = true;
12544 this.meta = o.metaData;
12545 this.recordType = Roo.data.Record.create(o.metaData.fields);
12546 this.onMetaChange(this.meta, this.recordType, o);
12548 return this.readRecords(o);
12551 // private function a store will implement
12552 onMetaChange : function(meta, recordType, o){
12559 simpleAccess: function(obj, subsc) {
12566 getJsonAccessor: function(){
12568 return function(expr) {
12570 return(re.test(expr))
12571 ? new Function("obj", "return obj." + expr)
12576 return Roo.emptyFn;
12581 * Create a data block containing Roo.data.Records from an XML document.
12582 * @param {Object} o An object which contains an Array of row objects in the property specified
12583 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12584 * which contains the total size of the dataset.
12585 * @return {Object} data A data block which is used by an Roo.data.Store object as
12586 * a cache of Roo.data.Records.
12588 readRecords : function(o){
12590 * After any data loads, the raw JSON data is available for further custom processing.
12594 var s = this.meta, Record = this.recordType,
12595 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12597 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12599 if(s.totalProperty) {
12600 this.getTotal = this.getJsonAccessor(s.totalProperty);
12602 if(s.successProperty) {
12603 this.getSuccess = this.getJsonAccessor(s.successProperty);
12605 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12607 var g = this.getJsonAccessor(s.id);
12608 this.getId = function(rec) {
12610 return (r === undefined || r === "") ? null : r;
12613 this.getId = function(){return null;};
12616 for(var jj = 0; jj < fl; jj++){
12618 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12619 this.ef[jj] = this.getJsonAccessor(map);
12623 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12624 if(s.totalProperty){
12625 var vt = parseInt(this.getTotal(o), 10);
12630 if(s.successProperty){
12631 var vs = this.getSuccess(o);
12632 if(vs === false || vs === 'false'){
12637 for(var i = 0; i < c; i++){
12640 var id = this.getId(n);
12641 for(var j = 0; j < fl; j++){
12643 var v = this.ef[j](n);
12645 Roo.log('missing convert for ' + f.name);
12649 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12651 var record = new Record(values, id);
12653 records[i] = record;
12659 totalRecords : totalRecords
12664 * Ext JS Library 1.1.1
12665 * Copyright(c) 2006-2007, Ext JS, LLC.
12667 * Originally Released Under LGPL - original licence link has changed is not relivant.
12670 * <script type="text/javascript">
12674 * @class Roo.data.ArrayReader
12675 * @extends Roo.data.DataReader
12676 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12677 * Each element of that Array represents a row of data fields. The
12678 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12679 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12683 var RecordDef = Roo.data.Record.create([
12684 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12685 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12687 var myReader = new Roo.data.ArrayReader({
12688 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12692 * This would consume an Array like this:
12694 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12696 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12698 * Create a new JsonReader
12699 * @param {Object} meta Metadata configuration options.
12700 * @param {Object} recordType Either an Array of field definition objects
12701 * as specified to {@link Roo.data.Record#create},
12702 * or an {@link Roo.data.Record} object
12703 * created using {@link Roo.data.Record#create}.
12705 Roo.data.ArrayReader = function(meta, recordType){
12706 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12709 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12711 * Create a data block containing Roo.data.Records from an XML document.
12712 * @param {Object} o An Array of row objects which represents the dataset.
12713 * @return {Object} data A data block which is used by an Roo.data.Store object as
12714 * a cache of Roo.data.Records.
12716 readRecords : function(o){
12717 var sid = this.meta ? this.meta.id : null;
12718 var recordType = this.recordType, fields = recordType.prototype.fields;
12721 for(var i = 0; i < root.length; i++){
12724 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12725 for(var j = 0, jlen = fields.length; j < jlen; j++){
12726 var f = fields.items[j];
12727 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12728 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12730 values[f.name] = v;
12732 var record = new recordType(values, id);
12734 records[records.length] = record;
12738 totalRecords : records.length
12747 * @class Roo.bootstrap.ComboBox
12748 * @extends Roo.bootstrap.TriggerField
12749 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12750 * @cfg {Boolean} append (true|false) default false
12751 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12752 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12753 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12754 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12755 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12756 * @cfg {Boolean} animate default true
12757 * @cfg {Boolean} emptyResultText only for touch device
12758 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12759 * @cfg {String} emptyTitle default ''
12761 * Create a new ComboBox.
12762 * @param {Object} config Configuration options
12764 Roo.bootstrap.ComboBox = function(config){
12765 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12769 * Fires when the dropdown list is expanded
12770 * @param {Roo.bootstrap.ComboBox} combo This combo box
12775 * Fires when the dropdown list is collapsed
12776 * @param {Roo.bootstrap.ComboBox} combo This combo box
12780 * @event beforeselect
12781 * Fires before a list item is selected. Return false to cancel the selection.
12782 * @param {Roo.bootstrap.ComboBox} combo This combo box
12783 * @param {Roo.data.Record} record The data record returned from the underlying store
12784 * @param {Number} index The index of the selected item in the dropdown list
12786 'beforeselect' : true,
12789 * Fires when a list item is selected
12790 * @param {Roo.bootstrap.ComboBox} combo This combo box
12791 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12792 * @param {Number} index The index of the selected item in the dropdown list
12796 * @event beforequery
12797 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12798 * The event object passed has these properties:
12799 * @param {Roo.bootstrap.ComboBox} combo This combo box
12800 * @param {String} query The query
12801 * @param {Boolean} forceAll true to force "all" query
12802 * @param {Boolean} cancel true to cancel the query
12803 * @param {Object} e The query event object
12805 'beforequery': true,
12808 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12809 * @param {Roo.bootstrap.ComboBox} combo This combo box
12814 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12815 * @param {Roo.bootstrap.ComboBox} combo This combo box
12816 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12821 * Fires when the remove value from the combobox array
12822 * @param {Roo.bootstrap.ComboBox} combo This combo box
12826 * @event afterremove
12827 * Fires when the remove value from the combobox array
12828 * @param {Roo.bootstrap.ComboBox} combo This combo box
12830 'afterremove' : true,
12832 * @event specialfilter
12833 * Fires when specialfilter
12834 * @param {Roo.bootstrap.ComboBox} combo This combo box
12836 'specialfilter' : true,
12839 * Fires when tick the element
12840 * @param {Roo.bootstrap.ComboBox} combo This combo box
12844 * @event touchviewdisplay
12845 * Fires when touch view require special display (default is using displayField)
12846 * @param {Roo.bootstrap.ComboBox} combo This combo box
12847 * @param {Object} cfg set html .
12849 'touchviewdisplay' : true
12854 this.tickItems = [];
12856 this.selectedIndex = -1;
12857 if(this.mode == 'local'){
12858 if(config.queryDelay === undefined){
12859 this.queryDelay = 10;
12861 if(config.minChars === undefined){
12867 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12870 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12871 * rendering into an Roo.Editor, defaults to false)
12874 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12875 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12878 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12881 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12882 * the dropdown list (defaults to undefined, with no header element)
12886 * @cfg {String/Roo.Template} tpl The template to use to render the output
12890 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12892 listWidth: undefined,
12894 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12895 * mode = 'remote' or 'text' if mode = 'local')
12897 displayField: undefined,
12900 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12901 * mode = 'remote' or 'value' if mode = 'local').
12902 * Note: use of a valueField requires the user make a selection
12903 * in order for a value to be mapped.
12905 valueField: undefined,
12907 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12912 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12913 * field's data value (defaults to the underlying DOM element's name)
12915 hiddenName: undefined,
12917 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12921 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12923 selectedClass: 'active',
12926 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12930 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12931 * anchor positions (defaults to 'tl-bl')
12933 listAlign: 'tl-bl?',
12935 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12939 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12940 * query specified by the allQuery config option (defaults to 'query')
12942 triggerAction: 'query',
12944 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12945 * (defaults to 4, does not apply if editable = false)
12949 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12950 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12954 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12955 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12959 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12960 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12964 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12965 * when editable = true (defaults to false)
12967 selectOnFocus:false,
12969 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12971 queryParam: 'query',
12973 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12974 * when mode = 'remote' (defaults to 'Loading...')
12976 loadingText: 'Loading...',
12978 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12982 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12986 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12987 * traditional select (defaults to true)
12991 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12995 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12999 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13000 * listWidth has a higher value)
13004 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13005 * allow the user to set arbitrary text into the field (defaults to false)
13007 forceSelection:false,
13009 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13010 * if typeAhead = true (defaults to 250)
13012 typeAheadDelay : 250,
13014 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13015 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13017 valueNotFoundText : undefined,
13019 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13021 blockFocus : false,
13024 * @cfg {Boolean} disableClear Disable showing of clear button.
13026 disableClear : false,
13028 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13030 alwaysQuery : false,
13033 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13038 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13040 invalidClass : "has-warning",
13043 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13045 validClass : "has-success",
13048 * @cfg {Boolean} specialFilter (true|false) special filter default false
13050 specialFilter : false,
13053 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13055 mobileTouchView : true,
13058 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13060 useNativeIOS : false,
13063 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13065 mobile_restrict_height : false,
13067 ios_options : false,
13079 btnPosition : 'right',
13080 triggerList : true,
13081 showToggleBtn : true,
13083 emptyResultText: 'Empty',
13084 triggerText : 'Select',
13087 // element that contains real text value.. (when hidden is used..)
13089 getAutoCreate : function()
13094 * Render classic select for iso
13097 if(Roo.isIOS && this.useNativeIOS){
13098 cfg = this.getAutoCreateNativeIOS();
13106 if(Roo.isTouch && this.mobileTouchView){
13107 cfg = this.getAutoCreateTouchView();
13114 if(!this.tickable){
13115 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13120 * ComboBox with tickable selections
13123 var align = this.labelAlign || this.parentLabelAlign();
13126 cls : 'form-group roo-combobox-tickable' //input-group
13129 var btn_text_select = '';
13130 var btn_text_done = '';
13131 var btn_text_cancel = '';
13133 if (this.btn_text_show) {
13134 btn_text_select = 'Select';
13135 btn_text_done = 'Done';
13136 btn_text_cancel = 'Cancel';
13141 cls : 'tickable-buttons',
13146 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13147 //html : this.triggerText
13148 html: btn_text_select
13154 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13156 html: btn_text_done
13162 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13164 html: btn_text_cancel
13170 buttons.cn.unshift({
13172 cls: 'roo-select2-search-field-input'
13178 Roo.each(buttons.cn, function(c){
13180 c.cls += ' btn-' + _this.size;
13183 if (_this.disabled) {
13194 cls: 'form-hidden-field'
13198 cls: 'roo-select2-choices',
13202 cls: 'roo-select2-search-field',
13213 cls: 'roo-select2-container input-group roo-select2-container-multi',
13218 // cls: 'typeahead typeahead-long dropdown-menu',
13219 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13224 if(this.hasFeedback && !this.allowBlank){
13228 cls: 'glyphicon form-control-feedback'
13231 combobox.cn.push(feedback);
13235 if (align ==='left' && this.fieldLabel.length) {
13237 cfg.cls += ' roo-form-group-label-left';
13242 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13243 tooltip : 'This field is required'
13248 cls : 'control-label',
13249 html : this.fieldLabel
13261 var labelCfg = cfg.cn[1];
13262 var contentCfg = cfg.cn[2];
13265 if(this.indicatorpos == 'right'){
13271 cls : 'control-label',
13275 html : this.fieldLabel
13279 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13280 tooltip : 'This field is required'
13295 labelCfg = cfg.cn[0];
13296 contentCfg = cfg.cn[1];
13300 if(this.labelWidth > 12){
13301 labelCfg.style = "width: " + this.labelWidth + 'px';
13304 if(this.labelWidth < 13 && this.labelmd == 0){
13305 this.labelmd = this.labelWidth;
13308 if(this.labellg > 0){
13309 labelCfg.cls += ' col-lg-' + this.labellg;
13310 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13313 if(this.labelmd > 0){
13314 labelCfg.cls += ' col-md-' + this.labelmd;
13315 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13318 if(this.labelsm > 0){
13319 labelCfg.cls += ' col-sm-' + this.labelsm;
13320 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13323 if(this.labelxs > 0){
13324 labelCfg.cls += ' col-xs-' + this.labelxs;
13325 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13329 } else if ( this.fieldLabel.length) {
13330 // Roo.log(" label");
13334 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13335 tooltip : 'This field is required'
13339 //cls : 'input-group-addon',
13340 html : this.fieldLabel
13345 if(this.indicatorpos == 'right'){
13349 //cls : 'input-group-addon',
13350 html : this.fieldLabel
13354 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13355 tooltip : 'This field is required'
13364 // Roo.log(" no label && no align");
13371 ['xs','sm','md','lg'].map(function(size){
13372 if (settings[size]) {
13373 cfg.cls += ' col-' + size + '-' + settings[size];
13381 _initEventsCalled : false,
13384 initEvents: function()
13386 if (this._initEventsCalled) { // as we call render... prevent looping...
13389 this._initEventsCalled = true;
13392 throw "can not find store for combo";
13395 this.indicator = this.indicatorEl();
13397 this.store = Roo.factory(this.store, Roo.data);
13398 this.store.parent = this;
13400 // if we are building from html. then this element is so complex, that we can not really
13401 // use the rendered HTML.
13402 // so we have to trash and replace the previous code.
13403 if (Roo.XComponent.build_from_html) {
13404 // remove this element....
13405 var e = this.el.dom, k=0;
13406 while (e ) { e = e.previousSibling; ++k;}
13411 this.rendered = false;
13413 this.render(this.parent().getChildContainer(true), k);
13416 if(Roo.isIOS && this.useNativeIOS){
13417 this.initIOSView();
13425 if(Roo.isTouch && this.mobileTouchView){
13426 this.initTouchView();
13431 this.initTickableEvents();
13435 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13437 if(this.hiddenName){
13439 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13441 this.hiddenField.dom.value =
13442 this.hiddenValue !== undefined ? this.hiddenValue :
13443 this.value !== undefined ? this.value : '';
13445 // prevent input submission
13446 this.el.dom.removeAttribute('name');
13447 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13452 // this.el.dom.setAttribute('autocomplete', 'off');
13455 var cls = 'x-combo-list';
13457 //this.list = new Roo.Layer({
13458 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13464 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13465 _this.list.setWidth(lw);
13468 this.list.on('mouseover', this.onViewOver, this);
13469 this.list.on('mousemove', this.onViewMove, this);
13470 this.list.on('scroll', this.onViewScroll, this);
13473 this.list.swallowEvent('mousewheel');
13474 this.assetHeight = 0;
13477 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13478 this.assetHeight += this.header.getHeight();
13481 this.innerList = this.list.createChild({cls:cls+'-inner'});
13482 this.innerList.on('mouseover', this.onViewOver, this);
13483 this.innerList.on('mousemove', this.onViewMove, this);
13484 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13486 if(this.allowBlank && !this.pageSize && !this.disableClear){
13487 this.footer = this.list.createChild({cls:cls+'-ft'});
13488 this.pageTb = new Roo.Toolbar(this.footer);
13492 this.footer = this.list.createChild({cls:cls+'-ft'});
13493 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13494 {pageSize: this.pageSize});
13498 if (this.pageTb && this.allowBlank && !this.disableClear) {
13500 this.pageTb.add(new Roo.Toolbar.Fill(), {
13501 cls: 'x-btn-icon x-btn-clear',
13503 handler: function()
13506 _this.clearValue();
13507 _this.onSelect(false, -1);
13512 this.assetHeight += this.footer.getHeight();
13517 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13520 this.view = new Roo.View(this.list, this.tpl, {
13521 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13523 //this.view.wrapEl.setDisplayed(false);
13524 this.view.on('click', this.onViewClick, this);
13527 this.store.on('beforeload', this.onBeforeLoad, this);
13528 this.store.on('load', this.onLoad, this);
13529 this.store.on('loadexception', this.onLoadException, this);
13531 if(this.resizable){
13532 this.resizer = new Roo.Resizable(this.list, {
13533 pinned:true, handles:'se'
13535 this.resizer.on('resize', function(r, w, h){
13536 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13537 this.listWidth = w;
13538 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13539 this.restrictHeight();
13541 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13544 if(!this.editable){
13545 this.editable = true;
13546 this.setEditable(false);
13551 if (typeof(this.events.add.listeners) != 'undefined') {
13553 this.addicon = this.wrap.createChild(
13554 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13556 this.addicon.on('click', function(e) {
13557 this.fireEvent('add', this);
13560 if (typeof(this.events.edit.listeners) != 'undefined') {
13562 this.editicon = this.wrap.createChild(
13563 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13564 if (this.addicon) {
13565 this.editicon.setStyle('margin-left', '40px');
13567 this.editicon.on('click', function(e) {
13569 // we fire even if inothing is selected..
13570 this.fireEvent('edit', this, this.lastData );
13576 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13577 "up" : function(e){
13578 this.inKeyMode = true;
13582 "down" : function(e){
13583 if(!this.isExpanded()){
13584 this.onTriggerClick();
13586 this.inKeyMode = true;
13591 "enter" : function(e){
13592 // this.onViewClick();
13596 if(this.fireEvent("specialkey", this, e)){
13597 this.onViewClick(false);
13603 "esc" : function(e){
13607 "tab" : function(e){
13610 if(this.fireEvent("specialkey", this, e)){
13611 this.onViewClick(false);
13619 doRelay : function(foo, bar, hname){
13620 if(hname == 'down' || this.scope.isExpanded()){
13621 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13630 this.queryDelay = Math.max(this.queryDelay || 10,
13631 this.mode == 'local' ? 10 : 250);
13634 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13636 if(this.typeAhead){
13637 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13639 if(this.editable !== false){
13640 this.inputEl().on("keyup", this.onKeyUp, this);
13642 if(this.forceSelection){
13643 this.inputEl().on('blur', this.doForce, this);
13647 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13648 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13652 initTickableEvents: function()
13656 if(this.hiddenName){
13658 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13660 this.hiddenField.dom.value =
13661 this.hiddenValue !== undefined ? this.hiddenValue :
13662 this.value !== undefined ? this.value : '';
13664 // prevent input submission
13665 this.el.dom.removeAttribute('name');
13666 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13671 // this.list = this.el.select('ul.dropdown-menu',true).first();
13673 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13674 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13675 if(this.triggerList){
13676 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13679 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13680 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13682 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13683 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13685 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13686 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13688 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13689 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13690 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13693 this.cancelBtn.hide();
13698 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13699 _this.list.setWidth(lw);
13702 this.list.on('mouseover', this.onViewOver, this);
13703 this.list.on('mousemove', this.onViewMove, this);
13705 this.list.on('scroll', this.onViewScroll, this);
13708 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13709 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13712 this.view = new Roo.View(this.list, this.tpl, {
13717 selectedClass: this.selectedClass
13720 //this.view.wrapEl.setDisplayed(false);
13721 this.view.on('click', this.onViewClick, this);
13725 this.store.on('beforeload', this.onBeforeLoad, this);
13726 this.store.on('load', this.onLoad, this);
13727 this.store.on('loadexception', this.onLoadException, this);
13730 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13731 "up" : function(e){
13732 this.inKeyMode = true;
13736 "down" : function(e){
13737 this.inKeyMode = true;
13741 "enter" : function(e){
13742 if(this.fireEvent("specialkey", this, e)){
13743 this.onViewClick(false);
13749 "esc" : function(e){
13750 this.onTickableFooterButtonClick(e, false, false);
13753 "tab" : function(e){
13754 this.fireEvent("specialkey", this, e);
13756 this.onTickableFooterButtonClick(e, false, false);
13763 doRelay : function(e, fn, key){
13764 if(this.scope.isExpanded()){
13765 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13774 this.queryDelay = Math.max(this.queryDelay || 10,
13775 this.mode == 'local' ? 10 : 250);
13778 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13780 if(this.typeAhead){
13781 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13784 if(this.editable !== false){
13785 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13788 this.indicator = this.indicatorEl();
13790 if(this.indicator){
13791 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13792 this.indicator.hide();
13797 onDestroy : function(){
13799 this.view.setStore(null);
13800 this.view.el.removeAllListeners();
13801 this.view.el.remove();
13802 this.view.purgeListeners();
13805 this.list.dom.innerHTML = '';
13809 this.store.un('beforeload', this.onBeforeLoad, this);
13810 this.store.un('load', this.onLoad, this);
13811 this.store.un('loadexception', this.onLoadException, this);
13813 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13817 fireKey : function(e){
13818 if(e.isNavKeyPress() && !this.list.isVisible()){
13819 this.fireEvent("specialkey", this, e);
13824 onResize: function(w, h){
13825 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13827 // if(typeof w != 'number'){
13828 // // we do not handle it!?!?
13831 // var tw = this.trigger.getWidth();
13832 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13833 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13835 // this.inputEl().setWidth( this.adjustWidth('input', x));
13837 // //this.trigger.setStyle('left', x+'px');
13839 // if(this.list && this.listWidth === undefined){
13840 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13841 // this.list.setWidth(lw);
13842 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13850 * Allow or prevent the user from directly editing the field text. If false is passed,
13851 * the user will only be able to select from the items defined in the dropdown list. This method
13852 * is the runtime equivalent of setting the 'editable' config option at config time.
13853 * @param {Boolean} value True to allow the user to directly edit the field text
13855 setEditable : function(value){
13856 if(value == this.editable){
13859 this.editable = value;
13861 this.inputEl().dom.setAttribute('readOnly', true);
13862 this.inputEl().on('mousedown', this.onTriggerClick, this);
13863 this.inputEl().addClass('x-combo-noedit');
13865 this.inputEl().dom.setAttribute('readOnly', false);
13866 this.inputEl().un('mousedown', this.onTriggerClick, this);
13867 this.inputEl().removeClass('x-combo-noedit');
13873 onBeforeLoad : function(combo,opts){
13874 if(!this.hasFocus){
13878 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13880 this.restrictHeight();
13881 this.selectedIndex = -1;
13885 onLoad : function(){
13887 this.hasQuery = false;
13889 if(!this.hasFocus){
13893 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13894 this.loading.hide();
13897 if(this.store.getCount() > 0){
13900 this.restrictHeight();
13901 if(this.lastQuery == this.allQuery){
13902 if(this.editable && !this.tickable){
13903 this.inputEl().dom.select();
13907 !this.selectByValue(this.value, true) &&
13910 !this.store.lastOptions ||
13911 typeof(this.store.lastOptions.add) == 'undefined' ||
13912 this.store.lastOptions.add != true
13915 this.select(0, true);
13918 if(this.autoFocus){
13921 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13922 this.taTask.delay(this.typeAheadDelay);
13926 this.onEmptyResults();
13932 onLoadException : function()
13934 this.hasQuery = false;
13936 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13937 this.loading.hide();
13940 if(this.tickable && this.editable){
13945 // only causes errors at present
13946 //Roo.log(this.store.reader.jsonData);
13947 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13949 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13955 onTypeAhead : function(){
13956 if(this.store.getCount() > 0){
13957 var r = this.store.getAt(0);
13958 var newValue = r.data[this.displayField];
13959 var len = newValue.length;
13960 var selStart = this.getRawValue().length;
13962 if(selStart != len){
13963 this.setRawValue(newValue);
13964 this.selectText(selStart, newValue.length);
13970 onSelect : function(record, index){
13972 if(this.fireEvent('beforeselect', this, record, index) !== false){
13974 this.setFromData(index > -1 ? record.data : false);
13977 this.fireEvent('select', this, record, index);
13982 * Returns the currently selected field value or empty string if no value is set.
13983 * @return {String} value The selected value
13985 getValue : function()
13987 if(Roo.isIOS && this.useNativeIOS){
13988 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13992 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13995 if(this.valueField){
13996 return typeof this.value != 'undefined' ? this.value : '';
13998 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14002 getRawValue : function()
14004 if(Roo.isIOS && this.useNativeIOS){
14005 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14008 var v = this.inputEl().getValue();
14014 * Clears any text/value currently set in the field
14016 clearValue : function(){
14018 if(this.hiddenField){
14019 this.hiddenField.dom.value = '';
14022 this.setRawValue('');
14023 this.lastSelectionText = '';
14024 this.lastData = false;
14026 var close = this.closeTriggerEl();
14037 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14038 * will be displayed in the field. If the value does not match the data value of an existing item,
14039 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14040 * Otherwise the field will be blank (although the value will still be set).
14041 * @param {String} value The value to match
14043 setValue : function(v)
14045 if(Roo.isIOS && this.useNativeIOS){
14046 this.setIOSValue(v);
14056 if(this.valueField){
14057 var r = this.findRecord(this.valueField, v);
14059 text = r.data[this.displayField];
14060 }else if(this.valueNotFoundText !== undefined){
14061 text = this.valueNotFoundText;
14064 this.lastSelectionText = text;
14065 if(this.hiddenField){
14066 this.hiddenField.dom.value = v;
14068 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14071 var close = this.closeTriggerEl();
14074 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14080 * @property {Object} the last set data for the element
14085 * Sets the value of the field based on a object which is related to the record format for the store.
14086 * @param {Object} value the value to set as. or false on reset?
14088 setFromData : function(o){
14095 var dv = ''; // display value
14096 var vv = ''; // value value..
14098 if (this.displayField) {
14099 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14101 // this is an error condition!!!
14102 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14105 if(this.valueField){
14106 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14109 var close = this.closeTriggerEl();
14112 if(dv.length || vv * 1 > 0){
14114 this.blockFocus=true;
14120 if(this.hiddenField){
14121 this.hiddenField.dom.value = vv;
14123 this.lastSelectionText = dv;
14124 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14128 // no hidden field.. - we store the value in 'value', but still display
14129 // display field!!!!
14130 this.lastSelectionText = dv;
14131 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14138 reset : function(){
14139 // overridden so that last data is reset..
14146 this.setValue(this.originalValue);
14147 //this.clearInvalid();
14148 this.lastData = false;
14150 this.view.clearSelections();
14156 findRecord : function(prop, value){
14158 if(this.store.getCount() > 0){
14159 this.store.each(function(r){
14160 if(r.data[prop] == value){
14170 getName: function()
14172 // returns hidden if it's set..
14173 if (!this.rendered) {return ''};
14174 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14178 onViewMove : function(e, t){
14179 this.inKeyMode = false;
14183 onViewOver : function(e, t){
14184 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14187 var item = this.view.findItemFromChild(t);
14190 var index = this.view.indexOf(item);
14191 this.select(index, false);
14196 onViewClick : function(view, doFocus, el, e)
14198 var index = this.view.getSelectedIndexes()[0];
14200 var r = this.store.getAt(index);
14204 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14211 Roo.each(this.tickItems, function(v,k){
14213 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14215 _this.tickItems.splice(k, 1);
14217 if(typeof(e) == 'undefined' && view == false){
14218 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14230 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14231 this.tickItems.push(r.data);
14234 if(typeof(e) == 'undefined' && view == false){
14235 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14242 this.onSelect(r, index);
14244 if(doFocus !== false && !this.blockFocus){
14245 this.inputEl().focus();
14250 restrictHeight : function(){
14251 //this.innerList.dom.style.height = '';
14252 //var inner = this.innerList.dom;
14253 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14254 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14255 //this.list.beginUpdate();
14256 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14257 this.list.alignTo(this.inputEl(), this.listAlign);
14258 this.list.alignTo(this.inputEl(), this.listAlign);
14259 //this.list.endUpdate();
14263 onEmptyResults : function(){
14265 if(this.tickable && this.editable){
14266 this.hasFocus = false;
14267 this.restrictHeight();
14275 * Returns true if the dropdown list is expanded, else false.
14277 isExpanded : function(){
14278 return this.list.isVisible();
14282 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14283 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14284 * @param {String} value The data value of the item to select
14285 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14286 * selected item if it is not currently in view (defaults to true)
14287 * @return {Boolean} True if the value matched an item in the list, else false
14289 selectByValue : function(v, scrollIntoView){
14290 if(v !== undefined && v !== null){
14291 var r = this.findRecord(this.valueField || this.displayField, v);
14293 this.select(this.store.indexOf(r), scrollIntoView);
14301 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14302 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14303 * @param {Number} index The zero-based index of the list item to select
14304 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14305 * selected item if it is not currently in view (defaults to true)
14307 select : function(index, scrollIntoView){
14308 this.selectedIndex = index;
14309 this.view.select(index);
14310 if(scrollIntoView !== false){
14311 var el = this.view.getNode(index);
14313 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14316 this.list.scrollChildIntoView(el, false);
14322 selectNext : function(){
14323 var ct = this.store.getCount();
14325 if(this.selectedIndex == -1){
14327 }else if(this.selectedIndex < ct-1){
14328 this.select(this.selectedIndex+1);
14334 selectPrev : function(){
14335 var ct = this.store.getCount();
14337 if(this.selectedIndex == -1){
14339 }else if(this.selectedIndex != 0){
14340 this.select(this.selectedIndex-1);
14346 onKeyUp : function(e){
14347 if(this.editable !== false && !e.isSpecialKey()){
14348 this.lastKey = e.getKey();
14349 this.dqTask.delay(this.queryDelay);
14354 validateBlur : function(){
14355 return !this.list || !this.list.isVisible();
14359 initQuery : function(){
14361 var v = this.getRawValue();
14363 if(this.tickable && this.editable){
14364 v = this.tickableInputEl().getValue();
14371 doForce : function(){
14372 if(this.inputEl().dom.value.length > 0){
14373 this.inputEl().dom.value =
14374 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14380 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14381 * query allowing the query action to be canceled if needed.
14382 * @param {String} query The SQL query to execute
14383 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14384 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14385 * saved in the current store (defaults to false)
14387 doQuery : function(q, forceAll){
14389 if(q === undefined || q === null){
14394 forceAll: forceAll,
14398 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14403 forceAll = qe.forceAll;
14404 if(forceAll === true || (q.length >= this.minChars)){
14406 this.hasQuery = true;
14408 if(this.lastQuery != q || this.alwaysQuery){
14409 this.lastQuery = q;
14410 if(this.mode == 'local'){
14411 this.selectedIndex = -1;
14413 this.store.clearFilter();
14416 if(this.specialFilter){
14417 this.fireEvent('specialfilter', this);
14422 this.store.filter(this.displayField, q);
14425 this.store.fireEvent("datachanged", this.store);
14432 this.store.baseParams[this.queryParam] = q;
14434 var options = {params : this.getParams(q)};
14437 options.add = true;
14438 options.params.start = this.page * this.pageSize;
14441 this.store.load(options);
14444 * this code will make the page width larger, at the beginning, the list not align correctly,
14445 * we should expand the list on onLoad
14446 * so command out it
14451 this.selectedIndex = -1;
14456 this.loadNext = false;
14460 getParams : function(q){
14462 //p[this.queryParam] = q;
14466 p.limit = this.pageSize;
14472 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14474 collapse : function(){
14475 if(!this.isExpanded()){
14481 this.hasFocus = false;
14485 this.cancelBtn.hide();
14486 this.trigger.show();
14489 this.tickableInputEl().dom.value = '';
14490 this.tickableInputEl().blur();
14495 Roo.get(document).un('mousedown', this.collapseIf, this);
14496 Roo.get(document).un('mousewheel', this.collapseIf, this);
14497 if (!this.editable) {
14498 Roo.get(document).un('keydown', this.listKeyPress, this);
14500 this.fireEvent('collapse', this);
14506 collapseIf : function(e){
14507 var in_combo = e.within(this.el);
14508 var in_list = e.within(this.list);
14509 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14511 if (in_combo || in_list || is_list) {
14512 //e.stopPropagation();
14517 this.onTickableFooterButtonClick(e, false, false);
14525 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14527 expand : function(){
14529 if(this.isExpanded() || !this.hasFocus){
14533 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14534 this.list.setWidth(lw);
14540 this.restrictHeight();
14544 this.tickItems = Roo.apply([], this.item);
14547 this.cancelBtn.show();
14548 this.trigger.hide();
14551 this.tickableInputEl().focus();
14556 Roo.get(document).on('mousedown', this.collapseIf, this);
14557 Roo.get(document).on('mousewheel', this.collapseIf, this);
14558 if (!this.editable) {
14559 Roo.get(document).on('keydown', this.listKeyPress, this);
14562 this.fireEvent('expand', this);
14566 // Implements the default empty TriggerField.onTriggerClick function
14567 onTriggerClick : function(e)
14569 Roo.log('trigger click');
14571 if(this.disabled || !this.triggerList){
14576 this.loadNext = false;
14578 if(this.isExpanded()){
14580 if (!this.blockFocus) {
14581 this.inputEl().focus();
14585 this.hasFocus = true;
14586 if(this.triggerAction == 'all') {
14587 this.doQuery(this.allQuery, true);
14589 this.doQuery(this.getRawValue());
14591 if (!this.blockFocus) {
14592 this.inputEl().focus();
14597 onTickableTriggerClick : function(e)
14604 this.loadNext = false;
14605 this.hasFocus = true;
14607 if(this.triggerAction == 'all') {
14608 this.doQuery(this.allQuery, true);
14610 this.doQuery(this.getRawValue());
14614 onSearchFieldClick : function(e)
14616 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14617 this.onTickableFooterButtonClick(e, false, false);
14621 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14626 this.loadNext = false;
14627 this.hasFocus = true;
14629 if(this.triggerAction == 'all') {
14630 this.doQuery(this.allQuery, true);
14632 this.doQuery(this.getRawValue());
14636 listKeyPress : function(e)
14638 //Roo.log('listkeypress');
14639 // scroll to first matching element based on key pres..
14640 if (e.isSpecialKey()) {
14643 var k = String.fromCharCode(e.getKey()).toUpperCase();
14646 var csel = this.view.getSelectedNodes();
14647 var cselitem = false;
14649 var ix = this.view.indexOf(csel[0]);
14650 cselitem = this.store.getAt(ix);
14651 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14657 this.store.each(function(v) {
14659 // start at existing selection.
14660 if (cselitem.id == v.id) {
14666 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14667 match = this.store.indexOf(v);
14673 if (match === false) {
14674 return true; // no more action?
14677 this.view.select(match);
14678 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14679 sn.scrollIntoView(sn.dom.parentNode, false);
14682 onViewScroll : function(e, t){
14684 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){
14688 this.hasQuery = true;
14690 this.loading = this.list.select('.loading', true).first();
14692 if(this.loading === null){
14693 this.list.createChild({
14695 cls: 'loading roo-select2-more-results roo-select2-active',
14696 html: 'Loading more results...'
14699 this.loading = this.list.select('.loading', true).first();
14701 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14703 this.loading.hide();
14706 this.loading.show();
14711 this.loadNext = true;
14713 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14718 addItem : function(o)
14720 var dv = ''; // display value
14722 if (this.displayField) {
14723 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14725 // this is an error condition!!!
14726 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14733 var choice = this.choices.createChild({
14735 cls: 'roo-select2-search-choice',
14744 cls: 'roo-select2-search-choice-close fa fa-times',
14749 }, this.searchField);
14751 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14753 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14761 this.inputEl().dom.value = '';
14766 onRemoveItem : function(e, _self, o)
14768 e.preventDefault();
14770 this.lastItem = Roo.apply([], this.item);
14772 var index = this.item.indexOf(o.data) * 1;
14775 Roo.log('not this item?!');
14779 this.item.splice(index, 1);
14784 this.fireEvent('remove', this, e);
14790 syncValue : function()
14792 if(!this.item.length){
14799 Roo.each(this.item, function(i){
14800 if(_this.valueField){
14801 value.push(i[_this.valueField]);
14808 this.value = value.join(',');
14810 if(this.hiddenField){
14811 this.hiddenField.dom.value = this.value;
14814 this.store.fireEvent("datachanged", this.store);
14819 clearItem : function()
14821 if(!this.multiple){
14827 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14835 if(this.tickable && !Roo.isTouch){
14836 this.view.refresh();
14840 inputEl: function ()
14842 if(Roo.isIOS && this.useNativeIOS){
14843 return this.el.select('select.roo-ios-select', true).first();
14846 if(Roo.isTouch && this.mobileTouchView){
14847 return this.el.select('input.form-control',true).first();
14851 return this.searchField;
14854 return this.el.select('input.form-control',true).first();
14857 onTickableFooterButtonClick : function(e, btn, el)
14859 e.preventDefault();
14861 this.lastItem = Roo.apply([], this.item);
14863 if(btn && btn.name == 'cancel'){
14864 this.tickItems = Roo.apply([], this.item);
14873 Roo.each(this.tickItems, function(o){
14881 validate : function()
14883 if(this.getVisibilityEl().hasClass('hidden')){
14887 var v = this.getRawValue();
14890 v = this.getValue();
14893 if(this.disabled || this.allowBlank || v.length){
14898 this.markInvalid();
14902 tickableInputEl : function()
14904 if(!this.tickable || !this.editable){
14905 return this.inputEl();
14908 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14912 getAutoCreateTouchView : function()
14917 cls: 'form-group' //input-group
14923 type : this.inputType,
14924 cls : 'form-control x-combo-noedit',
14925 autocomplete: 'new-password',
14926 placeholder : this.placeholder || '',
14931 input.name = this.name;
14935 input.cls += ' input-' + this.size;
14938 if (this.disabled) {
14939 input.disabled = true;
14950 inputblock.cls += ' input-group';
14952 inputblock.cn.unshift({
14954 cls : 'input-group-addon',
14959 if(this.removable && !this.multiple){
14960 inputblock.cls += ' roo-removable';
14962 inputblock.cn.push({
14965 cls : 'roo-combo-removable-btn close'
14969 if(this.hasFeedback && !this.allowBlank){
14971 inputblock.cls += ' has-feedback';
14973 inputblock.cn.push({
14975 cls: 'glyphicon form-control-feedback'
14982 inputblock.cls += (this.before) ? '' : ' input-group';
14984 inputblock.cn.push({
14986 cls : 'input-group-addon',
14997 cls: 'form-hidden-field'
15011 cls: 'form-hidden-field'
15015 cls: 'roo-select2-choices',
15019 cls: 'roo-select2-search-field',
15032 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15038 if(!this.multiple && this.showToggleBtn){
15045 if (this.caret != false) {
15048 cls: 'fa fa-' + this.caret
15055 cls : 'input-group-addon btn dropdown-toggle',
15060 cls: 'combobox-clear',
15074 combobox.cls += ' roo-select2-container-multi';
15077 var align = this.labelAlign || this.parentLabelAlign();
15079 if (align ==='left' && this.fieldLabel.length) {
15084 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15085 tooltip : 'This field is required'
15089 cls : 'control-label',
15090 html : this.fieldLabel
15101 var labelCfg = cfg.cn[1];
15102 var contentCfg = cfg.cn[2];
15105 if(this.indicatorpos == 'right'){
15110 cls : 'control-label',
15114 html : this.fieldLabel
15118 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15119 tooltip : 'This field is required'
15132 labelCfg = cfg.cn[0];
15133 contentCfg = cfg.cn[1];
15138 if(this.labelWidth > 12){
15139 labelCfg.style = "width: " + this.labelWidth + 'px';
15142 if(this.labelWidth < 13 && this.labelmd == 0){
15143 this.labelmd = this.labelWidth;
15146 if(this.labellg > 0){
15147 labelCfg.cls += ' col-lg-' + this.labellg;
15148 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15151 if(this.labelmd > 0){
15152 labelCfg.cls += ' col-md-' + this.labelmd;
15153 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15156 if(this.labelsm > 0){
15157 labelCfg.cls += ' col-sm-' + this.labelsm;
15158 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15161 if(this.labelxs > 0){
15162 labelCfg.cls += ' col-xs-' + this.labelxs;
15163 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15167 } else if ( this.fieldLabel.length) {
15171 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15172 tooltip : 'This field is required'
15176 cls : 'control-label',
15177 html : this.fieldLabel
15188 if(this.indicatorpos == 'right'){
15192 cls : 'control-label',
15193 html : this.fieldLabel,
15197 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15198 tooltip : 'This field is required'
15215 var settings = this;
15217 ['xs','sm','md','lg'].map(function(size){
15218 if (settings[size]) {
15219 cfg.cls += ' col-' + size + '-' + settings[size];
15226 initTouchView : function()
15228 this.renderTouchView();
15230 this.touchViewEl.on('scroll', function(){
15231 this.el.dom.scrollTop = 0;
15234 this.originalValue = this.getValue();
15236 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15238 this.inputEl().on("click", this.showTouchView, this);
15239 if (this.triggerEl) {
15240 this.triggerEl.on("click", this.showTouchView, this);
15244 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15245 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15247 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15249 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15250 this.store.on('load', this.onTouchViewLoad, this);
15251 this.store.on('loadexception', this.onTouchViewLoadException, this);
15253 if(this.hiddenName){
15255 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15257 this.hiddenField.dom.value =
15258 this.hiddenValue !== undefined ? this.hiddenValue :
15259 this.value !== undefined ? this.value : '';
15261 this.el.dom.removeAttribute('name');
15262 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15266 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15267 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15270 if(this.removable && !this.multiple){
15271 var close = this.closeTriggerEl();
15273 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15274 close.on('click', this.removeBtnClick, this, close);
15278 * fix the bug in Safari iOS8
15280 this.inputEl().on("focus", function(e){
15281 document.activeElement.blur();
15284 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15291 renderTouchView : function()
15293 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15294 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15296 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15297 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15299 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15300 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15301 this.touchViewBodyEl.setStyle('overflow', 'auto');
15303 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15304 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15306 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15307 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15311 showTouchView : function()
15317 this.touchViewHeaderEl.hide();
15319 if(this.modalTitle.length){
15320 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15321 this.touchViewHeaderEl.show();
15324 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15325 this.touchViewEl.show();
15327 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15329 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15330 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15332 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15334 if(this.modalTitle.length){
15335 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15338 this.touchViewBodyEl.setHeight(bodyHeight);
15342 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15344 this.touchViewEl.addClass('in');
15347 if(this._touchViewMask){
15348 Roo.get(document.body).addClass("x-body-masked");
15349 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15350 this._touchViewMask.setStyle('z-index', 10000);
15351 this._touchViewMask.addClass('show');
15354 this.doTouchViewQuery();
15358 hideTouchView : function()
15360 this.touchViewEl.removeClass('in');
15364 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15366 this.touchViewEl.setStyle('display', 'none');
15369 if(this._touchViewMask){
15370 this._touchViewMask.removeClass('show');
15371 Roo.get(document.body).removeClass("x-body-masked");
15375 setTouchViewValue : function()
15382 Roo.each(this.tickItems, function(o){
15387 this.hideTouchView();
15390 doTouchViewQuery : function()
15399 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15403 if(!this.alwaysQuery || this.mode == 'local'){
15404 this.onTouchViewLoad();
15411 onTouchViewBeforeLoad : function(combo,opts)
15417 onTouchViewLoad : function()
15419 if(this.store.getCount() < 1){
15420 this.onTouchViewEmptyResults();
15424 this.clearTouchView();
15426 var rawValue = this.getRawValue();
15428 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15430 this.tickItems = [];
15432 this.store.data.each(function(d, rowIndex){
15433 var row = this.touchViewListGroup.createChild(template);
15435 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15436 row.addClass(d.data.cls);
15439 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15442 html : d.data[this.displayField]
15445 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15446 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15449 row.removeClass('selected');
15450 if(!this.multiple && this.valueField &&
15451 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15454 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15455 row.addClass('selected');
15458 if(this.multiple && this.valueField &&
15459 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15463 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15464 this.tickItems.push(d.data);
15467 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15471 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15473 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15475 if(this.modalTitle.length){
15476 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15479 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15481 if(this.mobile_restrict_height && listHeight < bodyHeight){
15482 this.touchViewBodyEl.setHeight(listHeight);
15487 if(firstChecked && listHeight > bodyHeight){
15488 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15493 onTouchViewLoadException : function()
15495 this.hideTouchView();
15498 onTouchViewEmptyResults : function()
15500 this.clearTouchView();
15502 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15504 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15508 clearTouchView : function()
15510 this.touchViewListGroup.dom.innerHTML = '';
15513 onTouchViewClick : function(e, el, o)
15515 e.preventDefault();
15518 var rowIndex = o.rowIndex;
15520 var r = this.store.getAt(rowIndex);
15522 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15524 if(!this.multiple){
15525 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15526 c.dom.removeAttribute('checked');
15529 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15531 this.setFromData(r.data);
15533 var close = this.closeTriggerEl();
15539 this.hideTouchView();
15541 this.fireEvent('select', this, r, rowIndex);
15546 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15547 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15548 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15552 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15553 this.addItem(r.data);
15554 this.tickItems.push(r.data);
15558 getAutoCreateNativeIOS : function()
15561 cls: 'form-group' //input-group,
15566 cls : 'roo-ios-select'
15570 combobox.name = this.name;
15573 if (this.disabled) {
15574 combobox.disabled = true;
15577 var settings = this;
15579 ['xs','sm','md','lg'].map(function(size){
15580 if (settings[size]) {
15581 cfg.cls += ' col-' + size + '-' + settings[size];
15591 initIOSView : function()
15593 this.store.on('load', this.onIOSViewLoad, this);
15598 onIOSViewLoad : function()
15600 if(this.store.getCount() < 1){
15604 this.clearIOSView();
15606 if(this.allowBlank) {
15608 var default_text = '-- SELECT --';
15610 if(this.placeholder.length){
15611 default_text = this.placeholder;
15614 if(this.emptyTitle.length){
15615 default_text += ' - ' + this.emptyTitle + ' -';
15618 var opt = this.inputEl().createChild({
15621 html : default_text
15625 o[this.valueField] = 0;
15626 o[this.displayField] = default_text;
15628 this.ios_options.push({
15635 this.store.data.each(function(d, rowIndex){
15639 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15640 html = d.data[this.displayField];
15645 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15646 value = d.data[this.valueField];
15655 if(this.value == d.data[this.valueField]){
15656 option['selected'] = true;
15659 var opt = this.inputEl().createChild(option);
15661 this.ios_options.push({
15668 this.inputEl().on('change', function(){
15669 this.fireEvent('select', this);
15674 clearIOSView: function()
15676 this.inputEl().dom.innerHTML = '';
15678 this.ios_options = [];
15681 setIOSValue: function(v)
15685 if(!this.ios_options){
15689 Roo.each(this.ios_options, function(opts){
15691 opts.el.dom.removeAttribute('selected');
15693 if(opts.data[this.valueField] != v){
15697 opts.el.dom.setAttribute('selected', true);
15703 * @cfg {Boolean} grow
15707 * @cfg {Number} growMin
15711 * @cfg {Number} growMax
15720 Roo.apply(Roo.bootstrap.ComboBox, {
15724 cls: 'modal-header',
15746 cls: 'list-group-item',
15750 cls: 'roo-combobox-list-group-item-value'
15754 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15768 listItemCheckbox : {
15770 cls: 'list-group-item',
15774 cls: 'roo-combobox-list-group-item-value'
15778 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15794 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15799 cls: 'modal-footer',
15807 cls: 'col-xs-6 text-left',
15810 cls: 'btn btn-danger roo-touch-view-cancel',
15816 cls: 'col-xs-6 text-right',
15819 cls: 'btn btn-success roo-touch-view-ok',
15830 Roo.apply(Roo.bootstrap.ComboBox, {
15832 touchViewTemplate : {
15834 cls: 'modal fade roo-combobox-touch-view',
15838 cls: 'modal-dialog',
15839 style : 'position:fixed', // we have to fix position....
15843 cls: 'modal-content',
15845 Roo.bootstrap.ComboBox.header,
15846 Roo.bootstrap.ComboBox.body,
15847 Roo.bootstrap.ComboBox.footer
15856 * Ext JS Library 1.1.1
15857 * Copyright(c) 2006-2007, Ext JS, LLC.
15859 * Originally Released Under LGPL - original licence link has changed is not relivant.
15862 * <script type="text/javascript">
15867 * @extends Roo.util.Observable
15868 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15869 * This class also supports single and multi selection modes. <br>
15870 * Create a data model bound view:
15872 var store = new Roo.data.Store(...);
15874 var view = new Roo.View({
15876 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15878 singleSelect: true,
15879 selectedClass: "ydataview-selected",
15883 // listen for node click?
15884 view.on("click", function(vw, index, node, e){
15885 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15889 dataModel.load("foobar.xml");
15891 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15893 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15894 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15896 * Note: old style constructor is still suported (container, template, config)
15899 * Create a new View
15900 * @param {Object} config The config object
15903 Roo.View = function(config, depreciated_tpl, depreciated_config){
15905 this.parent = false;
15907 if (typeof(depreciated_tpl) == 'undefined') {
15908 // new way.. - universal constructor.
15909 Roo.apply(this, config);
15910 this.el = Roo.get(this.el);
15913 this.el = Roo.get(config);
15914 this.tpl = depreciated_tpl;
15915 Roo.apply(this, depreciated_config);
15917 this.wrapEl = this.el.wrap().wrap();
15918 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15921 if(typeof(this.tpl) == "string"){
15922 this.tpl = new Roo.Template(this.tpl);
15924 // support xtype ctors..
15925 this.tpl = new Roo.factory(this.tpl, Roo);
15929 this.tpl.compile();
15934 * @event beforeclick
15935 * Fires before a click is processed. Returns false to cancel the default action.
15936 * @param {Roo.View} this
15937 * @param {Number} index The index of the target node
15938 * @param {HTMLElement} node The target node
15939 * @param {Roo.EventObject} e The raw event object
15941 "beforeclick" : true,
15944 * Fires when a template node is clicked.
15945 * @param {Roo.View} this
15946 * @param {Number} index The index of the target node
15947 * @param {HTMLElement} node The target node
15948 * @param {Roo.EventObject} e The raw event object
15953 * Fires when a template node is double clicked.
15954 * @param {Roo.View} this
15955 * @param {Number} index The index of the target node
15956 * @param {HTMLElement} node The target node
15957 * @param {Roo.EventObject} e The raw event object
15961 * @event contextmenu
15962 * Fires when a template node is right clicked.
15963 * @param {Roo.View} this
15964 * @param {Number} index The index of the target node
15965 * @param {HTMLElement} node The target node
15966 * @param {Roo.EventObject} e The raw event object
15968 "contextmenu" : true,
15970 * @event selectionchange
15971 * Fires when the selected nodes change.
15972 * @param {Roo.View} this
15973 * @param {Array} selections Array of the selected nodes
15975 "selectionchange" : true,
15978 * @event beforeselect
15979 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15980 * @param {Roo.View} this
15981 * @param {HTMLElement} node The node to be selected
15982 * @param {Array} selections Array of currently selected nodes
15984 "beforeselect" : true,
15986 * @event preparedata
15987 * Fires on every row to render, to allow you to change the data.
15988 * @param {Roo.View} this
15989 * @param {Object} data to be rendered (change this)
15991 "preparedata" : true
15999 "click": this.onClick,
16000 "dblclick": this.onDblClick,
16001 "contextmenu": this.onContextMenu,
16005 this.selections = [];
16007 this.cmp = new Roo.CompositeElementLite([]);
16009 this.store = Roo.factory(this.store, Roo.data);
16010 this.setStore(this.store, true);
16013 if ( this.footer && this.footer.xtype) {
16015 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16017 this.footer.dataSource = this.store;
16018 this.footer.container = fctr;
16019 this.footer = Roo.factory(this.footer, Roo);
16020 fctr.insertFirst(this.el);
16022 // this is a bit insane - as the paging toolbar seems to detach the el..
16023 // dom.parentNode.parentNode.parentNode
16024 // they get detached?
16028 Roo.View.superclass.constructor.call(this);
16033 Roo.extend(Roo.View, Roo.util.Observable, {
16036 * @cfg {Roo.data.Store} store Data store to load data from.
16041 * @cfg {String|Roo.Element} el The container element.
16046 * @cfg {String|Roo.Template} tpl The template used by this View
16050 * @cfg {String} dataName the named area of the template to use as the data area
16051 * Works with domtemplates roo-name="name"
16055 * @cfg {String} selectedClass The css class to add to selected nodes
16057 selectedClass : "x-view-selected",
16059 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16064 * @cfg {String} text to display on mask (default Loading)
16068 * @cfg {Boolean} multiSelect Allow multiple selection
16070 multiSelect : false,
16072 * @cfg {Boolean} singleSelect Allow single selection
16074 singleSelect: false,
16077 * @cfg {Boolean} toggleSelect - selecting
16079 toggleSelect : false,
16082 * @cfg {Boolean} tickable - selecting
16087 * Returns the element this view is bound to.
16088 * @return {Roo.Element}
16090 getEl : function(){
16091 return this.wrapEl;
16097 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16099 refresh : function(){
16100 //Roo.log('refresh');
16103 // if we are using something like 'domtemplate', then
16104 // the what gets used is:
16105 // t.applySubtemplate(NAME, data, wrapping data..)
16106 // the outer template then get' applied with
16107 // the store 'extra data'
16108 // and the body get's added to the
16109 // roo-name="data" node?
16110 // <span class='roo-tpl-{name}'></span> ?????
16114 this.clearSelections();
16115 this.el.update("");
16117 var records = this.store.getRange();
16118 if(records.length < 1) {
16120 // is this valid?? = should it render a template??
16122 this.el.update(this.emptyText);
16126 if (this.dataName) {
16127 this.el.update(t.apply(this.store.meta)); //????
16128 el = this.el.child('.roo-tpl-' + this.dataName);
16131 for(var i = 0, len = records.length; i < len; i++){
16132 var data = this.prepareData(records[i].data, i, records[i]);
16133 this.fireEvent("preparedata", this, data, i, records[i]);
16135 var d = Roo.apply({}, data);
16138 Roo.apply(d, {'roo-id' : Roo.id()});
16142 Roo.each(this.parent.item, function(item){
16143 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16146 Roo.apply(d, {'roo-data-checked' : 'checked'});
16150 html[html.length] = Roo.util.Format.trim(
16152 t.applySubtemplate(this.dataName, d, this.store.meta) :
16159 el.update(html.join(""));
16160 this.nodes = el.dom.childNodes;
16161 this.updateIndexes(0);
16166 * Function to override to reformat the data that is sent to
16167 * the template for each node.
16168 * DEPRICATED - use the preparedata event handler.
16169 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16170 * a JSON object for an UpdateManager bound view).
16172 prepareData : function(data, index, record)
16174 this.fireEvent("preparedata", this, data, index, record);
16178 onUpdate : function(ds, record){
16179 // Roo.log('on update');
16180 this.clearSelections();
16181 var index = this.store.indexOf(record);
16182 var n = this.nodes[index];
16183 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16184 n.parentNode.removeChild(n);
16185 this.updateIndexes(index, index);
16191 onAdd : function(ds, records, index)
16193 //Roo.log(['on Add', ds, records, index] );
16194 this.clearSelections();
16195 if(this.nodes.length == 0){
16199 var n = this.nodes[index];
16200 for(var i = 0, len = records.length; i < len; i++){
16201 var d = this.prepareData(records[i].data, i, records[i]);
16203 this.tpl.insertBefore(n, d);
16206 this.tpl.append(this.el, d);
16209 this.updateIndexes(index);
16212 onRemove : function(ds, record, index){
16213 // Roo.log('onRemove');
16214 this.clearSelections();
16215 var el = this.dataName ?
16216 this.el.child('.roo-tpl-' + this.dataName) :
16219 el.dom.removeChild(this.nodes[index]);
16220 this.updateIndexes(index);
16224 * Refresh an individual node.
16225 * @param {Number} index
16227 refreshNode : function(index){
16228 this.onUpdate(this.store, this.store.getAt(index));
16231 updateIndexes : function(startIndex, endIndex){
16232 var ns = this.nodes;
16233 startIndex = startIndex || 0;
16234 endIndex = endIndex || ns.length - 1;
16235 for(var i = startIndex; i <= endIndex; i++){
16236 ns[i].nodeIndex = i;
16241 * Changes the data store this view uses and refresh the view.
16242 * @param {Store} store
16244 setStore : function(store, initial){
16245 if(!initial && this.store){
16246 this.store.un("datachanged", this.refresh);
16247 this.store.un("add", this.onAdd);
16248 this.store.un("remove", this.onRemove);
16249 this.store.un("update", this.onUpdate);
16250 this.store.un("clear", this.refresh);
16251 this.store.un("beforeload", this.onBeforeLoad);
16252 this.store.un("load", this.onLoad);
16253 this.store.un("loadexception", this.onLoad);
16257 store.on("datachanged", this.refresh, this);
16258 store.on("add", this.onAdd, this);
16259 store.on("remove", this.onRemove, this);
16260 store.on("update", this.onUpdate, this);
16261 store.on("clear", this.refresh, this);
16262 store.on("beforeload", this.onBeforeLoad, this);
16263 store.on("load", this.onLoad, this);
16264 store.on("loadexception", this.onLoad, this);
16272 * onbeforeLoad - masks the loading area.
16275 onBeforeLoad : function(store,opts)
16277 //Roo.log('onBeforeLoad');
16279 this.el.update("");
16281 this.el.mask(this.mask ? this.mask : "Loading" );
16283 onLoad : function ()
16290 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16291 * @param {HTMLElement} node
16292 * @return {HTMLElement} The template node
16294 findItemFromChild : function(node){
16295 var el = this.dataName ?
16296 this.el.child('.roo-tpl-' + this.dataName,true) :
16299 if(!node || node.parentNode == el){
16302 var p = node.parentNode;
16303 while(p && p != el){
16304 if(p.parentNode == el){
16313 onClick : function(e){
16314 var item = this.findItemFromChild(e.getTarget());
16316 var index = this.indexOf(item);
16317 if(this.onItemClick(item, index, e) !== false){
16318 this.fireEvent("click", this, index, item, e);
16321 this.clearSelections();
16326 onContextMenu : function(e){
16327 var item = this.findItemFromChild(e.getTarget());
16329 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16334 onDblClick : function(e){
16335 var item = this.findItemFromChild(e.getTarget());
16337 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16341 onItemClick : function(item, index, e)
16343 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16346 if (this.toggleSelect) {
16347 var m = this.isSelected(item) ? 'unselect' : 'select';
16350 _t[m](item, true, false);
16353 if(this.multiSelect || this.singleSelect){
16354 if(this.multiSelect && e.shiftKey && this.lastSelection){
16355 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16357 this.select(item, this.multiSelect && e.ctrlKey);
16358 this.lastSelection = item;
16361 if(!this.tickable){
16362 e.preventDefault();
16370 * Get the number of selected nodes.
16373 getSelectionCount : function(){
16374 return this.selections.length;
16378 * Get the currently selected nodes.
16379 * @return {Array} An array of HTMLElements
16381 getSelectedNodes : function(){
16382 return this.selections;
16386 * Get the indexes of the selected nodes.
16389 getSelectedIndexes : function(){
16390 var indexes = [], s = this.selections;
16391 for(var i = 0, len = s.length; i < len; i++){
16392 indexes.push(s[i].nodeIndex);
16398 * Clear all selections
16399 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16401 clearSelections : function(suppressEvent){
16402 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16403 this.cmp.elements = this.selections;
16404 this.cmp.removeClass(this.selectedClass);
16405 this.selections = [];
16406 if(!suppressEvent){
16407 this.fireEvent("selectionchange", this, this.selections);
16413 * Returns true if the passed node is selected
16414 * @param {HTMLElement/Number} node The node or node index
16415 * @return {Boolean}
16417 isSelected : function(node){
16418 var s = this.selections;
16422 node = this.getNode(node);
16423 return s.indexOf(node) !== -1;
16428 * @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
16429 * @param {Boolean} keepExisting (optional) true to keep existing selections
16430 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16432 select : function(nodeInfo, keepExisting, suppressEvent){
16433 if(nodeInfo instanceof Array){
16435 this.clearSelections(true);
16437 for(var i = 0, len = nodeInfo.length; i < len; i++){
16438 this.select(nodeInfo[i], true, true);
16442 var node = this.getNode(nodeInfo);
16443 if(!node || this.isSelected(node)){
16444 return; // already selected.
16447 this.clearSelections(true);
16450 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16451 Roo.fly(node).addClass(this.selectedClass);
16452 this.selections.push(node);
16453 if(!suppressEvent){
16454 this.fireEvent("selectionchange", this, this.selections);
16462 * @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
16463 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16464 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16466 unselect : function(nodeInfo, keepExisting, suppressEvent)
16468 if(nodeInfo instanceof Array){
16469 Roo.each(this.selections, function(s) {
16470 this.unselect(s, nodeInfo);
16474 var node = this.getNode(nodeInfo);
16475 if(!node || !this.isSelected(node)){
16476 //Roo.log("not selected");
16477 return; // not selected.
16481 Roo.each(this.selections, function(s) {
16483 Roo.fly(node).removeClass(this.selectedClass);
16490 this.selections= ns;
16491 this.fireEvent("selectionchange", this, this.selections);
16495 * Gets a template node.
16496 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16497 * @return {HTMLElement} The node or null if it wasn't found
16499 getNode : function(nodeInfo){
16500 if(typeof nodeInfo == "string"){
16501 return document.getElementById(nodeInfo);
16502 }else if(typeof nodeInfo == "number"){
16503 return this.nodes[nodeInfo];
16509 * Gets a range template nodes.
16510 * @param {Number} startIndex
16511 * @param {Number} endIndex
16512 * @return {Array} An array of nodes
16514 getNodes : function(start, end){
16515 var ns = this.nodes;
16516 start = start || 0;
16517 end = typeof end == "undefined" ? ns.length - 1 : end;
16520 for(var i = start; i <= end; i++){
16524 for(var i = start; i >= end; i--){
16532 * Finds the index of the passed node
16533 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16534 * @return {Number} The index of the node or -1
16536 indexOf : function(node){
16537 node = this.getNode(node);
16538 if(typeof node.nodeIndex == "number"){
16539 return node.nodeIndex;
16541 var ns = this.nodes;
16542 for(var i = 0, len = ns.length; i < len; i++){
16553 * based on jquery fullcalendar
16557 Roo.bootstrap = Roo.bootstrap || {};
16559 * @class Roo.bootstrap.Calendar
16560 * @extends Roo.bootstrap.Component
16561 * Bootstrap Calendar class
16562 * @cfg {Boolean} loadMask (true|false) default false
16563 * @cfg {Object} header generate the user specific header of the calendar, default false
16566 * Create a new Container
16567 * @param {Object} config The config object
16572 Roo.bootstrap.Calendar = function(config){
16573 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16577 * Fires when a date is selected
16578 * @param {DatePicker} this
16579 * @param {Date} date The selected date
16583 * @event monthchange
16584 * Fires when the displayed month changes
16585 * @param {DatePicker} this
16586 * @param {Date} date The selected month
16588 'monthchange': true,
16590 * @event evententer
16591 * Fires when mouse over an event
16592 * @param {Calendar} this
16593 * @param {event} Event
16595 'evententer': true,
16597 * @event eventleave
16598 * Fires when the mouse leaves an
16599 * @param {Calendar} this
16602 'eventleave': true,
16604 * @event eventclick
16605 * Fires when the mouse click an
16606 * @param {Calendar} this
16615 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16618 * @cfg {Number} startDay
16619 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16627 getAutoCreate : function(){
16630 var fc_button = function(name, corner, style, content ) {
16631 return Roo.apply({},{
16633 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16635 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16638 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16649 style : 'width:100%',
16656 cls : 'fc-header-left',
16658 fc_button('prev', 'left', 'arrow', '‹' ),
16659 fc_button('next', 'right', 'arrow', '›' ),
16660 { tag: 'span', cls: 'fc-header-space' },
16661 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16669 cls : 'fc-header-center',
16673 cls: 'fc-header-title',
16676 html : 'month / year'
16684 cls : 'fc-header-right',
16686 /* fc_button('month', 'left', '', 'month' ),
16687 fc_button('week', '', '', 'week' ),
16688 fc_button('day', 'right', '', 'day' )
16700 header = this.header;
16703 var cal_heads = function() {
16705 // fixme - handle this.
16707 for (var i =0; i < Date.dayNames.length; i++) {
16708 var d = Date.dayNames[i];
16711 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16712 html : d.substring(0,3)
16716 ret[0].cls += ' fc-first';
16717 ret[6].cls += ' fc-last';
16720 var cal_cell = function(n) {
16723 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16728 cls: 'fc-day-number',
16732 cls: 'fc-day-content',
16736 style: 'position: relative;' // height: 17px;
16748 var cal_rows = function() {
16751 for (var r = 0; r < 6; r++) {
16758 for (var i =0; i < Date.dayNames.length; i++) {
16759 var d = Date.dayNames[i];
16760 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16763 row.cn[0].cls+=' fc-first';
16764 row.cn[0].cn[0].style = 'min-height:90px';
16765 row.cn[6].cls+=' fc-last';
16769 ret[0].cls += ' fc-first';
16770 ret[4].cls += ' fc-prev-last';
16771 ret[5].cls += ' fc-last';
16778 cls: 'fc-border-separate',
16779 style : 'width:100%',
16787 cls : 'fc-first fc-last',
16805 cls : 'fc-content',
16806 style : "position: relative;",
16809 cls : 'fc-view fc-view-month fc-grid',
16810 style : 'position: relative',
16811 unselectable : 'on',
16814 cls : 'fc-event-container',
16815 style : 'position:absolute;z-index:8;top:0;left:0;'
16833 initEvents : function()
16836 throw "can not find store for calendar";
16842 style: "text-align:center",
16846 style: "background-color:white;width:50%;margin:250 auto",
16850 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16861 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16863 var size = this.el.select('.fc-content', true).first().getSize();
16864 this.maskEl.setSize(size.width, size.height);
16865 this.maskEl.enableDisplayMode("block");
16866 if(!this.loadMask){
16867 this.maskEl.hide();
16870 this.store = Roo.factory(this.store, Roo.data);
16871 this.store.on('load', this.onLoad, this);
16872 this.store.on('beforeload', this.onBeforeLoad, this);
16876 this.cells = this.el.select('.fc-day',true);
16877 //Roo.log(this.cells);
16878 this.textNodes = this.el.query('.fc-day-number');
16879 this.cells.addClassOnOver('fc-state-hover');
16881 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16882 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16883 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16884 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16886 this.on('monthchange', this.onMonthChange, this);
16888 this.update(new Date().clearTime());
16891 resize : function() {
16892 var sz = this.el.getSize();
16894 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16895 this.el.select('.fc-day-content div',true).setHeight(34);
16900 showPrevMonth : function(e){
16901 this.update(this.activeDate.add("mo", -1));
16903 showToday : function(e){
16904 this.update(new Date().clearTime());
16907 showNextMonth : function(e){
16908 this.update(this.activeDate.add("mo", 1));
16912 showPrevYear : function(){
16913 this.update(this.activeDate.add("y", -1));
16917 showNextYear : function(){
16918 this.update(this.activeDate.add("y", 1));
16923 update : function(date)
16925 var vd = this.activeDate;
16926 this.activeDate = date;
16927 // if(vd && this.el){
16928 // var t = date.getTime();
16929 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16930 // Roo.log('using add remove');
16932 // this.fireEvent('monthchange', this, date);
16934 // this.cells.removeClass("fc-state-highlight");
16935 // this.cells.each(function(c){
16936 // if(c.dateValue == t){
16937 // c.addClass("fc-state-highlight");
16938 // setTimeout(function(){
16939 // try{c.dom.firstChild.focus();}catch(e){}
16949 var days = date.getDaysInMonth();
16951 var firstOfMonth = date.getFirstDateOfMonth();
16952 var startingPos = firstOfMonth.getDay()-this.startDay;
16954 if(startingPos < this.startDay){
16958 var pm = date.add(Date.MONTH, -1);
16959 var prevStart = pm.getDaysInMonth()-startingPos;
16961 this.cells = this.el.select('.fc-day',true);
16962 this.textNodes = this.el.query('.fc-day-number');
16963 this.cells.addClassOnOver('fc-state-hover');
16965 var cells = this.cells.elements;
16966 var textEls = this.textNodes;
16968 Roo.each(cells, function(cell){
16969 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16972 days += startingPos;
16974 // convert everything to numbers so it's fast
16975 var day = 86400000;
16976 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16979 //Roo.log(prevStart);
16981 var today = new Date().clearTime().getTime();
16982 var sel = date.clearTime().getTime();
16983 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16984 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16985 var ddMatch = this.disabledDatesRE;
16986 var ddText = this.disabledDatesText;
16987 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16988 var ddaysText = this.disabledDaysText;
16989 var format = this.format;
16991 var setCellClass = function(cal, cell){
16995 //Roo.log('set Cell Class');
16997 var t = d.getTime();
17001 cell.dateValue = t;
17003 cell.className += " fc-today";
17004 cell.className += " fc-state-highlight";
17005 cell.title = cal.todayText;
17008 // disable highlight in other month..
17009 //cell.className += " fc-state-highlight";
17014 cell.className = " fc-state-disabled";
17015 cell.title = cal.minText;
17019 cell.className = " fc-state-disabled";
17020 cell.title = cal.maxText;
17024 if(ddays.indexOf(d.getDay()) != -1){
17025 cell.title = ddaysText;
17026 cell.className = " fc-state-disabled";
17029 if(ddMatch && format){
17030 var fvalue = d.dateFormat(format);
17031 if(ddMatch.test(fvalue)){
17032 cell.title = ddText.replace("%0", fvalue);
17033 cell.className = " fc-state-disabled";
17037 if (!cell.initialClassName) {
17038 cell.initialClassName = cell.dom.className;
17041 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17046 for(; i < startingPos; i++) {
17047 textEls[i].innerHTML = (++prevStart);
17048 d.setDate(d.getDate()+1);
17050 cells[i].className = "fc-past fc-other-month";
17051 setCellClass(this, cells[i]);
17056 for(; i < days; i++){
17057 intDay = i - startingPos + 1;
17058 textEls[i].innerHTML = (intDay);
17059 d.setDate(d.getDate()+1);
17061 cells[i].className = ''; // "x-date-active";
17062 setCellClass(this, cells[i]);
17066 for(; i < 42; i++) {
17067 textEls[i].innerHTML = (++extraDays);
17068 d.setDate(d.getDate()+1);
17070 cells[i].className = "fc-future fc-other-month";
17071 setCellClass(this, cells[i]);
17074 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17076 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17078 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17079 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17081 if(totalRows != 6){
17082 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17083 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17086 this.fireEvent('monthchange', this, date);
17090 if(!this.internalRender){
17091 var main = this.el.dom.firstChild;
17092 var w = main.offsetWidth;
17093 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17094 Roo.fly(main).setWidth(w);
17095 this.internalRender = true;
17096 // opera does not respect the auto grow header center column
17097 // then, after it gets a width opera refuses to recalculate
17098 // without a second pass
17099 if(Roo.isOpera && !this.secondPass){
17100 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17101 this.secondPass = true;
17102 this.update.defer(10, this, [date]);
17109 findCell : function(dt) {
17110 dt = dt.clearTime().getTime();
17112 this.cells.each(function(c){
17113 //Roo.log("check " +c.dateValue + '?=' + dt);
17114 if(c.dateValue == dt){
17124 findCells : function(ev) {
17125 var s = ev.start.clone().clearTime().getTime();
17127 var e= ev.end.clone().clearTime().getTime();
17130 this.cells.each(function(c){
17131 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17133 if(c.dateValue > e){
17136 if(c.dateValue < s){
17145 // findBestRow: function(cells)
17149 // for (var i =0 ; i < cells.length;i++) {
17150 // ret = Math.max(cells[i].rows || 0,ret);
17157 addItem : function(ev)
17159 // look for vertical location slot in
17160 var cells = this.findCells(ev);
17162 // ev.row = this.findBestRow(cells);
17164 // work out the location.
17168 for(var i =0; i < cells.length; i++) {
17170 cells[i].row = cells[0].row;
17173 cells[i].row = cells[i].row + 1;
17183 if (crow.start.getY() == cells[i].getY()) {
17185 crow.end = cells[i];
17202 cells[0].events.push(ev);
17204 this.calevents.push(ev);
17207 clearEvents: function() {
17209 if(!this.calevents){
17213 Roo.each(this.cells.elements, function(c){
17219 Roo.each(this.calevents, function(e) {
17220 Roo.each(e.els, function(el) {
17221 el.un('mouseenter' ,this.onEventEnter, this);
17222 el.un('mouseleave' ,this.onEventLeave, this);
17227 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17233 renderEvents: function()
17237 this.cells.each(function(c) {
17246 if(c.row != c.events.length){
17247 r = 4 - (4 - (c.row - c.events.length));
17250 c.events = ev.slice(0, r);
17251 c.more = ev.slice(r);
17253 if(c.more.length && c.more.length == 1){
17254 c.events.push(c.more.pop());
17257 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17261 this.cells.each(function(c) {
17263 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17266 for (var e = 0; e < c.events.length; e++){
17267 var ev = c.events[e];
17268 var rows = ev.rows;
17270 for(var i = 0; i < rows.length; i++) {
17272 // how many rows should it span..
17275 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17276 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17278 unselectable : "on",
17281 cls: 'fc-event-inner',
17285 // cls: 'fc-event-time',
17286 // html : cells.length > 1 ? '' : ev.time
17290 cls: 'fc-event-title',
17291 html : String.format('{0}', ev.title)
17298 cls: 'ui-resizable-handle ui-resizable-e',
17299 html : '  '
17306 cfg.cls += ' fc-event-start';
17308 if ((i+1) == rows.length) {
17309 cfg.cls += ' fc-event-end';
17312 var ctr = _this.el.select('.fc-event-container',true).first();
17313 var cg = ctr.createChild(cfg);
17315 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17316 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17318 var r = (c.more.length) ? 1 : 0;
17319 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17320 cg.setWidth(ebox.right - sbox.x -2);
17322 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17323 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17324 cg.on('click', _this.onEventClick, _this, ev);
17335 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17336 style : 'position: absolute',
17337 unselectable : "on",
17340 cls: 'fc-event-inner',
17344 cls: 'fc-event-title',
17352 cls: 'ui-resizable-handle ui-resizable-e',
17353 html : '  '
17359 var ctr = _this.el.select('.fc-event-container',true).first();
17360 var cg = ctr.createChild(cfg);
17362 var sbox = c.select('.fc-day-content',true).first().getBox();
17363 var ebox = c.select('.fc-day-content',true).first().getBox();
17365 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17366 cg.setWidth(ebox.right - sbox.x -2);
17368 cg.on('click', _this.onMoreEventClick, _this, c.more);
17378 onEventEnter: function (e, el,event,d) {
17379 this.fireEvent('evententer', this, el, event);
17382 onEventLeave: function (e, el,event,d) {
17383 this.fireEvent('eventleave', this, el, event);
17386 onEventClick: function (e, el,event,d) {
17387 this.fireEvent('eventclick', this, el, event);
17390 onMonthChange: function () {
17394 onMoreEventClick: function(e, el, more)
17398 this.calpopover.placement = 'right';
17399 this.calpopover.setTitle('More');
17401 this.calpopover.setContent('');
17403 var ctr = this.calpopover.el.select('.popover-content', true).first();
17405 Roo.each(more, function(m){
17407 cls : 'fc-event-hori fc-event-draggable',
17410 var cg = ctr.createChild(cfg);
17412 cg.on('click', _this.onEventClick, _this, m);
17415 this.calpopover.show(el);
17420 onLoad: function ()
17422 this.calevents = [];
17425 if(this.store.getCount() > 0){
17426 this.store.data.each(function(d){
17429 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17430 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17431 time : d.data.start_time,
17432 title : d.data.title,
17433 description : d.data.description,
17434 venue : d.data.venue
17439 this.renderEvents();
17441 if(this.calevents.length && this.loadMask){
17442 this.maskEl.hide();
17446 onBeforeLoad: function()
17448 this.clearEvents();
17450 this.maskEl.show();
17464 * @class Roo.bootstrap.Popover
17465 * @extends Roo.bootstrap.Component
17466 * Bootstrap Popover class
17467 * @cfg {String} html contents of the popover (or false to use children..)
17468 * @cfg {String} title of popover (or false to hide)
17469 * @cfg {String} placement how it is placed
17470 * @cfg {String} trigger click || hover (or false to trigger manually)
17471 * @cfg {String} over what (parent or false to trigger manually.)
17472 * @cfg {Number} delay - delay before showing
17475 * Create a new Popover
17476 * @param {Object} config The config object
17479 Roo.bootstrap.Popover = function(config){
17480 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17486 * After the popover show
17488 * @param {Roo.bootstrap.Popover} this
17493 * After the popover hide
17495 * @param {Roo.bootstrap.Popover} this
17501 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17503 title: 'Fill in a title',
17506 placement : 'right',
17507 trigger : 'hover', // hover
17513 can_build_overlaid : false,
17515 getChildContainer : function()
17517 return this.el.select('.popover-content',true).first();
17520 getAutoCreate : function(){
17523 cls : 'popover roo-dynamic',
17524 style: 'display:block',
17530 cls : 'popover-inner',
17534 cls: 'popover-title',
17538 cls : 'popover-content',
17549 setTitle: function(str)
17552 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17554 setContent: function(str)
17557 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17559 // as it get's added to the bottom of the page.
17560 onRender : function(ct, position)
17562 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17564 var cfg = Roo.apply({}, this.getAutoCreate());
17568 cfg.cls += ' ' + this.cls;
17571 cfg.style = this.style;
17573 //Roo.log("adding to ");
17574 this.el = Roo.get(document.body).createChild(cfg, position);
17575 // Roo.log(this.el);
17580 initEvents : function()
17582 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17583 this.el.enableDisplayMode('block');
17585 if (this.over === false) {
17588 if (this.triggers === false) {
17591 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17592 var triggers = this.trigger ? this.trigger.split(' ') : [];
17593 Roo.each(triggers, function(trigger) {
17595 if (trigger == 'click') {
17596 on_el.on('click', this.toggle, this);
17597 } else if (trigger != 'manual') {
17598 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17599 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17601 on_el.on(eventIn ,this.enter, this);
17602 on_el.on(eventOut, this.leave, this);
17613 toggle : function () {
17614 this.hoverState == 'in' ? this.leave() : this.enter();
17617 enter : function () {
17619 clearTimeout(this.timeout);
17621 this.hoverState = 'in';
17623 if (!this.delay || !this.delay.show) {
17628 this.timeout = setTimeout(function () {
17629 if (_t.hoverState == 'in') {
17632 }, this.delay.show)
17635 leave : function() {
17636 clearTimeout(this.timeout);
17638 this.hoverState = 'out';
17640 if (!this.delay || !this.delay.hide) {
17645 this.timeout = setTimeout(function () {
17646 if (_t.hoverState == 'out') {
17649 }, this.delay.hide)
17652 show : function (on_el)
17655 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17659 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17660 if (this.html !== false) {
17661 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17663 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17664 if (!this.title.length) {
17665 this.el.select('.popover-title',true).hide();
17668 var placement = typeof this.placement == 'function' ?
17669 this.placement.call(this, this.el, on_el) :
17672 var autoToken = /\s?auto?\s?/i;
17673 var autoPlace = autoToken.test(placement);
17675 placement = placement.replace(autoToken, '') || 'top';
17679 //this.el.setXY([0,0]);
17681 this.el.dom.style.display='block';
17682 this.el.addClass(placement);
17684 //this.el.appendTo(on_el);
17686 var p = this.getPosition();
17687 var box = this.el.getBox();
17692 var align = Roo.bootstrap.Popover.alignment[placement];
17695 this.el.alignTo(on_el, align[0],align[1]);
17696 //var arrow = this.el.select('.arrow',true).first();
17697 //arrow.set(align[2],
17699 this.el.addClass('in');
17702 if (this.el.hasClass('fade')) {
17706 this.hoverState = 'in';
17708 this.fireEvent('show', this);
17713 this.el.setXY([0,0]);
17714 this.el.removeClass('in');
17716 this.hoverState = null;
17718 this.fireEvent('hide', this);
17723 Roo.bootstrap.Popover.alignment = {
17724 'left' : ['r-l', [-10,0], 'right'],
17725 'right' : ['l-r', [10,0], 'left'],
17726 'bottom' : ['t-b', [0,10], 'top'],
17727 'top' : [ 'b-t', [0,-10], 'bottom']
17738 * @class Roo.bootstrap.Progress
17739 * @extends Roo.bootstrap.Component
17740 * Bootstrap Progress class
17741 * @cfg {Boolean} striped striped of the progress bar
17742 * @cfg {Boolean} active animated of the progress bar
17746 * Create a new Progress
17747 * @param {Object} config The config object
17750 Roo.bootstrap.Progress = function(config){
17751 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17754 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17759 getAutoCreate : function(){
17767 cfg.cls += ' progress-striped';
17771 cfg.cls += ' active';
17790 * @class Roo.bootstrap.ProgressBar
17791 * @extends Roo.bootstrap.Component
17792 * Bootstrap ProgressBar class
17793 * @cfg {Number} aria_valuenow aria-value now
17794 * @cfg {Number} aria_valuemin aria-value min
17795 * @cfg {Number} aria_valuemax aria-value max
17796 * @cfg {String} label label for the progress bar
17797 * @cfg {String} panel (success | info | warning | danger )
17798 * @cfg {String} role role of the progress bar
17799 * @cfg {String} sr_only text
17803 * Create a new ProgressBar
17804 * @param {Object} config The config object
17807 Roo.bootstrap.ProgressBar = function(config){
17808 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17811 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17815 aria_valuemax : 100,
17821 getAutoCreate : function()
17826 cls: 'progress-bar',
17827 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17839 cfg.role = this.role;
17842 if(this.aria_valuenow){
17843 cfg['aria-valuenow'] = this.aria_valuenow;
17846 if(this.aria_valuemin){
17847 cfg['aria-valuemin'] = this.aria_valuemin;
17850 if(this.aria_valuemax){
17851 cfg['aria-valuemax'] = this.aria_valuemax;
17854 if(this.label && !this.sr_only){
17855 cfg.html = this.label;
17859 cfg.cls += ' progress-bar-' + this.panel;
17865 update : function(aria_valuenow)
17867 this.aria_valuenow = aria_valuenow;
17869 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17884 * @class Roo.bootstrap.TabGroup
17885 * @extends Roo.bootstrap.Column
17886 * Bootstrap Column class
17887 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17888 * @cfg {Boolean} carousel true to make the group behave like a carousel
17889 * @cfg {Boolean} bullets show bullets for the panels
17890 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17891 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17892 * @cfg {Boolean} showarrow (true|false) show arrow default true
17895 * Create a new TabGroup
17896 * @param {Object} config The config object
17899 Roo.bootstrap.TabGroup = function(config){
17900 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17902 this.navId = Roo.id();
17905 Roo.bootstrap.TabGroup.register(this);
17909 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17912 transition : false,
17917 slideOnTouch : false,
17920 getAutoCreate : function()
17922 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17924 cfg.cls += ' tab-content';
17926 if (this.carousel) {
17927 cfg.cls += ' carousel slide';
17930 cls : 'carousel-inner',
17934 if(this.bullets && !Roo.isTouch){
17937 cls : 'carousel-bullets',
17941 if(this.bullets_cls){
17942 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17949 cfg.cn[0].cn.push(bullets);
17952 if(this.showarrow){
17953 cfg.cn[0].cn.push({
17955 class : 'carousel-arrow',
17959 class : 'carousel-prev',
17963 class : 'fa fa-chevron-left'
17969 class : 'carousel-next',
17973 class : 'fa fa-chevron-right'
17986 initEvents: function()
17988 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17989 // this.el.on("touchstart", this.onTouchStart, this);
17992 if(this.autoslide){
17995 this.slideFn = window.setInterval(function() {
17996 _this.showPanelNext();
18000 if(this.showarrow){
18001 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18002 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18008 // onTouchStart : function(e, el, o)
18010 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18014 // this.showPanelNext();
18018 getChildContainer : function()
18020 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18024 * register a Navigation item
18025 * @param {Roo.bootstrap.NavItem} the navitem to add
18027 register : function(item)
18029 this.tabs.push( item);
18030 item.navId = this.navId; // not really needed..
18035 getActivePanel : function()
18038 Roo.each(this.tabs, function(t) {
18048 getPanelByName : function(n)
18051 Roo.each(this.tabs, function(t) {
18052 if (t.tabId == n) {
18060 indexOfPanel : function(p)
18063 Roo.each(this.tabs, function(t,i) {
18064 if (t.tabId == p.tabId) {
18073 * show a specific panel
18074 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18075 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18077 showPanel : function (pan)
18079 if(this.transition || typeof(pan) == 'undefined'){
18080 Roo.log("waiting for the transitionend");
18084 if (typeof(pan) == 'number') {
18085 pan = this.tabs[pan];
18088 if (typeof(pan) == 'string') {
18089 pan = this.getPanelByName(pan);
18092 var cur = this.getActivePanel();
18095 Roo.log('pan or acitve pan is undefined');
18099 if (pan.tabId == this.getActivePanel().tabId) {
18103 if (false === cur.fireEvent('beforedeactivate')) {
18107 if(this.bullets > 0 && !Roo.isTouch){
18108 this.setActiveBullet(this.indexOfPanel(pan));
18111 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18113 this.transition = true;
18114 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18115 var lr = dir == 'next' ? 'left' : 'right';
18116 pan.el.addClass(dir); // or prev
18117 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18118 cur.el.addClass(lr); // or right
18119 pan.el.addClass(lr);
18122 cur.el.on('transitionend', function() {
18123 Roo.log("trans end?");
18125 pan.el.removeClass([lr,dir]);
18126 pan.setActive(true);
18128 cur.el.removeClass([lr]);
18129 cur.setActive(false);
18131 _this.transition = false;
18133 }, this, { single: true } );
18138 cur.setActive(false);
18139 pan.setActive(true);
18144 showPanelNext : function()
18146 var i = this.indexOfPanel(this.getActivePanel());
18148 if (i >= this.tabs.length - 1 && !this.autoslide) {
18152 if (i >= this.tabs.length - 1 && this.autoslide) {
18156 this.showPanel(this.tabs[i+1]);
18159 showPanelPrev : function()
18161 var i = this.indexOfPanel(this.getActivePanel());
18163 if (i < 1 && !this.autoslide) {
18167 if (i < 1 && this.autoslide) {
18168 i = this.tabs.length;
18171 this.showPanel(this.tabs[i-1]);
18175 addBullet: function()
18177 if(!this.bullets || Roo.isTouch){
18180 var ctr = this.el.select('.carousel-bullets',true).first();
18181 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18182 var bullet = ctr.createChild({
18183 cls : 'bullet bullet-' + i
18184 },ctr.dom.lastChild);
18189 bullet.on('click', (function(e, el, o, ii, t){
18191 e.preventDefault();
18193 this.showPanel(ii);
18195 if(this.autoslide && this.slideFn){
18196 clearInterval(this.slideFn);
18197 this.slideFn = window.setInterval(function() {
18198 _this.showPanelNext();
18202 }).createDelegate(this, [i, bullet], true));
18207 setActiveBullet : function(i)
18213 Roo.each(this.el.select('.bullet', true).elements, function(el){
18214 el.removeClass('selected');
18217 var bullet = this.el.select('.bullet-' + i, true).first();
18223 bullet.addClass('selected');
18234 Roo.apply(Roo.bootstrap.TabGroup, {
18238 * register a Navigation Group
18239 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18241 register : function(navgrp)
18243 this.groups[navgrp.navId] = navgrp;
18247 * fetch a Navigation Group based on the navigation ID
18248 * if one does not exist , it will get created.
18249 * @param {string} the navgroup to add
18250 * @returns {Roo.bootstrap.NavGroup} the navgroup
18252 get: function(navId) {
18253 if (typeof(this.groups[navId]) == 'undefined') {
18254 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18256 return this.groups[navId] ;
18271 * @class Roo.bootstrap.TabPanel
18272 * @extends Roo.bootstrap.Component
18273 * Bootstrap TabPanel class
18274 * @cfg {Boolean} active panel active
18275 * @cfg {String} html panel content
18276 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18277 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18278 * @cfg {String} href click to link..
18282 * Create a new TabPanel
18283 * @param {Object} config The config object
18286 Roo.bootstrap.TabPanel = function(config){
18287 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18291 * Fires when the active status changes
18292 * @param {Roo.bootstrap.TabPanel} this
18293 * @param {Boolean} state the new state
18298 * @event beforedeactivate
18299 * Fires before a tab is de-activated - can be used to do validation on a form.
18300 * @param {Roo.bootstrap.TabPanel} this
18301 * @return {Boolean} false if there is an error
18304 'beforedeactivate': true
18307 this.tabId = this.tabId || Roo.id();
18311 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18319 getAutoCreate : function(){
18322 // item is needed for carousel - not sure if it has any effect otherwise
18323 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18324 html: this.html || ''
18328 cfg.cls += ' active';
18332 cfg.tabId = this.tabId;
18339 initEvents: function()
18341 var p = this.parent();
18343 this.navId = this.navId || p.navId;
18345 if (typeof(this.navId) != 'undefined') {
18346 // not really needed.. but just in case.. parent should be a NavGroup.
18347 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18351 var i = tg.tabs.length - 1;
18353 if(this.active && tg.bullets > 0 && i < tg.bullets){
18354 tg.setActiveBullet(i);
18358 this.el.on('click', this.onClick, this);
18361 this.el.on("touchstart", this.onTouchStart, this);
18362 this.el.on("touchmove", this.onTouchMove, this);
18363 this.el.on("touchend", this.onTouchEnd, this);
18368 onRender : function(ct, position)
18370 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18373 setActive : function(state)
18375 Roo.log("panel - set active " + this.tabId + "=" + state);
18377 this.active = state;
18379 this.el.removeClass('active');
18381 } else if (!this.el.hasClass('active')) {
18382 this.el.addClass('active');
18385 this.fireEvent('changed', this, state);
18388 onClick : function(e)
18390 e.preventDefault();
18392 if(!this.href.length){
18396 window.location.href = this.href;
18405 onTouchStart : function(e)
18407 this.swiping = false;
18409 this.startX = e.browserEvent.touches[0].clientX;
18410 this.startY = e.browserEvent.touches[0].clientY;
18413 onTouchMove : function(e)
18415 this.swiping = true;
18417 this.endX = e.browserEvent.touches[0].clientX;
18418 this.endY = e.browserEvent.touches[0].clientY;
18421 onTouchEnd : function(e)
18428 var tabGroup = this.parent();
18430 if(this.endX > this.startX){ // swiping right
18431 tabGroup.showPanelPrev();
18435 if(this.startX > this.endX){ // swiping left
18436 tabGroup.showPanelNext();
18455 * @class Roo.bootstrap.DateField
18456 * @extends Roo.bootstrap.Input
18457 * Bootstrap DateField class
18458 * @cfg {Number} weekStart default 0
18459 * @cfg {String} viewMode default empty, (months|years)
18460 * @cfg {String} minViewMode default empty, (months|years)
18461 * @cfg {Number} startDate default -Infinity
18462 * @cfg {Number} endDate default Infinity
18463 * @cfg {Boolean} todayHighlight default false
18464 * @cfg {Boolean} todayBtn default false
18465 * @cfg {Boolean} calendarWeeks default false
18466 * @cfg {Object} daysOfWeekDisabled default empty
18467 * @cfg {Boolean} singleMode default false (true | false)
18469 * @cfg {Boolean} keyboardNavigation default true
18470 * @cfg {String} language default en
18473 * Create a new DateField
18474 * @param {Object} config The config object
18477 Roo.bootstrap.DateField = function(config){
18478 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18482 * Fires when this field show.
18483 * @param {Roo.bootstrap.DateField} this
18484 * @param {Mixed} date The date value
18489 * Fires when this field hide.
18490 * @param {Roo.bootstrap.DateField} this
18491 * @param {Mixed} date The date value
18496 * Fires when select a date.
18497 * @param {Roo.bootstrap.DateField} this
18498 * @param {Mixed} date The date value
18502 * @event beforeselect
18503 * Fires when before select a date.
18504 * @param {Roo.bootstrap.DateField} this
18505 * @param {Mixed} date The date value
18507 beforeselect : true
18511 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18514 * @cfg {String} format
18515 * The default date format string which can be overriden for localization support. The format must be
18516 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18520 * @cfg {String} altFormats
18521 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18522 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18524 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18532 todayHighlight : false,
18538 keyboardNavigation: true,
18540 calendarWeeks: false,
18542 startDate: -Infinity,
18546 daysOfWeekDisabled: [],
18550 singleMode : false,
18552 UTCDate: function()
18554 return new Date(Date.UTC.apply(Date, arguments));
18557 UTCToday: function()
18559 var today = new Date();
18560 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18563 getDate: function() {
18564 var d = this.getUTCDate();
18565 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18568 getUTCDate: function() {
18572 setDate: function(d) {
18573 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18576 setUTCDate: function(d) {
18578 this.setValue(this.formatDate(this.date));
18581 onRender: function(ct, position)
18584 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18586 this.language = this.language || 'en';
18587 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18588 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18590 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18591 this.format = this.format || 'm/d/y';
18592 this.isInline = false;
18593 this.isInput = true;
18594 this.component = this.el.select('.add-on', true).first() || false;
18595 this.component = (this.component && this.component.length === 0) ? false : this.component;
18596 this.hasInput = this.component && this.inputEl().length;
18598 if (typeof(this.minViewMode === 'string')) {
18599 switch (this.minViewMode) {
18601 this.minViewMode = 1;
18604 this.minViewMode = 2;
18607 this.minViewMode = 0;
18612 if (typeof(this.viewMode === 'string')) {
18613 switch (this.viewMode) {
18626 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18628 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18630 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18632 this.picker().on('mousedown', this.onMousedown, this);
18633 this.picker().on('click', this.onClick, this);
18635 this.picker().addClass('datepicker-dropdown');
18637 this.startViewMode = this.viewMode;
18639 if(this.singleMode){
18640 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18641 v.setVisibilityMode(Roo.Element.DISPLAY);
18645 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18646 v.setStyle('width', '189px');
18650 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18651 if(!this.calendarWeeks){
18656 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18657 v.attr('colspan', function(i, val){
18658 return parseInt(val) + 1;
18663 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18665 this.setStartDate(this.startDate);
18666 this.setEndDate(this.endDate);
18668 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18675 if(this.isInline) {
18680 picker : function()
18682 return this.pickerEl;
18683 // return this.el.select('.datepicker', true).first();
18686 fillDow: function()
18688 var dowCnt = this.weekStart;
18697 if(this.calendarWeeks){
18705 while (dowCnt < this.weekStart + 7) {
18709 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18713 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18716 fillMonths: function()
18719 var months = this.picker().select('>.datepicker-months td', true).first();
18721 months.dom.innerHTML = '';
18727 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18730 months.createChild(month);
18737 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;
18739 if (this.date < this.startDate) {
18740 this.viewDate = new Date(this.startDate);
18741 } else if (this.date > this.endDate) {
18742 this.viewDate = new Date(this.endDate);
18744 this.viewDate = new Date(this.date);
18752 var d = new Date(this.viewDate),
18753 year = d.getUTCFullYear(),
18754 month = d.getUTCMonth(),
18755 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18756 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18757 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18758 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18759 currentDate = this.date && this.date.valueOf(),
18760 today = this.UTCToday();
18762 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18764 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18766 // this.picker.select('>tfoot th.today').
18767 // .text(dates[this.language].today)
18768 // .toggle(this.todayBtn !== false);
18770 this.updateNavArrows();
18773 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18775 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18777 prevMonth.setUTCDate(day);
18779 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18781 var nextMonth = new Date(prevMonth);
18783 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18785 nextMonth = nextMonth.valueOf();
18787 var fillMonths = false;
18789 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18791 while(prevMonth.valueOf() <= nextMonth) {
18794 if (prevMonth.getUTCDay() === this.weekStart) {
18796 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18804 if(this.calendarWeeks){
18805 // ISO 8601: First week contains first thursday.
18806 // ISO also states week starts on Monday, but we can be more abstract here.
18808 // Start of current week: based on weekstart/current date
18809 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18810 // Thursday of this week
18811 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18812 // First Thursday of year, year from thursday
18813 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18814 // Calendar week: ms between thursdays, div ms per day, div 7 days
18815 calWeek = (th - yth) / 864e5 / 7 + 1;
18817 fillMonths.cn.push({
18825 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18827 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18830 if (this.todayHighlight &&
18831 prevMonth.getUTCFullYear() == today.getFullYear() &&
18832 prevMonth.getUTCMonth() == today.getMonth() &&
18833 prevMonth.getUTCDate() == today.getDate()) {
18834 clsName += ' today';
18837 if (currentDate && prevMonth.valueOf() === currentDate) {
18838 clsName += ' active';
18841 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18842 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18843 clsName += ' disabled';
18846 fillMonths.cn.push({
18848 cls: 'day ' + clsName,
18849 html: prevMonth.getDate()
18852 prevMonth.setDate(prevMonth.getDate()+1);
18855 var currentYear = this.date && this.date.getUTCFullYear();
18856 var currentMonth = this.date && this.date.getUTCMonth();
18858 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18860 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18861 v.removeClass('active');
18863 if(currentYear === year && k === currentMonth){
18864 v.addClass('active');
18867 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18868 v.addClass('disabled');
18874 year = parseInt(year/10, 10) * 10;
18876 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18878 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18881 for (var i = -1; i < 11; i++) {
18882 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18884 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18892 showMode: function(dir)
18895 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18898 Roo.each(this.picker().select('>div',true).elements, function(v){
18899 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18902 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18907 if(this.isInline) {
18911 this.picker().removeClass(['bottom', 'top']);
18913 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18915 * place to the top of element!
18919 this.picker().addClass('top');
18920 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18925 this.picker().addClass('bottom');
18927 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18930 parseDate : function(value)
18932 if(!value || value instanceof Date){
18935 var v = Date.parseDate(value, this.format);
18936 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18937 v = Date.parseDate(value, 'Y-m-d');
18939 if(!v && this.altFormats){
18940 if(!this.altFormatsArray){
18941 this.altFormatsArray = this.altFormats.split("|");
18943 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18944 v = Date.parseDate(value, this.altFormatsArray[i]);
18950 formatDate : function(date, fmt)
18952 return (!date || !(date instanceof Date)) ?
18953 date : date.dateFormat(fmt || this.format);
18956 onFocus : function()
18958 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18962 onBlur : function()
18964 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18966 var d = this.inputEl().getValue();
18973 showPopup : function()
18975 this.picker().show();
18979 this.fireEvent('showpopup', this, this.date);
18982 hidePopup : function()
18984 if(this.isInline) {
18987 this.picker().hide();
18988 this.viewMode = this.startViewMode;
18991 this.fireEvent('hidepopup', this, this.date);
18995 onMousedown: function(e)
18997 e.stopPropagation();
18998 e.preventDefault();
19003 Roo.bootstrap.DateField.superclass.keyup.call(this);
19007 setValue: function(v)
19009 if(this.fireEvent('beforeselect', this, v) !== false){
19010 var d = new Date(this.parseDate(v) ).clearTime();
19012 if(isNaN(d.getTime())){
19013 this.date = this.viewDate = '';
19014 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19018 v = this.formatDate(d);
19020 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19022 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19026 this.fireEvent('select', this, this.date);
19030 getValue: function()
19032 return this.formatDate(this.date);
19035 fireKey: function(e)
19037 if (!this.picker().isVisible()){
19038 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19044 var dateChanged = false,
19046 newDate, newViewDate;
19051 e.preventDefault();
19055 if (!this.keyboardNavigation) {
19058 dir = e.keyCode == 37 ? -1 : 1;
19061 newDate = this.moveYear(this.date, dir);
19062 newViewDate = this.moveYear(this.viewDate, dir);
19063 } else if (e.shiftKey){
19064 newDate = this.moveMonth(this.date, dir);
19065 newViewDate = this.moveMonth(this.viewDate, dir);
19067 newDate = new Date(this.date);
19068 newDate.setUTCDate(this.date.getUTCDate() + dir);
19069 newViewDate = new Date(this.viewDate);
19070 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19072 if (this.dateWithinRange(newDate)){
19073 this.date = newDate;
19074 this.viewDate = newViewDate;
19075 this.setValue(this.formatDate(this.date));
19077 e.preventDefault();
19078 dateChanged = true;
19083 if (!this.keyboardNavigation) {
19086 dir = e.keyCode == 38 ? -1 : 1;
19088 newDate = this.moveYear(this.date, dir);
19089 newViewDate = this.moveYear(this.viewDate, dir);
19090 } else if (e.shiftKey){
19091 newDate = this.moveMonth(this.date, dir);
19092 newViewDate = this.moveMonth(this.viewDate, dir);
19094 newDate = new Date(this.date);
19095 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19096 newViewDate = new Date(this.viewDate);
19097 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19099 if (this.dateWithinRange(newDate)){
19100 this.date = newDate;
19101 this.viewDate = newViewDate;
19102 this.setValue(this.formatDate(this.date));
19104 e.preventDefault();
19105 dateChanged = true;
19109 this.setValue(this.formatDate(this.date));
19111 e.preventDefault();
19114 this.setValue(this.formatDate(this.date));
19128 onClick: function(e)
19130 e.stopPropagation();
19131 e.preventDefault();
19133 var target = e.getTarget();
19135 if(target.nodeName.toLowerCase() === 'i'){
19136 target = Roo.get(target).dom.parentNode;
19139 var nodeName = target.nodeName;
19140 var className = target.className;
19141 var html = target.innerHTML;
19142 //Roo.log(nodeName);
19144 switch(nodeName.toLowerCase()) {
19146 switch(className) {
19152 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19153 switch(this.viewMode){
19155 this.viewDate = this.moveMonth(this.viewDate, dir);
19159 this.viewDate = this.moveYear(this.viewDate, dir);
19165 var date = new Date();
19166 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19168 this.setValue(this.formatDate(this.date));
19175 if (className.indexOf('disabled') < 0) {
19176 this.viewDate.setUTCDate(1);
19177 if (className.indexOf('month') > -1) {
19178 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19180 var year = parseInt(html, 10) || 0;
19181 this.viewDate.setUTCFullYear(year);
19185 if(this.singleMode){
19186 this.setValue(this.formatDate(this.viewDate));
19197 //Roo.log(className);
19198 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19199 var day = parseInt(html, 10) || 1;
19200 var year = this.viewDate.getUTCFullYear(),
19201 month = this.viewDate.getUTCMonth();
19203 if (className.indexOf('old') > -1) {
19210 } else if (className.indexOf('new') > -1) {
19218 //Roo.log([year,month,day]);
19219 this.date = this.UTCDate(year, month, day,0,0,0,0);
19220 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19222 //Roo.log(this.formatDate(this.date));
19223 this.setValue(this.formatDate(this.date));
19230 setStartDate: function(startDate)
19232 this.startDate = startDate || -Infinity;
19233 if (this.startDate !== -Infinity) {
19234 this.startDate = this.parseDate(this.startDate);
19237 this.updateNavArrows();
19240 setEndDate: function(endDate)
19242 this.endDate = endDate || Infinity;
19243 if (this.endDate !== Infinity) {
19244 this.endDate = this.parseDate(this.endDate);
19247 this.updateNavArrows();
19250 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19252 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19253 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19254 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19256 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19257 return parseInt(d, 10);
19260 this.updateNavArrows();
19263 updateNavArrows: function()
19265 if(this.singleMode){
19269 var d = new Date(this.viewDate),
19270 year = d.getUTCFullYear(),
19271 month = d.getUTCMonth();
19273 Roo.each(this.picker().select('.prev', true).elements, function(v){
19275 switch (this.viewMode) {
19278 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19284 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19291 Roo.each(this.picker().select('.next', true).elements, function(v){
19293 switch (this.viewMode) {
19296 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19302 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19310 moveMonth: function(date, dir)
19315 var new_date = new Date(date.valueOf()),
19316 day = new_date.getUTCDate(),
19317 month = new_date.getUTCMonth(),
19318 mag = Math.abs(dir),
19320 dir = dir > 0 ? 1 : -1;
19323 // If going back one month, make sure month is not current month
19324 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19326 return new_date.getUTCMonth() == month;
19328 // If going forward one month, make sure month is as expected
19329 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19331 return new_date.getUTCMonth() != new_month;
19333 new_month = month + dir;
19334 new_date.setUTCMonth(new_month);
19335 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19336 if (new_month < 0 || new_month > 11) {
19337 new_month = (new_month + 12) % 12;
19340 // For magnitudes >1, move one month at a time...
19341 for (var i=0; i<mag; i++) {
19342 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19343 new_date = this.moveMonth(new_date, dir);
19345 // ...then reset the day, keeping it in the new month
19346 new_month = new_date.getUTCMonth();
19347 new_date.setUTCDate(day);
19349 return new_month != new_date.getUTCMonth();
19352 // Common date-resetting loop -- if date is beyond end of month, make it
19355 new_date.setUTCDate(--day);
19356 new_date.setUTCMonth(new_month);
19361 moveYear: function(date, dir)
19363 return this.moveMonth(date, dir*12);
19366 dateWithinRange: function(date)
19368 return date >= this.startDate && date <= this.endDate;
19374 this.picker().remove();
19377 validateValue : function(value)
19379 if(this.getVisibilityEl().hasClass('hidden')){
19383 if(value.length < 1) {
19384 if(this.allowBlank){
19390 if(value.length < this.minLength){
19393 if(value.length > this.maxLength){
19397 var vt = Roo.form.VTypes;
19398 if(!vt[this.vtype](value, this)){
19402 if(typeof this.validator == "function"){
19403 var msg = this.validator(value);
19409 if(this.regex && !this.regex.test(value)){
19413 if(typeof(this.parseDate(value)) == 'undefined'){
19417 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19421 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19431 this.date = this.viewDate = '';
19433 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19438 Roo.apply(Roo.bootstrap.DateField, {
19449 html: '<i class="fa fa-arrow-left"/>'
19459 html: '<i class="fa fa-arrow-right"/>'
19501 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19502 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19503 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19504 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19505 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19518 navFnc: 'FullYear',
19523 navFnc: 'FullYear',
19528 Roo.apply(Roo.bootstrap.DateField, {
19532 cls: 'datepicker dropdown-menu roo-dynamic',
19536 cls: 'datepicker-days',
19540 cls: 'table-condensed',
19542 Roo.bootstrap.DateField.head,
19546 Roo.bootstrap.DateField.footer
19553 cls: 'datepicker-months',
19557 cls: 'table-condensed',
19559 Roo.bootstrap.DateField.head,
19560 Roo.bootstrap.DateField.content,
19561 Roo.bootstrap.DateField.footer
19568 cls: 'datepicker-years',
19572 cls: 'table-condensed',
19574 Roo.bootstrap.DateField.head,
19575 Roo.bootstrap.DateField.content,
19576 Roo.bootstrap.DateField.footer
19595 * @class Roo.bootstrap.TimeField
19596 * @extends Roo.bootstrap.Input
19597 * Bootstrap DateField class
19601 * Create a new TimeField
19602 * @param {Object} config The config object
19605 Roo.bootstrap.TimeField = function(config){
19606 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19610 * Fires when this field show.
19611 * @param {Roo.bootstrap.DateField} thisthis
19612 * @param {Mixed} date The date value
19617 * Fires when this field hide.
19618 * @param {Roo.bootstrap.DateField} this
19619 * @param {Mixed} date The date value
19624 * Fires when select a date.
19625 * @param {Roo.bootstrap.DateField} this
19626 * @param {Mixed} date The date value
19632 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19635 * @cfg {String} format
19636 * The default time format string which can be overriden for localization support. The format must be
19637 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19641 onRender: function(ct, position)
19644 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19646 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19648 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19650 this.pop = this.picker().select('>.datepicker-time',true).first();
19651 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19653 this.picker().on('mousedown', this.onMousedown, this);
19654 this.picker().on('click', this.onClick, this);
19656 this.picker().addClass('datepicker-dropdown');
19661 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19662 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19663 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19664 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19665 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19666 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19670 fireKey: function(e){
19671 if (!this.picker().isVisible()){
19672 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19678 e.preventDefault();
19686 this.onTogglePeriod();
19689 this.onIncrementMinutes();
19692 this.onDecrementMinutes();
19701 onClick: function(e) {
19702 e.stopPropagation();
19703 e.preventDefault();
19706 picker : function()
19708 return this.el.select('.datepicker', true).first();
19711 fillTime: function()
19713 var time = this.pop.select('tbody', true).first();
19715 time.dom.innerHTML = '';
19730 cls: 'hours-up glyphicon glyphicon-chevron-up'
19750 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19771 cls: 'timepicker-hour',
19786 cls: 'timepicker-minute',
19801 cls: 'btn btn-primary period',
19823 cls: 'hours-down glyphicon glyphicon-chevron-down'
19843 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19861 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19868 var hours = this.time.getHours();
19869 var minutes = this.time.getMinutes();
19882 hours = hours - 12;
19886 hours = '0' + hours;
19890 minutes = '0' + minutes;
19893 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19894 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19895 this.pop.select('button', true).first().dom.innerHTML = period;
19901 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19903 var cls = ['bottom'];
19905 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19912 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19917 this.picker().addClass(cls.join('-'));
19921 Roo.each(cls, function(c){
19923 _this.picker().setTop(_this.inputEl().getHeight());
19927 _this.picker().setTop(0 - _this.picker().getHeight());
19932 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19936 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19943 onFocus : function()
19945 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19949 onBlur : function()
19951 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19957 this.picker().show();
19962 this.fireEvent('show', this, this.date);
19967 this.picker().hide();
19970 this.fireEvent('hide', this, this.date);
19973 setTime : function()
19976 this.setValue(this.time.format(this.format));
19978 this.fireEvent('select', this, this.date);
19983 onMousedown: function(e){
19984 e.stopPropagation();
19985 e.preventDefault();
19988 onIncrementHours: function()
19990 Roo.log('onIncrementHours');
19991 this.time = this.time.add(Date.HOUR, 1);
19996 onDecrementHours: function()
19998 Roo.log('onDecrementHours');
19999 this.time = this.time.add(Date.HOUR, -1);
20003 onIncrementMinutes: function()
20005 Roo.log('onIncrementMinutes');
20006 this.time = this.time.add(Date.MINUTE, 1);
20010 onDecrementMinutes: function()
20012 Roo.log('onDecrementMinutes');
20013 this.time = this.time.add(Date.MINUTE, -1);
20017 onTogglePeriod: function()
20019 Roo.log('onTogglePeriod');
20020 this.time = this.time.add(Date.HOUR, 12);
20027 Roo.apply(Roo.bootstrap.TimeField, {
20057 cls: 'btn btn-info ok',
20069 Roo.apply(Roo.bootstrap.TimeField, {
20073 cls: 'datepicker dropdown-menu',
20077 cls: 'datepicker-time',
20081 cls: 'table-condensed',
20083 Roo.bootstrap.TimeField.content,
20084 Roo.bootstrap.TimeField.footer
20103 * @class Roo.bootstrap.MonthField
20104 * @extends Roo.bootstrap.Input
20105 * Bootstrap MonthField class
20107 * @cfg {String} language default en
20110 * Create a new MonthField
20111 * @param {Object} config The config object
20114 Roo.bootstrap.MonthField = function(config){
20115 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20120 * Fires when this field show.
20121 * @param {Roo.bootstrap.MonthField} this
20122 * @param {Mixed} date The date value
20127 * Fires when this field hide.
20128 * @param {Roo.bootstrap.MonthField} this
20129 * @param {Mixed} date The date value
20134 * Fires when select a date.
20135 * @param {Roo.bootstrap.MonthField} this
20136 * @param {String} oldvalue The old value
20137 * @param {String} newvalue The new value
20143 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20145 onRender: function(ct, position)
20148 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20150 this.language = this.language || 'en';
20151 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20152 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20154 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20155 this.isInline = false;
20156 this.isInput = true;
20157 this.component = this.el.select('.add-on', true).first() || false;
20158 this.component = (this.component && this.component.length === 0) ? false : this.component;
20159 this.hasInput = this.component && this.inputEL().length;
20161 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20163 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20165 this.picker().on('mousedown', this.onMousedown, this);
20166 this.picker().on('click', this.onClick, this);
20168 this.picker().addClass('datepicker-dropdown');
20170 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20171 v.setStyle('width', '189px');
20178 if(this.isInline) {
20184 setValue: function(v, suppressEvent)
20186 var o = this.getValue();
20188 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20192 if(suppressEvent !== true){
20193 this.fireEvent('select', this, o, v);
20198 getValue: function()
20203 onClick: function(e)
20205 e.stopPropagation();
20206 e.preventDefault();
20208 var target = e.getTarget();
20210 if(target.nodeName.toLowerCase() === 'i'){
20211 target = Roo.get(target).dom.parentNode;
20214 var nodeName = target.nodeName;
20215 var className = target.className;
20216 var html = target.innerHTML;
20218 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20222 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20224 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20230 picker : function()
20232 return this.pickerEl;
20235 fillMonths: function()
20238 var months = this.picker().select('>.datepicker-months td', true).first();
20240 months.dom.innerHTML = '';
20246 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20249 months.createChild(month);
20258 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20259 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20262 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20263 e.removeClass('active');
20265 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20266 e.addClass('active');
20273 if(this.isInline) {
20277 this.picker().removeClass(['bottom', 'top']);
20279 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20281 * place to the top of element!
20285 this.picker().addClass('top');
20286 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20291 this.picker().addClass('bottom');
20293 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20296 onFocus : function()
20298 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20302 onBlur : function()
20304 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20306 var d = this.inputEl().getValue();
20315 this.picker().show();
20316 this.picker().select('>.datepicker-months', true).first().show();
20320 this.fireEvent('show', this, this.date);
20325 if(this.isInline) {
20328 this.picker().hide();
20329 this.fireEvent('hide', this, this.date);
20333 onMousedown: function(e)
20335 e.stopPropagation();
20336 e.preventDefault();
20341 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20345 fireKey: function(e)
20347 if (!this.picker().isVisible()){
20348 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20359 e.preventDefault();
20363 dir = e.keyCode == 37 ? -1 : 1;
20365 this.vIndex = this.vIndex + dir;
20367 if(this.vIndex < 0){
20371 if(this.vIndex > 11){
20375 if(isNaN(this.vIndex)){
20379 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20385 dir = e.keyCode == 38 ? -1 : 1;
20387 this.vIndex = this.vIndex + dir * 4;
20389 if(this.vIndex < 0){
20393 if(this.vIndex > 11){
20397 if(isNaN(this.vIndex)){
20401 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20406 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20407 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20411 e.preventDefault();
20414 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20415 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20431 this.picker().remove();
20436 Roo.apply(Roo.bootstrap.MonthField, {
20455 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20456 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20461 Roo.apply(Roo.bootstrap.MonthField, {
20465 cls: 'datepicker dropdown-menu roo-dynamic',
20469 cls: 'datepicker-months',
20473 cls: 'table-condensed',
20475 Roo.bootstrap.DateField.content
20495 * @class Roo.bootstrap.CheckBox
20496 * @extends Roo.bootstrap.Input
20497 * Bootstrap CheckBox class
20499 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20500 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20501 * @cfg {String} boxLabel The text that appears beside the checkbox
20502 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20503 * @cfg {Boolean} checked initnal the element
20504 * @cfg {Boolean} inline inline the element (default false)
20505 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20506 * @cfg {String} tooltip label tooltip
20509 * Create a new CheckBox
20510 * @param {Object} config The config object
20513 Roo.bootstrap.CheckBox = function(config){
20514 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20519 * Fires when the element is checked or unchecked.
20520 * @param {Roo.bootstrap.CheckBox} this This input
20521 * @param {Boolean} checked The new checked value
20526 * Fires when the element is click.
20527 * @param {Roo.bootstrap.CheckBox} this This input
20534 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20536 inputType: 'checkbox',
20545 getAutoCreate : function()
20547 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20553 cfg.cls = 'form-group ' + this.inputType; //input-group
20556 cfg.cls += ' ' + this.inputType + '-inline';
20562 type : this.inputType,
20563 value : this.inputValue,
20564 cls : 'roo-' + this.inputType, //'form-box',
20565 placeholder : this.placeholder || ''
20569 if(this.inputType != 'radio'){
20573 cls : 'roo-hidden-value',
20574 value : this.checked ? this.inputValue : this.valueOff
20579 if (this.weight) { // Validity check?
20580 cfg.cls += " " + this.inputType + "-" + this.weight;
20583 if (this.disabled) {
20584 input.disabled=true;
20588 input.checked = this.checked;
20593 input.name = this.name;
20595 if(this.inputType != 'radio'){
20596 hidden.name = this.name;
20597 input.name = '_hidden_' + this.name;
20602 input.cls += ' input-' + this.size;
20607 ['xs','sm','md','lg'].map(function(size){
20608 if (settings[size]) {
20609 cfg.cls += ' col-' + size + '-' + settings[size];
20613 var inputblock = input;
20615 if (this.before || this.after) {
20618 cls : 'input-group',
20623 inputblock.cn.push({
20625 cls : 'input-group-addon',
20630 inputblock.cn.push(input);
20632 if(this.inputType != 'radio'){
20633 inputblock.cn.push(hidden);
20637 inputblock.cn.push({
20639 cls : 'input-group-addon',
20646 if (align ==='left' && this.fieldLabel.length) {
20647 // Roo.log("left and has label");
20652 cls : 'control-label',
20653 html : this.fieldLabel
20663 if(this.labelWidth > 12){
20664 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20667 if(this.labelWidth < 13 && this.labelmd == 0){
20668 this.labelmd = this.labelWidth;
20671 if(this.labellg > 0){
20672 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20673 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20676 if(this.labelmd > 0){
20677 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20678 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20681 if(this.labelsm > 0){
20682 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20683 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20686 if(this.labelxs > 0){
20687 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20688 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20691 } else if ( this.fieldLabel.length) {
20692 // Roo.log(" label");
20696 tag: this.boxLabel ? 'span' : 'label',
20698 cls: 'control-label box-input-label',
20699 //cls : 'input-group-addon',
20700 html : this.fieldLabel
20709 // Roo.log(" no label && no align");
20710 cfg.cn = [ inputblock ] ;
20716 var boxLabelCfg = {
20718 //'for': id, // box label is handled by onclick - so no for...
20720 html: this.boxLabel
20724 boxLabelCfg.tooltip = this.tooltip;
20727 cfg.cn.push(boxLabelCfg);
20730 if(this.inputType != 'radio'){
20731 cfg.cn.push(hidden);
20739 * return the real input element.
20741 inputEl: function ()
20743 return this.el.select('input.roo-' + this.inputType,true).first();
20745 hiddenEl: function ()
20747 return this.el.select('input.roo-hidden-value',true).first();
20750 labelEl: function()
20752 return this.el.select('label.control-label',true).first();
20754 /* depricated... */
20758 return this.labelEl();
20761 boxLabelEl: function()
20763 return this.el.select('label.box-label',true).first();
20766 initEvents : function()
20768 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20770 this.inputEl().on('click', this.onClick, this);
20772 if (this.boxLabel) {
20773 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20776 this.startValue = this.getValue();
20779 Roo.bootstrap.CheckBox.register(this);
20783 onClick : function(e)
20785 if(this.fireEvent('click', this, e) !== false){
20786 this.setChecked(!this.checked);
20791 setChecked : function(state,suppressEvent)
20793 this.startValue = this.getValue();
20795 if(this.inputType == 'radio'){
20797 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20798 e.dom.checked = false;
20801 this.inputEl().dom.checked = true;
20803 this.inputEl().dom.value = this.inputValue;
20805 if(suppressEvent !== true){
20806 this.fireEvent('check', this, true);
20814 this.checked = state;
20816 this.inputEl().dom.checked = state;
20819 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20821 if(suppressEvent !== true){
20822 this.fireEvent('check', this, state);
20828 getValue : function()
20830 if(this.inputType == 'radio'){
20831 return this.getGroupValue();
20834 return this.hiddenEl().dom.value;
20838 getGroupValue : function()
20840 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20844 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20847 setValue : function(v,suppressEvent)
20849 if(this.inputType == 'radio'){
20850 this.setGroupValue(v, suppressEvent);
20854 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20859 setGroupValue : function(v, suppressEvent)
20861 this.startValue = this.getValue();
20863 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20864 e.dom.checked = false;
20866 if(e.dom.value == v){
20867 e.dom.checked = true;
20871 if(suppressEvent !== true){
20872 this.fireEvent('check', this, true);
20880 validate : function()
20882 if(this.getVisibilityEl().hasClass('hidden')){
20888 (this.inputType == 'radio' && this.validateRadio()) ||
20889 (this.inputType == 'checkbox' && this.validateCheckbox())
20895 this.markInvalid();
20899 validateRadio : function()
20901 if(this.getVisibilityEl().hasClass('hidden')){
20905 if(this.allowBlank){
20911 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20912 if(!e.dom.checked){
20924 validateCheckbox : function()
20927 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20928 //return (this.getValue() == this.inputValue) ? true : false;
20931 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20939 for(var i in group){
20940 if(group[i].el.isVisible(true)){
20948 for(var i in group){
20953 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20960 * Mark this field as valid
20962 markValid : function()
20966 this.fireEvent('valid', this);
20968 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20971 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20978 if(this.inputType == 'radio'){
20979 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20980 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20981 e.findParent('.form-group', false, true).addClass(_this.validClass);
20988 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20989 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20993 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20999 for(var i in group){
21000 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21001 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21006 * Mark this field as invalid
21007 * @param {String} msg The validation message
21009 markInvalid : function(msg)
21011 if(this.allowBlank){
21017 this.fireEvent('invalid', this, msg);
21019 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21022 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21026 label.markInvalid();
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.invalidClass);
21039 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21040 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
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.invalidClass);
21057 clearInvalid : function()
21059 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21061 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21063 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21065 if (label && label.iconEl) {
21066 label.iconEl.removeClass(label.validClass);
21067 label.iconEl.removeClass(label.invalidClass);
21071 disable : function()
21073 if(this.inputType != 'radio'){
21074 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21081 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21082 _this.getActionEl().addClass(this.disabledClass);
21083 e.dom.disabled = true;
21087 this.disabled = true;
21088 this.fireEvent("disable", this);
21092 enable : function()
21094 if(this.inputType != 'radio'){
21095 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21102 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21103 _this.getActionEl().removeClass(this.disabledClass);
21104 e.dom.disabled = false;
21108 this.disabled = false;
21109 this.fireEvent("enable", this);
21113 setBoxLabel : function(v)
21118 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21124 Roo.apply(Roo.bootstrap.CheckBox, {
21129 * register a CheckBox Group
21130 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21132 register : function(checkbox)
21134 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21135 this.groups[checkbox.groupId] = {};
21138 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21142 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21146 * fetch a CheckBox Group based on the group ID
21147 * @param {string} the group ID
21148 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21150 get: function(groupId) {
21151 if (typeof(this.groups[groupId]) == 'undefined') {
21155 return this.groups[groupId] ;
21168 * @class Roo.bootstrap.Radio
21169 * @extends Roo.bootstrap.Component
21170 * Bootstrap Radio class
21171 * @cfg {String} boxLabel - the label associated
21172 * @cfg {String} value - the value of radio
21175 * Create a new Radio
21176 * @param {Object} config The config object
21178 Roo.bootstrap.Radio = function(config){
21179 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21183 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21189 getAutoCreate : function()
21193 cls : 'form-group radio',
21198 html : this.boxLabel
21206 initEvents : function()
21208 this.parent().register(this);
21210 this.el.on('click', this.onClick, this);
21214 onClick : function(e)
21216 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21217 this.setChecked(true);
21221 setChecked : function(state, suppressEvent)
21223 this.parent().setValue(this.value, suppressEvent);
21227 setBoxLabel : function(v)
21232 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21247 * @class Roo.bootstrap.SecurePass
21248 * @extends Roo.bootstrap.Input
21249 * Bootstrap SecurePass class
21253 * Create a new SecurePass
21254 * @param {Object} config The config object
21257 Roo.bootstrap.SecurePass = function (config) {
21258 // these go here, so the translation tool can replace them..
21260 PwdEmpty: "Please type a password, and then retype it to confirm.",
21261 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21262 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21263 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21264 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21265 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21266 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21267 TooWeak: "Your password is Too Weak."
21269 this.meterLabel = "Password strength:";
21270 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21271 this.meterClass = [
21272 "roo-password-meter-tooweak",
21273 "roo-password-meter-weak",
21274 "roo-password-meter-medium",
21275 "roo-password-meter-strong",
21276 "roo-password-meter-grey"
21281 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21284 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21286 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21288 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21289 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21290 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21291 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21292 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21293 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21294 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21304 * @cfg {String/Object} Label for the strength meter (defaults to
21305 * 'Password strength:')
21310 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21311 * ['Weak', 'Medium', 'Strong'])
21314 pwdStrengths: false,
21327 initEvents: function ()
21329 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21331 if (this.el.is('input[type=password]') && Roo.isSafari) {
21332 this.el.on('keydown', this.SafariOnKeyDown, this);
21335 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21338 onRender: function (ct, position)
21340 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21341 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21342 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21344 this.trigger.createChild({
21349 cls: 'roo-password-meter-grey col-xs-12',
21352 //width: this.meterWidth + 'px'
21356 cls: 'roo-password-meter-text'
21362 if (this.hideTrigger) {
21363 this.trigger.setDisplayed(false);
21365 this.setSize(this.width || '', this.height || '');
21368 onDestroy: function ()
21370 if (this.trigger) {
21371 this.trigger.removeAllListeners();
21372 this.trigger.remove();
21375 this.wrap.remove();
21377 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21380 checkStrength: function ()
21382 var pwd = this.inputEl().getValue();
21383 if (pwd == this._lastPwd) {
21388 if (this.ClientSideStrongPassword(pwd)) {
21390 } else if (this.ClientSideMediumPassword(pwd)) {
21392 } else if (this.ClientSideWeakPassword(pwd)) {
21398 Roo.log('strength1: ' + strength);
21400 //var pm = this.trigger.child('div/div/div').dom;
21401 var pm = this.trigger.child('div/div');
21402 pm.removeClass(this.meterClass);
21403 pm.addClass(this.meterClass[strength]);
21406 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21408 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21410 this._lastPwd = pwd;
21414 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21416 this._lastPwd = '';
21418 var pm = this.trigger.child('div/div');
21419 pm.removeClass(this.meterClass);
21420 pm.addClass('roo-password-meter-grey');
21423 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21426 this.inputEl().dom.type='password';
21429 validateValue: function (value)
21432 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21435 if (value.length == 0) {
21436 if (this.allowBlank) {
21437 this.clearInvalid();
21441 this.markInvalid(this.errors.PwdEmpty);
21442 this.errorMsg = this.errors.PwdEmpty;
21450 if ('[\x21-\x7e]*'.match(value)) {
21451 this.markInvalid(this.errors.PwdBadChar);
21452 this.errorMsg = this.errors.PwdBadChar;
21455 if (value.length < 6) {
21456 this.markInvalid(this.errors.PwdShort);
21457 this.errorMsg = this.errors.PwdShort;
21460 if (value.length > 16) {
21461 this.markInvalid(this.errors.PwdLong);
21462 this.errorMsg = this.errors.PwdLong;
21466 if (this.ClientSideStrongPassword(value)) {
21468 } else if (this.ClientSideMediumPassword(value)) {
21470 } else if (this.ClientSideWeakPassword(value)) {
21477 if (strength < 2) {
21478 //this.markInvalid(this.errors.TooWeak);
21479 this.errorMsg = this.errors.TooWeak;
21484 console.log('strength2: ' + strength);
21486 //var pm = this.trigger.child('div/div/div').dom;
21488 var pm = this.trigger.child('div/div');
21489 pm.removeClass(this.meterClass);
21490 pm.addClass(this.meterClass[strength]);
21492 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21494 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21496 this.errorMsg = '';
21500 CharacterSetChecks: function (type)
21503 this.fResult = false;
21506 isctype: function (character, type)
21509 case this.kCapitalLetter:
21510 if (character >= 'A' && character <= 'Z') {
21515 case this.kSmallLetter:
21516 if (character >= 'a' && character <= 'z') {
21522 if (character >= '0' && character <= '9') {
21527 case this.kPunctuation:
21528 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21539 IsLongEnough: function (pwd, size)
21541 return !(pwd == null || isNaN(size) || pwd.length < size);
21544 SpansEnoughCharacterSets: function (word, nb)
21546 if (!this.IsLongEnough(word, nb))
21551 var characterSetChecks = new Array(
21552 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21553 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21556 for (var index = 0; index < word.length; ++index) {
21557 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21558 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21559 characterSetChecks[nCharSet].fResult = true;
21566 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21567 if (characterSetChecks[nCharSet].fResult) {
21572 if (nCharSets < nb) {
21578 ClientSideStrongPassword: function (pwd)
21580 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21583 ClientSideMediumPassword: function (pwd)
21585 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21588 ClientSideWeakPassword: function (pwd)
21590 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21593 })//<script type="text/javascript">
21596 * Based Ext JS Library 1.1.1
21597 * Copyright(c) 2006-2007, Ext JS, LLC.
21603 * @class Roo.HtmlEditorCore
21604 * @extends Roo.Component
21605 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21607 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21610 Roo.HtmlEditorCore = function(config){
21613 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21618 * @event initialize
21619 * Fires when the editor is fully initialized (including the iframe)
21620 * @param {Roo.HtmlEditorCore} this
21625 * Fires when the editor is first receives the focus. Any insertion must wait
21626 * until after this event.
21627 * @param {Roo.HtmlEditorCore} this
21631 * @event beforesync
21632 * Fires before the textarea is updated with content from the editor iframe. Return false
21633 * to cancel the sync.
21634 * @param {Roo.HtmlEditorCore} this
21635 * @param {String} html
21639 * @event beforepush
21640 * Fires before the iframe editor is updated with content from the textarea. Return false
21641 * to cancel the push.
21642 * @param {Roo.HtmlEditorCore} this
21643 * @param {String} html
21648 * Fires when the textarea is updated with content from the editor iframe.
21649 * @param {Roo.HtmlEditorCore} this
21650 * @param {String} html
21655 * Fires when the iframe editor is updated with content from the textarea.
21656 * @param {Roo.HtmlEditorCore} this
21657 * @param {String} html
21662 * @event editorevent
21663 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21664 * @param {Roo.HtmlEditorCore} this
21670 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21672 // defaults : white / black...
21673 this.applyBlacklists();
21680 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21684 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21690 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21695 * @cfg {Number} height (in pixels)
21699 * @cfg {Number} width (in pixels)
21704 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21707 stylesheets: false,
21712 // private properties
21713 validationEvent : false,
21715 initialized : false,
21717 sourceEditMode : false,
21718 onFocus : Roo.emptyFn,
21720 hideMode:'offsets',
21724 // blacklist + whitelisted elements..
21731 * Protected method that will not generally be called directly. It
21732 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21733 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21735 getDocMarkup : function(){
21739 // inherit styels from page...??
21740 if (this.stylesheets === false) {
21742 Roo.get(document.head).select('style').each(function(node) {
21743 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21746 Roo.get(document.head).select('link').each(function(node) {
21747 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21750 } else if (!this.stylesheets.length) {
21752 st = '<style type="text/css">' +
21753 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21756 st = '<style type="text/css">' +
21761 st += '<style type="text/css">' +
21762 'IMG { cursor: pointer } ' +
21765 var cls = 'roo-htmleditor-body';
21767 if(this.bodyCls.length){
21768 cls += ' ' + this.bodyCls;
21771 return '<html><head>' + st +
21772 //<style type="text/css">' +
21773 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21775 ' </head><body class="' + cls + '"></body></html>';
21779 onRender : function(ct, position)
21782 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21783 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21786 this.el.dom.style.border = '0 none';
21787 this.el.dom.setAttribute('tabIndex', -1);
21788 this.el.addClass('x-hidden hide');
21792 if(Roo.isIE){ // fix IE 1px bogus margin
21793 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21797 this.frameId = Roo.id();
21801 var iframe = this.owner.wrap.createChild({
21803 cls: 'form-control', // bootstrap..
21805 name: this.frameId,
21806 frameBorder : 'no',
21807 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21812 this.iframe = iframe.dom;
21814 this.assignDocWin();
21816 this.doc.designMode = 'on';
21819 this.doc.write(this.getDocMarkup());
21823 var task = { // must defer to wait for browser to be ready
21825 //console.log("run task?" + this.doc.readyState);
21826 this.assignDocWin();
21827 if(this.doc.body || this.doc.readyState == 'complete'){
21829 this.doc.designMode="on";
21833 Roo.TaskMgr.stop(task);
21834 this.initEditor.defer(10, this);
21841 Roo.TaskMgr.start(task);
21846 onResize : function(w, h)
21848 Roo.log('resize: ' +w + ',' + h );
21849 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21853 if(typeof w == 'number'){
21855 this.iframe.style.width = w + 'px';
21857 if(typeof h == 'number'){
21859 this.iframe.style.height = h + 'px';
21861 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21868 * Toggles the editor between standard and source edit mode.
21869 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21871 toggleSourceEdit : function(sourceEditMode){
21873 this.sourceEditMode = sourceEditMode === true;
21875 if(this.sourceEditMode){
21877 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21880 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21881 //this.iframe.className = '';
21884 //this.setSize(this.owner.wrap.getSize());
21885 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21892 * Protected method that will not generally be called directly. If you need/want
21893 * custom HTML cleanup, this is the method you should override.
21894 * @param {String} html The HTML to be cleaned
21895 * return {String} The cleaned HTML
21897 cleanHtml : function(html){
21898 html = String(html);
21899 if(html.length > 5){
21900 if(Roo.isSafari){ // strip safari nonsense
21901 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21904 if(html == ' '){
21911 * HTML Editor -> Textarea
21912 * Protected method that will not generally be called directly. Syncs the contents
21913 * of the editor iframe with the textarea.
21915 syncValue : function(){
21916 if(this.initialized){
21917 var bd = (this.doc.body || this.doc.documentElement);
21918 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21919 var html = bd.innerHTML;
21921 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21922 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21924 html = '<div style="'+m[0]+'">' + html + '</div>';
21927 html = this.cleanHtml(html);
21928 // fix up the special chars.. normaly like back quotes in word...
21929 // however we do not want to do this with chinese..
21930 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21931 var cc = b.charCodeAt();
21933 (cc >= 0x4E00 && cc < 0xA000 ) ||
21934 (cc >= 0x3400 && cc < 0x4E00 ) ||
21935 (cc >= 0xf900 && cc < 0xfb00 )
21941 if(this.owner.fireEvent('beforesync', this, html) !== false){
21942 this.el.dom.value = html;
21943 this.owner.fireEvent('sync', this, html);
21949 * Protected method that will not generally be called directly. Pushes the value of the textarea
21950 * into the iframe editor.
21952 pushValue : function(){
21953 if(this.initialized){
21954 var v = this.el.dom.value.trim();
21956 // if(v.length < 1){
21960 if(this.owner.fireEvent('beforepush', this, v) !== false){
21961 var d = (this.doc.body || this.doc.documentElement);
21963 this.cleanUpPaste();
21964 this.el.dom.value = d.innerHTML;
21965 this.owner.fireEvent('push', this, v);
21971 deferFocus : function(){
21972 this.focus.defer(10, this);
21976 focus : function(){
21977 if(this.win && !this.sourceEditMode){
21984 assignDocWin: function()
21986 var iframe = this.iframe;
21989 this.doc = iframe.contentWindow.document;
21990 this.win = iframe.contentWindow;
21992 // if (!Roo.get(this.frameId)) {
21995 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21996 // this.win = Roo.get(this.frameId).dom.contentWindow;
21998 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22002 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22003 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22008 initEditor : function(){
22009 //console.log("INIT EDITOR");
22010 this.assignDocWin();
22014 this.doc.designMode="on";
22016 this.doc.write(this.getDocMarkup());
22019 var dbody = (this.doc.body || this.doc.documentElement);
22020 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22021 // this copies styles from the containing element into thsi one..
22022 // not sure why we need all of this..
22023 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22025 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22026 //ss['background-attachment'] = 'fixed'; // w3c
22027 dbody.bgProperties = 'fixed'; // ie
22028 //Roo.DomHelper.applyStyles(dbody, ss);
22029 Roo.EventManager.on(this.doc, {
22030 //'mousedown': this.onEditorEvent,
22031 'mouseup': this.onEditorEvent,
22032 'dblclick': this.onEditorEvent,
22033 'click': this.onEditorEvent,
22034 'keyup': this.onEditorEvent,
22039 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22041 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22042 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22044 this.initialized = true;
22046 this.owner.fireEvent('initialize', this);
22051 onDestroy : function(){
22057 //for (var i =0; i < this.toolbars.length;i++) {
22058 // // fixme - ask toolbars for heights?
22059 // this.toolbars[i].onDestroy();
22062 //this.wrap.dom.innerHTML = '';
22063 //this.wrap.remove();
22068 onFirstFocus : function(){
22070 this.assignDocWin();
22073 this.activated = true;
22076 if(Roo.isGecko){ // prevent silly gecko errors
22078 var s = this.win.getSelection();
22079 if(!s.focusNode || s.focusNode.nodeType != 3){
22080 var r = s.getRangeAt(0);
22081 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22086 this.execCmd('useCSS', true);
22087 this.execCmd('styleWithCSS', false);
22090 this.owner.fireEvent('activate', this);
22094 adjustFont: function(btn){
22095 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22096 //if(Roo.isSafari){ // safari
22099 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22100 if(Roo.isSafari){ // safari
22101 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22102 v = (v < 10) ? 10 : v;
22103 v = (v > 48) ? 48 : v;
22104 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22109 v = Math.max(1, v+adjust);
22111 this.execCmd('FontSize', v );
22114 onEditorEvent : function(e)
22116 this.owner.fireEvent('editorevent', this, e);
22117 // this.updateToolbar();
22118 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22121 insertTag : function(tg)
22123 // could be a bit smarter... -> wrap the current selected tRoo..
22124 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22126 range = this.createRange(this.getSelection());
22127 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22128 wrappingNode.appendChild(range.extractContents());
22129 range.insertNode(wrappingNode);
22136 this.execCmd("formatblock", tg);
22140 insertText : function(txt)
22144 var range = this.createRange();
22145 range.deleteContents();
22146 //alert(Sender.getAttribute('label'));
22148 range.insertNode(this.doc.createTextNode(txt));
22154 * Executes a Midas editor command on the editor document and performs necessary focus and
22155 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22156 * @param {String} cmd The Midas command
22157 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22159 relayCmd : function(cmd, value){
22161 this.execCmd(cmd, value);
22162 this.owner.fireEvent('editorevent', this);
22163 //this.updateToolbar();
22164 this.owner.deferFocus();
22168 * Executes a Midas editor command directly on the editor document.
22169 * For visual commands, you should use {@link #relayCmd} instead.
22170 * <b>This should only be called after the editor is initialized.</b>
22171 * @param {String} cmd The Midas command
22172 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22174 execCmd : function(cmd, value){
22175 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22182 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22184 * @param {String} text | dom node..
22186 insertAtCursor : function(text)
22189 if(!this.activated){
22195 var r = this.doc.selection.createRange();
22206 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22210 // from jquery ui (MIT licenced)
22212 var win = this.win;
22214 if (win.getSelection && win.getSelection().getRangeAt) {
22215 range = win.getSelection().getRangeAt(0);
22216 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22217 range.insertNode(node);
22218 } else if (win.document.selection && win.document.selection.createRange) {
22219 // no firefox support
22220 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22221 win.document.selection.createRange().pasteHTML(txt);
22223 // no firefox support
22224 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22225 this.execCmd('InsertHTML', txt);
22234 mozKeyPress : function(e){
22236 var c = e.getCharCode(), cmd;
22239 c = String.fromCharCode(c).toLowerCase();
22253 this.cleanUpPaste.defer(100, this);
22261 e.preventDefault();
22269 fixKeys : function(){ // load time branching for fastest keydown performance
22271 return function(e){
22272 var k = e.getKey(), r;
22275 r = this.doc.selection.createRange();
22278 r.pasteHTML('    ');
22285 r = this.doc.selection.createRange();
22287 var target = r.parentElement();
22288 if(!target || target.tagName.toLowerCase() != 'li'){
22290 r.pasteHTML('<br />');
22296 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22297 this.cleanUpPaste.defer(100, this);
22303 }else if(Roo.isOpera){
22304 return function(e){
22305 var k = e.getKey();
22309 this.execCmd('InsertHTML','    ');
22312 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22313 this.cleanUpPaste.defer(100, this);
22318 }else if(Roo.isSafari){
22319 return function(e){
22320 var k = e.getKey();
22324 this.execCmd('InsertText','\t');
22328 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22329 this.cleanUpPaste.defer(100, this);
22337 getAllAncestors: function()
22339 var p = this.getSelectedNode();
22342 a.push(p); // push blank onto stack..
22343 p = this.getParentElement();
22347 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22351 a.push(this.doc.body);
22355 lastSelNode : false,
22358 getSelection : function()
22360 this.assignDocWin();
22361 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22364 getSelectedNode: function()
22366 // this may only work on Gecko!!!
22368 // should we cache this!!!!
22373 var range = this.createRange(this.getSelection()).cloneRange();
22376 var parent = range.parentElement();
22378 var testRange = range.duplicate();
22379 testRange.moveToElementText(parent);
22380 if (testRange.inRange(range)) {
22383 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22386 parent = parent.parentElement;
22391 // is ancestor a text element.
22392 var ac = range.commonAncestorContainer;
22393 if (ac.nodeType == 3) {
22394 ac = ac.parentNode;
22397 var ar = ac.childNodes;
22400 var other_nodes = [];
22401 var has_other_nodes = false;
22402 for (var i=0;i<ar.length;i++) {
22403 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22406 // fullly contained node.
22408 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22413 // probably selected..
22414 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22415 other_nodes.push(ar[i]);
22419 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22424 has_other_nodes = true;
22426 if (!nodes.length && other_nodes.length) {
22427 nodes= other_nodes;
22429 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22435 createRange: function(sel)
22437 // this has strange effects when using with
22438 // top toolbar - not sure if it's a great idea.
22439 //this.editor.contentWindow.focus();
22440 if (typeof sel != "undefined") {
22442 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22444 return this.doc.createRange();
22447 return this.doc.createRange();
22450 getParentElement: function()
22453 this.assignDocWin();
22454 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22456 var range = this.createRange(sel);
22459 var p = range.commonAncestorContainer;
22460 while (p.nodeType == 3) { // text node
22471 * Range intersection.. the hard stuff...
22475 * [ -- selected range --- ]
22479 * if end is before start or hits it. fail.
22480 * if start is after end or hits it fail.
22482 * if either hits (but other is outside. - then it's not
22488 // @see http://www.thismuchiknow.co.uk/?p=64.
22489 rangeIntersectsNode : function(range, node)
22491 var nodeRange = node.ownerDocument.createRange();
22493 nodeRange.selectNode(node);
22495 nodeRange.selectNodeContents(node);
22498 var rangeStartRange = range.cloneRange();
22499 rangeStartRange.collapse(true);
22501 var rangeEndRange = range.cloneRange();
22502 rangeEndRange.collapse(false);
22504 var nodeStartRange = nodeRange.cloneRange();
22505 nodeStartRange.collapse(true);
22507 var nodeEndRange = nodeRange.cloneRange();
22508 nodeEndRange.collapse(false);
22510 return rangeStartRange.compareBoundaryPoints(
22511 Range.START_TO_START, nodeEndRange) == -1 &&
22512 rangeEndRange.compareBoundaryPoints(
22513 Range.START_TO_START, nodeStartRange) == 1;
22517 rangeCompareNode : function(range, node)
22519 var nodeRange = node.ownerDocument.createRange();
22521 nodeRange.selectNode(node);
22523 nodeRange.selectNodeContents(node);
22527 range.collapse(true);
22529 nodeRange.collapse(true);
22531 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22532 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22534 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22536 var nodeIsBefore = ss == 1;
22537 var nodeIsAfter = ee == -1;
22539 if (nodeIsBefore && nodeIsAfter) {
22542 if (!nodeIsBefore && nodeIsAfter) {
22543 return 1; //right trailed.
22546 if (nodeIsBefore && !nodeIsAfter) {
22547 return 2; // left trailed.
22553 // private? - in a new class?
22554 cleanUpPaste : function()
22556 // cleans up the whole document..
22557 Roo.log('cleanuppaste');
22559 this.cleanUpChildren(this.doc.body);
22560 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22561 if (clean != this.doc.body.innerHTML) {
22562 this.doc.body.innerHTML = clean;
22567 cleanWordChars : function(input) {// change the chars to hex code
22568 var he = Roo.HtmlEditorCore;
22570 var output = input;
22571 Roo.each(he.swapCodes, function(sw) {
22572 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22574 output = output.replace(swapper, sw[1]);
22581 cleanUpChildren : function (n)
22583 if (!n.childNodes.length) {
22586 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22587 this.cleanUpChild(n.childNodes[i]);
22594 cleanUpChild : function (node)
22597 //console.log(node);
22598 if (node.nodeName == "#text") {
22599 // clean up silly Windows -- stuff?
22602 if (node.nodeName == "#comment") {
22603 node.parentNode.removeChild(node);
22604 // clean up silly Windows -- stuff?
22607 var lcname = node.tagName.toLowerCase();
22608 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22609 // whitelist of tags..
22611 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22613 node.parentNode.removeChild(node);
22618 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22620 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22621 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22623 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22624 // remove_keep_children = true;
22627 if (remove_keep_children) {
22628 this.cleanUpChildren(node);
22629 // inserts everything just before this node...
22630 while (node.childNodes.length) {
22631 var cn = node.childNodes[0];
22632 node.removeChild(cn);
22633 node.parentNode.insertBefore(cn, node);
22635 node.parentNode.removeChild(node);
22639 if (!node.attributes || !node.attributes.length) {
22640 this.cleanUpChildren(node);
22644 function cleanAttr(n,v)
22647 if (v.match(/^\./) || v.match(/^\//)) {
22650 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22653 if (v.match(/^#/)) {
22656 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22657 node.removeAttribute(n);
22661 var cwhite = this.cwhite;
22662 var cblack = this.cblack;
22664 function cleanStyle(n,v)
22666 if (v.match(/expression/)) { //XSS?? should we even bother..
22667 node.removeAttribute(n);
22671 var parts = v.split(/;/);
22674 Roo.each(parts, function(p) {
22675 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22679 var l = p.split(':').shift().replace(/\s+/g,'');
22680 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22682 if ( cwhite.length && cblack.indexOf(l) > -1) {
22683 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22684 //node.removeAttribute(n);
22688 // only allow 'c whitelisted system attributes'
22689 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22690 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22691 //node.removeAttribute(n);
22701 if (clean.length) {
22702 node.setAttribute(n, clean.join(';'));
22704 node.removeAttribute(n);
22710 for (var i = node.attributes.length-1; i > -1 ; i--) {
22711 var a = node.attributes[i];
22714 if (a.name.toLowerCase().substr(0,2)=='on') {
22715 node.removeAttribute(a.name);
22718 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22719 node.removeAttribute(a.name);
22722 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22723 cleanAttr(a.name,a.value); // fixme..
22726 if (a.name == 'style') {
22727 cleanStyle(a.name,a.value);
22730 /// clean up MS crap..
22731 // tecnically this should be a list of valid class'es..
22734 if (a.name == 'class') {
22735 if (a.value.match(/^Mso/)) {
22736 node.className = '';
22739 if (a.value.match(/^body$/)) {
22740 node.className = '';
22751 this.cleanUpChildren(node);
22757 * Clean up MS wordisms...
22759 cleanWord : function(node)
22764 this.cleanWord(this.doc.body);
22767 if (node.nodeName == "#text") {
22768 // clean up silly Windows -- stuff?
22771 if (node.nodeName == "#comment") {
22772 node.parentNode.removeChild(node);
22773 // clean up silly Windows -- stuff?
22777 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22778 node.parentNode.removeChild(node);
22782 // remove - but keep children..
22783 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22784 while (node.childNodes.length) {
22785 var cn = node.childNodes[0];
22786 node.removeChild(cn);
22787 node.parentNode.insertBefore(cn, node);
22789 node.parentNode.removeChild(node);
22790 this.iterateChildren(node, this.cleanWord);
22794 if (node.className.length) {
22796 var cn = node.className.split(/\W+/);
22798 Roo.each(cn, function(cls) {
22799 if (cls.match(/Mso[a-zA-Z]+/)) {
22804 node.className = cna.length ? cna.join(' ') : '';
22806 node.removeAttribute("class");
22810 if (node.hasAttribute("lang")) {
22811 node.removeAttribute("lang");
22814 if (node.hasAttribute("style")) {
22816 var styles = node.getAttribute("style").split(";");
22818 Roo.each(styles, function(s) {
22819 if (!s.match(/:/)) {
22822 var kv = s.split(":");
22823 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22826 // what ever is left... we allow.
22829 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22830 if (!nstyle.length) {
22831 node.removeAttribute('style');
22834 this.iterateChildren(node, this.cleanWord);
22840 * iterateChildren of a Node, calling fn each time, using this as the scole..
22841 * @param {DomNode} node node to iterate children of.
22842 * @param {Function} fn method of this class to call on each item.
22844 iterateChildren : function(node, fn)
22846 if (!node.childNodes.length) {
22849 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22850 fn.call(this, node.childNodes[i])
22856 * cleanTableWidths.
22858 * Quite often pasting from word etc.. results in tables with column and widths.
22859 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22862 cleanTableWidths : function(node)
22867 this.cleanTableWidths(this.doc.body);
22872 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22875 Roo.log(node.tagName);
22876 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22877 this.iterateChildren(node, this.cleanTableWidths);
22880 if (node.hasAttribute('width')) {
22881 node.removeAttribute('width');
22885 if (node.hasAttribute("style")) {
22888 var styles = node.getAttribute("style").split(";");
22890 Roo.each(styles, function(s) {
22891 if (!s.match(/:/)) {
22894 var kv = s.split(":");
22895 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22898 // what ever is left... we allow.
22901 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22902 if (!nstyle.length) {
22903 node.removeAttribute('style');
22907 this.iterateChildren(node, this.cleanTableWidths);
22915 domToHTML : function(currentElement, depth, nopadtext) {
22917 depth = depth || 0;
22918 nopadtext = nopadtext || false;
22920 if (!currentElement) {
22921 return this.domToHTML(this.doc.body);
22924 //Roo.log(currentElement);
22926 var allText = false;
22927 var nodeName = currentElement.nodeName;
22928 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22930 if (nodeName == '#text') {
22932 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22937 if (nodeName != 'BODY') {
22940 // Prints the node tagName, such as <A>, <IMG>, etc
22943 for(i = 0; i < currentElement.attributes.length;i++) {
22945 var aname = currentElement.attributes.item(i).name;
22946 if (!currentElement.attributes.item(i).value.length) {
22949 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22952 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22961 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22964 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22969 // Traverse the tree
22971 var currentElementChild = currentElement.childNodes.item(i);
22972 var allText = true;
22973 var innerHTML = '';
22975 while (currentElementChild) {
22976 // Formatting code (indent the tree so it looks nice on the screen)
22977 var nopad = nopadtext;
22978 if (lastnode == 'SPAN') {
22982 if (currentElementChild.nodeName == '#text') {
22983 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22984 toadd = nopadtext ? toadd : toadd.trim();
22985 if (!nopad && toadd.length > 80) {
22986 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22988 innerHTML += toadd;
22991 currentElementChild = currentElement.childNodes.item(i);
22997 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22999 // Recursively traverse the tree structure of the child node
23000 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23001 lastnode = currentElementChild.nodeName;
23003 currentElementChild=currentElement.childNodes.item(i);
23009 // The remaining code is mostly for formatting the tree
23010 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23015 ret+= "</"+tagName+">";
23021 applyBlacklists : function()
23023 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23024 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23028 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23029 if (b.indexOf(tag) > -1) {
23032 this.white.push(tag);
23036 Roo.each(w, function(tag) {
23037 if (b.indexOf(tag) > -1) {
23040 if (this.white.indexOf(tag) > -1) {
23043 this.white.push(tag);
23048 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23049 if (w.indexOf(tag) > -1) {
23052 this.black.push(tag);
23056 Roo.each(b, function(tag) {
23057 if (w.indexOf(tag) > -1) {
23060 if (this.black.indexOf(tag) > -1) {
23063 this.black.push(tag);
23068 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23069 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23073 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23074 if (b.indexOf(tag) > -1) {
23077 this.cwhite.push(tag);
23081 Roo.each(w, function(tag) {
23082 if (b.indexOf(tag) > -1) {
23085 if (this.cwhite.indexOf(tag) > -1) {
23088 this.cwhite.push(tag);
23093 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23094 if (w.indexOf(tag) > -1) {
23097 this.cblack.push(tag);
23101 Roo.each(b, function(tag) {
23102 if (w.indexOf(tag) > -1) {
23105 if (this.cblack.indexOf(tag) > -1) {
23108 this.cblack.push(tag);
23113 setStylesheets : function(stylesheets)
23115 if(typeof(stylesheets) == 'string'){
23116 Roo.get(this.iframe.contentDocument.head).createChild({
23118 rel : 'stylesheet',
23127 Roo.each(stylesheets, function(s) {
23132 Roo.get(_this.iframe.contentDocument.head).createChild({
23134 rel : 'stylesheet',
23143 removeStylesheets : function()
23147 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23152 setStyle : function(style)
23154 Roo.get(this.iframe.contentDocument.head).createChild({
23163 // hide stuff that is not compatible
23177 * @event specialkey
23181 * @cfg {String} fieldClass @hide
23184 * @cfg {String} focusClass @hide
23187 * @cfg {String} autoCreate @hide
23190 * @cfg {String} inputType @hide
23193 * @cfg {String} invalidClass @hide
23196 * @cfg {String} invalidText @hide
23199 * @cfg {String} msgFx @hide
23202 * @cfg {String} validateOnBlur @hide
23206 Roo.HtmlEditorCore.white = [
23207 'area', 'br', 'img', 'input', 'hr', 'wbr',
23209 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23210 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23211 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23212 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23213 'table', 'ul', 'xmp',
23215 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23218 'dir', 'menu', 'ol', 'ul', 'dl',
23224 Roo.HtmlEditorCore.black = [
23225 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23227 'base', 'basefont', 'bgsound', 'blink', 'body',
23228 'frame', 'frameset', 'head', 'html', 'ilayer',
23229 'iframe', 'layer', 'link', 'meta', 'object',
23230 'script', 'style' ,'title', 'xml' // clean later..
23232 Roo.HtmlEditorCore.clean = [
23233 'script', 'style', 'title', 'xml'
23235 Roo.HtmlEditorCore.remove = [
23240 Roo.HtmlEditorCore.ablack = [
23244 Roo.HtmlEditorCore.aclean = [
23245 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23249 Roo.HtmlEditorCore.pwhite= [
23250 'http', 'https', 'mailto'
23253 // white listed style attributes.
23254 Roo.HtmlEditorCore.cwhite= [
23255 // 'text-align', /// default is to allow most things..
23261 // black listed style attributes.
23262 Roo.HtmlEditorCore.cblack= [
23263 // 'font-size' -- this can be set by the project
23267 Roo.HtmlEditorCore.swapCodes =[
23286 * @class Roo.bootstrap.HtmlEditor
23287 * @extends Roo.bootstrap.TextArea
23288 * Bootstrap HtmlEditor class
23291 * Create a new HtmlEditor
23292 * @param {Object} config The config object
23295 Roo.bootstrap.HtmlEditor = function(config){
23296 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23297 if (!this.toolbars) {
23298 this.toolbars = [];
23301 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23304 * @event initialize
23305 * Fires when the editor is fully initialized (including the iframe)
23306 * @param {HtmlEditor} this
23311 * Fires when the editor is first receives the focus. Any insertion must wait
23312 * until after this event.
23313 * @param {HtmlEditor} this
23317 * @event beforesync
23318 * Fires before the textarea is updated with content from the editor iframe. Return false
23319 * to cancel the sync.
23320 * @param {HtmlEditor} this
23321 * @param {String} html
23325 * @event beforepush
23326 * Fires before the iframe editor is updated with content from the textarea. Return false
23327 * to cancel the push.
23328 * @param {HtmlEditor} this
23329 * @param {String} html
23334 * Fires when the textarea is updated with content from the editor iframe.
23335 * @param {HtmlEditor} this
23336 * @param {String} html
23341 * Fires when the iframe editor is updated with content from the textarea.
23342 * @param {HtmlEditor} this
23343 * @param {String} html
23347 * @event editmodechange
23348 * Fires when the editor switches edit modes
23349 * @param {HtmlEditor} this
23350 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23352 editmodechange: true,
23354 * @event editorevent
23355 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23356 * @param {HtmlEditor} this
23360 * @event firstfocus
23361 * Fires when on first focus - needed by toolbars..
23362 * @param {HtmlEditor} this
23367 * Auto save the htmlEditor value as a file into Events
23368 * @param {HtmlEditor} this
23372 * @event savedpreview
23373 * preview the saved version of htmlEditor
23374 * @param {HtmlEditor} this
23381 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23385 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23390 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23395 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23400 * @cfg {Number} height (in pixels)
23404 * @cfg {Number} width (in pixels)
23409 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23412 stylesheets: false,
23417 // private properties
23418 validationEvent : false,
23420 initialized : false,
23423 onFocus : Roo.emptyFn,
23425 hideMode:'offsets',
23427 tbContainer : false,
23431 toolbarContainer :function() {
23432 return this.wrap.select('.x-html-editor-tb',true).first();
23436 * Protected method that will not generally be called directly. It
23437 * is called when the editor creates its toolbar. Override this method if you need to
23438 * add custom toolbar buttons.
23439 * @param {HtmlEditor} editor
23441 createToolbar : function(){
23442 Roo.log('renewing');
23443 Roo.log("create toolbars");
23445 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23446 this.toolbars[0].render(this.toolbarContainer());
23450 // if (!editor.toolbars || !editor.toolbars.length) {
23451 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23454 // for (var i =0 ; i < editor.toolbars.length;i++) {
23455 // editor.toolbars[i] = Roo.factory(
23456 // typeof(editor.toolbars[i]) == 'string' ?
23457 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23458 // Roo.bootstrap.HtmlEditor);
23459 // editor.toolbars[i].init(editor);
23465 onRender : function(ct, position)
23467 // Roo.log("Call onRender: " + this.xtype);
23469 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23471 this.wrap = this.inputEl().wrap({
23472 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23475 this.editorcore.onRender(ct, position);
23477 if (this.resizable) {
23478 this.resizeEl = new Roo.Resizable(this.wrap, {
23482 minHeight : this.height,
23483 height: this.height,
23484 handles : this.resizable,
23487 resize : function(r, w, h) {
23488 _t.onResize(w,h); // -something
23494 this.createToolbar(this);
23497 if(!this.width && this.resizable){
23498 this.setSize(this.wrap.getSize());
23500 if (this.resizeEl) {
23501 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23502 // should trigger onReize..
23508 onResize : function(w, h)
23510 Roo.log('resize: ' +w + ',' + h );
23511 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23515 if(this.inputEl() ){
23516 if(typeof w == 'number'){
23517 var aw = w - this.wrap.getFrameWidth('lr');
23518 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23521 if(typeof h == 'number'){
23522 var tbh = -11; // fixme it needs to tool bar size!
23523 for (var i =0; i < this.toolbars.length;i++) {
23524 // fixme - ask toolbars for heights?
23525 tbh += this.toolbars[i].el.getHeight();
23526 //if (this.toolbars[i].footer) {
23527 // tbh += this.toolbars[i].footer.el.getHeight();
23535 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23536 ah -= 5; // knock a few pixes off for look..
23537 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23541 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23542 this.editorcore.onResize(ew,eh);
23547 * Toggles the editor between standard and source edit mode.
23548 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23550 toggleSourceEdit : function(sourceEditMode)
23552 this.editorcore.toggleSourceEdit(sourceEditMode);
23554 if(this.editorcore.sourceEditMode){
23555 Roo.log('editor - showing textarea');
23558 // Roo.log(this.syncValue());
23560 this.inputEl().removeClass(['hide', 'x-hidden']);
23561 this.inputEl().dom.removeAttribute('tabIndex');
23562 this.inputEl().focus();
23564 Roo.log('editor - hiding textarea');
23566 // Roo.log(this.pushValue());
23569 this.inputEl().addClass(['hide', 'x-hidden']);
23570 this.inputEl().dom.setAttribute('tabIndex', -1);
23571 //this.deferFocus();
23574 if(this.resizable){
23575 this.setSize(this.wrap.getSize());
23578 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23581 // private (for BoxComponent)
23582 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23584 // private (for BoxComponent)
23585 getResizeEl : function(){
23589 // private (for BoxComponent)
23590 getPositionEl : function(){
23595 initEvents : function(){
23596 this.originalValue = this.getValue();
23600 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23603 // markInvalid : Roo.emptyFn,
23605 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23608 // clearInvalid : Roo.emptyFn,
23610 setValue : function(v){
23611 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23612 this.editorcore.pushValue();
23617 deferFocus : function(){
23618 this.focus.defer(10, this);
23622 focus : function(){
23623 this.editorcore.focus();
23629 onDestroy : function(){
23635 for (var i =0; i < this.toolbars.length;i++) {
23636 // fixme - ask toolbars for heights?
23637 this.toolbars[i].onDestroy();
23640 this.wrap.dom.innerHTML = '';
23641 this.wrap.remove();
23646 onFirstFocus : function(){
23647 //Roo.log("onFirstFocus");
23648 this.editorcore.onFirstFocus();
23649 for (var i =0; i < this.toolbars.length;i++) {
23650 this.toolbars[i].onFirstFocus();
23656 syncValue : function()
23658 this.editorcore.syncValue();
23661 pushValue : function()
23663 this.editorcore.pushValue();
23667 // hide stuff that is not compatible
23681 * @event specialkey
23685 * @cfg {String} fieldClass @hide
23688 * @cfg {String} focusClass @hide
23691 * @cfg {String} autoCreate @hide
23694 * @cfg {String} inputType @hide
23697 * @cfg {String} invalidClass @hide
23700 * @cfg {String} invalidText @hide
23703 * @cfg {String} msgFx @hide
23706 * @cfg {String} validateOnBlur @hide
23715 Roo.namespace('Roo.bootstrap.htmleditor');
23717 * @class Roo.bootstrap.HtmlEditorToolbar1
23722 new Roo.bootstrap.HtmlEditor({
23725 new Roo.bootstrap.HtmlEditorToolbar1({
23726 disable : { fonts: 1 , format: 1, ..., ... , ...],
23732 * @cfg {Object} disable List of elements to disable..
23733 * @cfg {Array} btns List of additional buttons.
23737 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23740 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23743 Roo.apply(this, config);
23745 // default disabled, based on 'good practice'..
23746 this.disable = this.disable || {};
23747 Roo.applyIf(this.disable, {
23750 specialElements : true
23752 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23754 this.editor = config.editor;
23755 this.editorcore = config.editor.editorcore;
23757 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23759 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23760 // dont call parent... till later.
23762 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23767 editorcore : false,
23772 "h1","h2","h3","h4","h5","h6",
23774 "abbr", "acronym", "address", "cite", "samp", "var",
23778 onRender : function(ct, position)
23780 // Roo.log("Call onRender: " + this.xtype);
23782 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23784 this.el.dom.style.marginBottom = '0';
23786 var editorcore = this.editorcore;
23787 var editor= this.editor;
23790 var btn = function(id,cmd , toggle, handler, html){
23792 var event = toggle ? 'toggle' : 'click';
23797 xns: Roo.bootstrap,
23800 enableToggle:toggle !== false,
23802 pressed : toggle ? false : null,
23805 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23806 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23812 // var cb_box = function...
23817 xns: Roo.bootstrap,
23818 glyphicon : 'font',
23822 xns: Roo.bootstrap,
23826 Roo.each(this.formats, function(f) {
23827 style.menu.items.push({
23829 xns: Roo.bootstrap,
23830 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23835 editorcore.insertTag(this.tagname);
23842 children.push(style);
23844 btn('bold',false,true);
23845 btn('italic',false,true);
23846 btn('align-left', 'justifyleft',true);
23847 btn('align-center', 'justifycenter',true);
23848 btn('align-right' , 'justifyright',true);
23849 btn('link', false, false, function(btn) {
23850 //Roo.log("create link?");
23851 var url = prompt(this.createLinkText, this.defaultLinkValue);
23852 if(url && url != 'http:/'+'/'){
23853 this.editorcore.relayCmd('createlink', url);
23856 btn('list','insertunorderedlist',true);
23857 btn('pencil', false,true, function(btn){
23859 this.toggleSourceEdit(btn.pressed);
23862 if (this.editor.btns.length > 0) {
23863 for (var i = 0; i<this.editor.btns.length; i++) {
23864 children.push(this.editor.btns[i]);
23872 xns: Roo.bootstrap,
23877 xns: Roo.bootstrap,
23882 cog.menu.items.push({
23884 xns: Roo.bootstrap,
23885 html : Clean styles,
23890 editorcore.insertTag(this.tagname);
23899 this.xtype = 'NavSimplebar';
23901 for(var i=0;i< children.length;i++) {
23903 this.buttons.add(this.addxtypeChild(children[i]));
23907 editor.on('editorevent', this.updateToolbar, this);
23909 onBtnClick : function(id)
23911 this.editorcore.relayCmd(id);
23912 this.editorcore.focus();
23916 * Protected method that will not generally be called directly. It triggers
23917 * a toolbar update by reading the markup state of the current selection in the editor.
23919 updateToolbar: function(){
23921 if(!this.editorcore.activated){
23922 this.editor.onFirstFocus(); // is this neeed?
23926 var btns = this.buttons;
23927 var doc = this.editorcore.doc;
23928 btns.get('bold').setActive(doc.queryCommandState('bold'));
23929 btns.get('italic').setActive(doc.queryCommandState('italic'));
23930 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23932 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23933 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23934 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23936 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23937 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23940 var ans = this.editorcore.getAllAncestors();
23941 if (this.formatCombo) {
23944 var store = this.formatCombo.store;
23945 this.formatCombo.setValue("");
23946 for (var i =0; i < ans.length;i++) {
23947 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23949 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23957 // hides menus... - so this cant be on a menu...
23958 Roo.bootstrap.MenuMgr.hideAll();
23960 Roo.bootstrap.MenuMgr.hideAll();
23961 //this.editorsyncValue();
23963 onFirstFocus: function() {
23964 this.buttons.each(function(item){
23968 toggleSourceEdit : function(sourceEditMode){
23971 if(sourceEditMode){
23972 Roo.log("disabling buttons");
23973 this.buttons.each( function(item){
23974 if(item.cmd != 'pencil'){
23980 Roo.log("enabling buttons");
23981 if(this.editorcore.initialized){
23982 this.buttons.each( function(item){
23988 Roo.log("calling toggole on editor");
23989 // tell the editor that it's been pressed..
23990 this.editor.toggleSourceEdit(sourceEditMode);
24000 * @class Roo.bootstrap.Table.AbstractSelectionModel
24001 * @extends Roo.util.Observable
24002 * Abstract base class for grid SelectionModels. It provides the interface that should be
24003 * implemented by descendant classes. This class should not be directly instantiated.
24006 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24007 this.locked = false;
24008 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24012 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24013 /** @ignore Called by the grid automatically. Do not call directly. */
24014 init : function(grid){
24020 * Locks the selections.
24023 this.locked = true;
24027 * Unlocks the selections.
24029 unlock : function(){
24030 this.locked = false;
24034 * Returns true if the selections are locked.
24035 * @return {Boolean}
24037 isLocked : function(){
24038 return this.locked;
24042 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24043 * @class Roo.bootstrap.Table.RowSelectionModel
24044 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24045 * It supports multiple selections and keyboard selection/navigation.
24047 * @param {Object} config
24050 Roo.bootstrap.Table.RowSelectionModel = function(config){
24051 Roo.apply(this, config);
24052 this.selections = new Roo.util.MixedCollection(false, function(o){
24057 this.lastActive = false;
24061 * @event selectionchange
24062 * Fires when the selection changes
24063 * @param {SelectionModel} this
24065 "selectionchange" : true,
24067 * @event afterselectionchange
24068 * Fires after the selection changes (eg. by key press or clicking)
24069 * @param {SelectionModel} this
24071 "afterselectionchange" : true,
24073 * @event beforerowselect
24074 * Fires when a row is selected being selected, return false to cancel.
24075 * @param {SelectionModel} this
24076 * @param {Number} rowIndex The selected index
24077 * @param {Boolean} keepExisting False if other selections will be cleared
24079 "beforerowselect" : true,
24082 * Fires when a row is selected.
24083 * @param {SelectionModel} this
24084 * @param {Number} rowIndex The selected index
24085 * @param {Roo.data.Record} r The record
24087 "rowselect" : true,
24089 * @event rowdeselect
24090 * Fires when a row is deselected.
24091 * @param {SelectionModel} this
24092 * @param {Number} rowIndex The selected index
24094 "rowdeselect" : true
24096 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24097 this.locked = false;
24100 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24102 * @cfg {Boolean} singleSelect
24103 * True to allow selection of only one row at a time (defaults to false)
24105 singleSelect : false,
24108 initEvents : function()
24111 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24112 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24113 //}else{ // allow click to work like normal
24114 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24116 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24117 this.grid.on("rowclick", this.handleMouseDown, this);
24119 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24120 "up" : function(e){
24122 this.selectPrevious(e.shiftKey);
24123 }else if(this.last !== false && this.lastActive !== false){
24124 var last = this.last;
24125 this.selectRange(this.last, this.lastActive-1);
24126 this.grid.getView().focusRow(this.lastActive);
24127 if(last !== false){
24131 this.selectFirstRow();
24133 this.fireEvent("afterselectionchange", this);
24135 "down" : function(e){
24137 this.selectNext(e.shiftKey);
24138 }else if(this.last !== false && this.lastActive !== false){
24139 var last = this.last;
24140 this.selectRange(this.last, this.lastActive+1);
24141 this.grid.getView().focusRow(this.lastActive);
24142 if(last !== false){
24146 this.selectFirstRow();
24148 this.fireEvent("afterselectionchange", this);
24152 this.grid.store.on('load', function(){
24153 this.selections.clear();
24156 var view = this.grid.view;
24157 view.on("refresh", this.onRefresh, this);
24158 view.on("rowupdated", this.onRowUpdated, this);
24159 view.on("rowremoved", this.onRemove, this);
24164 onRefresh : function()
24166 var ds = this.grid.store, i, v = this.grid.view;
24167 var s = this.selections;
24168 s.each(function(r){
24169 if((i = ds.indexOfId(r.id)) != -1){
24178 onRemove : function(v, index, r){
24179 this.selections.remove(r);
24183 onRowUpdated : function(v, index, r){
24184 if(this.isSelected(r)){
24185 v.onRowSelect(index);
24191 * @param {Array} records The records to select
24192 * @param {Boolean} keepExisting (optional) True to keep existing selections
24194 selectRecords : function(records, keepExisting)
24197 this.clearSelections();
24199 var ds = this.grid.store;
24200 for(var i = 0, len = records.length; i < len; i++){
24201 this.selectRow(ds.indexOf(records[i]), true);
24206 * Gets the number of selected rows.
24209 getCount : function(){
24210 return this.selections.length;
24214 * Selects the first row in the grid.
24216 selectFirstRow : function(){
24221 * Select the last row.
24222 * @param {Boolean} keepExisting (optional) True to keep existing selections
24224 selectLastRow : function(keepExisting){
24225 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24226 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24230 * Selects the row immediately following the last selected row.
24231 * @param {Boolean} keepExisting (optional) True to keep existing selections
24233 selectNext : function(keepExisting)
24235 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24236 this.selectRow(this.last+1, keepExisting);
24237 this.grid.getView().focusRow(this.last);
24242 * Selects the row that precedes the last selected row.
24243 * @param {Boolean} keepExisting (optional) True to keep existing selections
24245 selectPrevious : function(keepExisting){
24247 this.selectRow(this.last-1, keepExisting);
24248 this.grid.getView().focusRow(this.last);
24253 * Returns the selected records
24254 * @return {Array} Array of selected records
24256 getSelections : function(){
24257 return [].concat(this.selections.items);
24261 * Returns the first selected record.
24264 getSelected : function(){
24265 return this.selections.itemAt(0);
24270 * Clears all selections.
24272 clearSelections : function(fast)
24278 var ds = this.grid.store;
24279 var s = this.selections;
24280 s.each(function(r){
24281 this.deselectRow(ds.indexOfId(r.id));
24285 this.selections.clear();
24292 * Selects all rows.
24294 selectAll : function(){
24298 this.selections.clear();
24299 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24300 this.selectRow(i, true);
24305 * Returns True if there is a selection.
24306 * @return {Boolean}
24308 hasSelection : function(){
24309 return this.selections.length > 0;
24313 * Returns True if the specified row is selected.
24314 * @param {Number/Record} record The record or index of the record to check
24315 * @return {Boolean}
24317 isSelected : function(index){
24318 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24319 return (r && this.selections.key(r.id) ? true : false);
24323 * Returns True if the specified record id is selected.
24324 * @param {String} id The id of record to check
24325 * @return {Boolean}
24327 isIdSelected : function(id){
24328 return (this.selections.key(id) ? true : false);
24333 handleMouseDBClick : function(e, t){
24337 handleMouseDown : function(e, t)
24339 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24340 if(this.isLocked() || rowIndex < 0 ){
24343 if(e.shiftKey && this.last !== false){
24344 var last = this.last;
24345 this.selectRange(last, rowIndex, e.ctrlKey);
24346 this.last = last; // reset the last
24350 var isSelected = this.isSelected(rowIndex);
24351 //Roo.log("select row:" + rowIndex);
24353 this.deselectRow(rowIndex);
24355 this.selectRow(rowIndex, true);
24359 if(e.button !== 0 && isSelected){
24360 alert('rowIndex 2: ' + rowIndex);
24361 view.focusRow(rowIndex);
24362 }else if(e.ctrlKey && isSelected){
24363 this.deselectRow(rowIndex);
24364 }else if(!isSelected){
24365 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24366 view.focusRow(rowIndex);
24370 this.fireEvent("afterselectionchange", this);
24373 handleDragableRowClick : function(grid, rowIndex, e)
24375 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24376 this.selectRow(rowIndex, false);
24377 grid.view.focusRow(rowIndex);
24378 this.fireEvent("afterselectionchange", this);
24383 * Selects multiple rows.
24384 * @param {Array} rows Array of the indexes of the row to select
24385 * @param {Boolean} keepExisting (optional) True to keep existing selections
24387 selectRows : function(rows, keepExisting){
24389 this.clearSelections();
24391 for(var i = 0, len = rows.length; i < len; i++){
24392 this.selectRow(rows[i], true);
24397 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24398 * @param {Number} startRow The index of the first row in the range
24399 * @param {Number} endRow The index of the last row in the range
24400 * @param {Boolean} keepExisting (optional) True to retain existing selections
24402 selectRange : function(startRow, endRow, keepExisting){
24407 this.clearSelections();
24409 if(startRow <= endRow){
24410 for(var i = startRow; i <= endRow; i++){
24411 this.selectRow(i, true);
24414 for(var i = startRow; i >= endRow; i--){
24415 this.selectRow(i, true);
24421 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24422 * @param {Number} startRow The index of the first row in the range
24423 * @param {Number} endRow The index of the last row in the range
24425 deselectRange : function(startRow, endRow, preventViewNotify){
24429 for(var i = startRow; i <= endRow; i++){
24430 this.deselectRow(i, preventViewNotify);
24436 * @param {Number} row The index of the row to select
24437 * @param {Boolean} keepExisting (optional) True to keep existing selections
24439 selectRow : function(index, keepExisting, preventViewNotify)
24441 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24444 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24445 if(!keepExisting || this.singleSelect){
24446 this.clearSelections();
24449 var r = this.grid.store.getAt(index);
24450 //console.log('selectRow - record id :' + r.id);
24452 this.selections.add(r);
24453 this.last = this.lastActive = index;
24454 if(!preventViewNotify){
24455 var proxy = new Roo.Element(
24456 this.grid.getRowDom(index)
24458 proxy.addClass('bg-info info');
24460 this.fireEvent("rowselect", this, index, r);
24461 this.fireEvent("selectionchange", this);
24467 * @param {Number} row The index of the row to deselect
24469 deselectRow : function(index, preventViewNotify)
24474 if(this.last == index){
24477 if(this.lastActive == index){
24478 this.lastActive = false;
24481 var r = this.grid.store.getAt(index);
24486 this.selections.remove(r);
24487 //.console.log('deselectRow - record id :' + r.id);
24488 if(!preventViewNotify){
24490 var proxy = new Roo.Element(
24491 this.grid.getRowDom(index)
24493 proxy.removeClass('bg-info info');
24495 this.fireEvent("rowdeselect", this, index);
24496 this.fireEvent("selectionchange", this);
24500 restoreLast : function(){
24502 this.last = this._last;
24507 acceptsNav : function(row, col, cm){
24508 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24512 onEditorKey : function(field, e){
24513 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24518 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24520 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24522 }else if(k == e.ENTER && !e.ctrlKey){
24526 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24528 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24530 }else if(k == e.ESC){
24534 g.startEditing(newCell[0], newCell[1]);
24540 * Ext JS Library 1.1.1
24541 * Copyright(c) 2006-2007, Ext JS, LLC.
24543 * Originally Released Under LGPL - original licence link has changed is not relivant.
24546 * <script type="text/javascript">
24550 * @class Roo.bootstrap.PagingToolbar
24551 * @extends Roo.bootstrap.NavSimplebar
24552 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24554 * Create a new PagingToolbar
24555 * @param {Object} config The config object
24556 * @param {Roo.data.Store} store
24558 Roo.bootstrap.PagingToolbar = function(config)
24560 // old args format still supported... - xtype is prefered..
24561 // created from xtype...
24563 this.ds = config.dataSource;
24565 if (config.store && !this.ds) {
24566 this.store= Roo.factory(config.store, Roo.data);
24567 this.ds = this.store;
24568 this.ds.xmodule = this.xmodule || false;
24571 this.toolbarItems = [];
24572 if (config.items) {
24573 this.toolbarItems = config.items;
24576 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24581 this.bind(this.ds);
24584 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24588 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24590 * @cfg {Roo.data.Store} dataSource
24591 * The underlying data store providing the paged data
24594 * @cfg {String/HTMLElement/Element} container
24595 * container The id or element that will contain the toolbar
24598 * @cfg {Boolean} displayInfo
24599 * True to display the displayMsg (defaults to false)
24602 * @cfg {Number} pageSize
24603 * The number of records to display per page (defaults to 20)
24607 * @cfg {String} displayMsg
24608 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24610 displayMsg : 'Displaying {0} - {1} of {2}',
24612 * @cfg {String} emptyMsg
24613 * The message to display when no records are found (defaults to "No data to display")
24615 emptyMsg : 'No data to display',
24617 * Customizable piece of the default paging text (defaults to "Page")
24620 beforePageText : "Page",
24622 * Customizable piece of the default paging text (defaults to "of %0")
24625 afterPageText : "of {0}",
24627 * Customizable piece of the default paging text (defaults to "First Page")
24630 firstText : "First Page",
24632 * Customizable piece of the default paging text (defaults to "Previous Page")
24635 prevText : "Previous Page",
24637 * Customizable piece of the default paging text (defaults to "Next Page")
24640 nextText : "Next Page",
24642 * Customizable piece of the default paging text (defaults to "Last Page")
24645 lastText : "Last Page",
24647 * Customizable piece of the default paging text (defaults to "Refresh")
24650 refreshText : "Refresh",
24654 onRender : function(ct, position)
24656 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24657 this.navgroup.parentId = this.id;
24658 this.navgroup.onRender(this.el, null);
24659 // add the buttons to the navgroup
24661 if(this.displayInfo){
24662 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24663 this.displayEl = this.el.select('.x-paging-info', true).first();
24664 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24665 // this.displayEl = navel.el.select('span',true).first();
24671 Roo.each(_this.buttons, function(e){ // this might need to use render????
24672 Roo.factory(e).render(_this.el);
24676 Roo.each(_this.toolbarItems, function(e) {
24677 _this.navgroup.addItem(e);
24681 this.first = this.navgroup.addItem({
24682 tooltip: this.firstText,
24684 icon : 'fa fa-backward',
24686 preventDefault: true,
24687 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24690 this.prev = this.navgroup.addItem({
24691 tooltip: this.prevText,
24693 icon : 'fa fa-step-backward',
24695 preventDefault: true,
24696 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24698 //this.addSeparator();
24701 var field = this.navgroup.addItem( {
24703 cls : 'x-paging-position',
24705 html : this.beforePageText +
24706 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24707 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24710 this.field = field.el.select('input', true).first();
24711 this.field.on("keydown", this.onPagingKeydown, this);
24712 this.field.on("focus", function(){this.dom.select();});
24715 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24716 //this.field.setHeight(18);
24717 //this.addSeparator();
24718 this.next = this.navgroup.addItem({
24719 tooltip: this.nextText,
24721 html : ' <i class="fa fa-step-forward">',
24723 preventDefault: true,
24724 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24726 this.last = this.navgroup.addItem({
24727 tooltip: this.lastText,
24728 icon : 'fa fa-forward',
24731 preventDefault: true,
24732 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24734 //this.addSeparator();
24735 this.loading = this.navgroup.addItem({
24736 tooltip: this.refreshText,
24737 icon: 'fa fa-refresh',
24738 preventDefault: true,
24739 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24745 updateInfo : function(){
24746 if(this.displayEl){
24747 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24748 var msg = count == 0 ?
24752 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24754 this.displayEl.update(msg);
24759 onLoad : function(ds, r, o)
24761 this.cursor = o.params.start ? o.params.start : 0;
24763 var d = this.getPageData(),
24768 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24769 this.field.dom.value = ap;
24770 this.first.setDisabled(ap == 1);
24771 this.prev.setDisabled(ap == 1);
24772 this.next.setDisabled(ap == ps);
24773 this.last.setDisabled(ap == ps);
24774 this.loading.enable();
24779 getPageData : function(){
24780 var total = this.ds.getTotalCount();
24783 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24784 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24789 onLoadError : function(){
24790 this.loading.enable();
24794 onPagingKeydown : function(e){
24795 var k = e.getKey();
24796 var d = this.getPageData();
24798 var v = this.field.dom.value, pageNum;
24799 if(!v || isNaN(pageNum = parseInt(v, 10))){
24800 this.field.dom.value = d.activePage;
24803 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24804 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24807 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))
24809 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24810 this.field.dom.value = pageNum;
24811 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24814 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24816 var v = this.field.dom.value, pageNum;
24817 var increment = (e.shiftKey) ? 10 : 1;
24818 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24821 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24822 this.field.dom.value = d.activePage;
24825 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24827 this.field.dom.value = parseInt(v, 10) + increment;
24828 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24829 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24836 beforeLoad : function(){
24838 this.loading.disable();
24843 onClick : function(which){
24852 ds.load({params:{start: 0, limit: this.pageSize}});
24855 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24858 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24861 var total = ds.getTotalCount();
24862 var extra = total % this.pageSize;
24863 var lastStart = extra ? (total - extra) : total-this.pageSize;
24864 ds.load({params:{start: lastStart, limit: this.pageSize}});
24867 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24873 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24874 * @param {Roo.data.Store} store The data store to unbind
24876 unbind : function(ds){
24877 ds.un("beforeload", this.beforeLoad, this);
24878 ds.un("load", this.onLoad, this);
24879 ds.un("loadexception", this.onLoadError, this);
24880 ds.un("remove", this.updateInfo, this);
24881 ds.un("add", this.updateInfo, this);
24882 this.ds = undefined;
24886 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24887 * @param {Roo.data.Store} store The data store to bind
24889 bind : function(ds){
24890 ds.on("beforeload", this.beforeLoad, this);
24891 ds.on("load", this.onLoad, this);
24892 ds.on("loadexception", this.onLoadError, this);
24893 ds.on("remove", this.updateInfo, this);
24894 ds.on("add", this.updateInfo, this);
24905 * @class Roo.bootstrap.MessageBar
24906 * @extends Roo.bootstrap.Component
24907 * Bootstrap MessageBar class
24908 * @cfg {String} html contents of the MessageBar
24909 * @cfg {String} weight (info | success | warning | danger) default info
24910 * @cfg {String} beforeClass insert the bar before the given class
24911 * @cfg {Boolean} closable (true | false) default false
24912 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24915 * Create a new Element
24916 * @param {Object} config The config object
24919 Roo.bootstrap.MessageBar = function(config){
24920 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24923 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24929 beforeClass: 'bootstrap-sticky-wrap',
24931 getAutoCreate : function(){
24935 cls: 'alert alert-dismissable alert-' + this.weight,
24940 html: this.html || ''
24946 cfg.cls += ' alert-messages-fixed';
24960 onRender : function(ct, position)
24962 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24965 var cfg = Roo.apply({}, this.getAutoCreate());
24969 cfg.cls += ' ' + this.cls;
24972 cfg.style = this.style;
24974 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24976 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24979 this.el.select('>button.close').on('click', this.hide, this);
24985 if (!this.rendered) {
24991 this.fireEvent('show', this);
24997 if (!this.rendered) {
25003 this.fireEvent('hide', this);
25006 update : function()
25008 // var e = this.el.dom.firstChild;
25010 // if(this.closable){
25011 // e = e.nextSibling;
25014 // e.data = this.html || '';
25016 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25032 * @class Roo.bootstrap.Graph
25033 * @extends Roo.bootstrap.Component
25034 * Bootstrap Graph class
25038 @cfg {String} graphtype bar | vbar | pie
25039 @cfg {number} g_x coodinator | centre x (pie)
25040 @cfg {number} g_y coodinator | centre y (pie)
25041 @cfg {number} g_r radius (pie)
25042 @cfg {number} g_height height of the chart (respected by all elements in the set)
25043 @cfg {number} g_width width of the chart (respected by all elements in the set)
25044 @cfg {Object} title The title of the chart
25047 -opts (object) options for the chart
25049 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25050 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25052 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.
25053 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25055 o stretch (boolean)
25057 -opts (object) options for the pie
25060 o startAngle (number)
25061 o endAngle (number)
25065 * Create a new Input
25066 * @param {Object} config The config object
25069 Roo.bootstrap.Graph = function(config){
25070 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25076 * The img click event for the img.
25077 * @param {Roo.EventObject} e
25083 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25094 //g_colors: this.colors,
25101 getAutoCreate : function(){
25112 onRender : function(ct,position){
25115 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25117 if (typeof(Raphael) == 'undefined') {
25118 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25122 this.raphael = Raphael(this.el.dom);
25124 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25125 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25126 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25127 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25129 r.text(160, 10, "Single Series Chart").attr(txtattr);
25130 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25131 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25132 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25134 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25135 r.barchart(330, 10, 300, 220, data1);
25136 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25137 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25140 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25141 // r.barchart(30, 30, 560, 250, xdata, {
25142 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25143 // axis : "0 0 1 1",
25144 // axisxlabels : xdata
25145 // //yvalues : cols,
25148 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25150 // this.load(null,xdata,{
25151 // axis : "0 0 1 1",
25152 // axisxlabels : xdata
25157 load : function(graphtype,xdata,opts)
25159 this.raphael.clear();
25161 graphtype = this.graphtype;
25166 var r = this.raphael,
25167 fin = function () {
25168 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25170 fout = function () {
25171 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25173 pfin = function() {
25174 this.sector.stop();
25175 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25178 this.label[0].stop();
25179 this.label[0].attr({ r: 7.5 });
25180 this.label[1].attr({ "font-weight": 800 });
25183 pfout = function() {
25184 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25187 this.label[0].animate({ r: 5 }, 500, "bounce");
25188 this.label[1].attr({ "font-weight": 400 });
25194 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25197 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25200 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25201 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25203 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25210 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25215 setTitle: function(o)
25220 initEvents: function() {
25223 this.el.on('click', this.onClick, this);
25227 onClick : function(e)
25229 Roo.log('img onclick');
25230 this.fireEvent('click', this, e);
25242 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25245 * @class Roo.bootstrap.dash.NumberBox
25246 * @extends Roo.bootstrap.Component
25247 * Bootstrap NumberBox class
25248 * @cfg {String} headline Box headline
25249 * @cfg {String} content Box content
25250 * @cfg {String} icon Box icon
25251 * @cfg {String} footer Footer text
25252 * @cfg {String} fhref Footer href
25255 * Create a new NumberBox
25256 * @param {Object} config The config object
25260 Roo.bootstrap.dash.NumberBox = function(config){
25261 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25265 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25274 getAutoCreate : function(){
25278 cls : 'small-box ',
25286 cls : 'roo-headline',
25287 html : this.headline
25291 cls : 'roo-content',
25292 html : this.content
25306 cls : 'ion ' + this.icon
25315 cls : 'small-box-footer',
25316 href : this.fhref || '#',
25320 cfg.cn.push(footer);
25327 onRender : function(ct,position){
25328 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25335 setHeadline: function (value)
25337 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25340 setFooter: function (value, href)
25342 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25345 this.el.select('a.small-box-footer',true).first().attr('href', href);
25350 setContent: function (value)
25352 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25355 initEvents: function()
25369 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25372 * @class Roo.bootstrap.dash.TabBox
25373 * @extends Roo.bootstrap.Component
25374 * Bootstrap TabBox class
25375 * @cfg {String} title Title of the TabBox
25376 * @cfg {String} icon Icon of the TabBox
25377 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25378 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25381 * Create a new TabBox
25382 * @param {Object} config The config object
25386 Roo.bootstrap.dash.TabBox = function(config){
25387 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25392 * When a pane is added
25393 * @param {Roo.bootstrap.dash.TabPane} pane
25397 * @event activatepane
25398 * When a pane is activated
25399 * @param {Roo.bootstrap.dash.TabPane} pane
25401 "activatepane" : true
25409 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25414 tabScrollable : false,
25416 getChildContainer : function()
25418 return this.el.select('.tab-content', true).first();
25421 getAutoCreate : function(){
25425 cls: 'pull-left header',
25433 cls: 'fa ' + this.icon
25439 cls: 'nav nav-tabs pull-right',
25445 if(this.tabScrollable){
25452 cls: 'nav nav-tabs pull-right',
25463 cls: 'nav-tabs-custom',
25468 cls: 'tab-content no-padding',
25476 initEvents : function()
25478 //Roo.log('add add pane handler');
25479 this.on('addpane', this.onAddPane, this);
25482 * Updates the box title
25483 * @param {String} html to set the title to.
25485 setTitle : function(value)
25487 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25489 onAddPane : function(pane)
25491 this.panes.push(pane);
25492 //Roo.log('addpane');
25494 // tabs are rendere left to right..
25495 if(!this.showtabs){
25499 var ctr = this.el.select('.nav-tabs', true).first();
25502 var existing = ctr.select('.nav-tab',true);
25503 var qty = existing.getCount();;
25506 var tab = ctr.createChild({
25508 cls : 'nav-tab' + (qty ? '' : ' active'),
25516 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25519 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25521 pane.el.addClass('active');
25526 onTabClick : function(ev,un,ob,pane)
25528 //Roo.log('tab - prev default');
25529 ev.preventDefault();
25532 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25533 pane.tab.addClass('active');
25534 //Roo.log(pane.title);
25535 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25536 // technically we should have a deactivate event.. but maybe add later.
25537 // and it should not de-activate the selected tab...
25538 this.fireEvent('activatepane', pane);
25539 pane.el.addClass('active');
25540 pane.fireEvent('activate');
25545 getActivePane : function()
25548 Roo.each(this.panes, function(p) {
25549 if(p.el.hasClass('active')){
25570 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25572 * @class Roo.bootstrap.TabPane
25573 * @extends Roo.bootstrap.Component
25574 * Bootstrap TabPane class
25575 * @cfg {Boolean} active (false | true) Default false
25576 * @cfg {String} title title of panel
25580 * Create a new TabPane
25581 * @param {Object} config The config object
25584 Roo.bootstrap.dash.TabPane = function(config){
25585 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25591 * When a pane is activated
25592 * @param {Roo.bootstrap.dash.TabPane} pane
25599 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25604 // the tabBox that this is attached to.
25607 getAutoCreate : function()
25615 cfg.cls += ' active';
25620 initEvents : function()
25622 //Roo.log('trigger add pane handler');
25623 this.parent().fireEvent('addpane', this)
25627 * Updates the tab title
25628 * @param {String} html to set the title to.
25630 setTitle: function(str)
25636 this.tab.select('a', true).first().dom.innerHTML = str;
25653 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25656 * @class Roo.bootstrap.menu.Menu
25657 * @extends Roo.bootstrap.Component
25658 * Bootstrap Menu class - container for Menu
25659 * @cfg {String} html Text of the menu
25660 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25661 * @cfg {String} icon Font awesome icon
25662 * @cfg {String} pos Menu align to (top | bottom) default bottom
25666 * Create a new Menu
25667 * @param {Object} config The config object
25671 Roo.bootstrap.menu.Menu = function(config){
25672 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25676 * @event beforeshow
25677 * Fires before this menu is displayed
25678 * @param {Roo.bootstrap.menu.Menu} this
25682 * @event beforehide
25683 * Fires before this menu is hidden
25684 * @param {Roo.bootstrap.menu.Menu} this
25689 * Fires after this menu is displayed
25690 * @param {Roo.bootstrap.menu.Menu} this
25695 * Fires after this menu is hidden
25696 * @param {Roo.bootstrap.menu.Menu} this
25701 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25702 * @param {Roo.bootstrap.menu.Menu} this
25703 * @param {Roo.EventObject} e
25710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25714 weight : 'default',
25719 getChildContainer : function() {
25720 if(this.isSubMenu){
25724 return this.el.select('ul.dropdown-menu', true).first();
25727 getAutoCreate : function()
25732 cls : 'roo-menu-text',
25740 cls : 'fa ' + this.icon
25751 cls : 'dropdown-button btn btn-' + this.weight,
25756 cls : 'dropdown-toggle btn btn-' + this.weight,
25766 cls : 'dropdown-menu'
25772 if(this.pos == 'top'){
25773 cfg.cls += ' dropup';
25776 if(this.isSubMenu){
25779 cls : 'dropdown-menu'
25786 onRender : function(ct, position)
25788 this.isSubMenu = ct.hasClass('dropdown-submenu');
25790 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25793 initEvents : function()
25795 if(this.isSubMenu){
25799 this.hidden = true;
25801 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25802 this.triggerEl.on('click', this.onTriggerPress, this);
25804 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25805 this.buttonEl.on('click', this.onClick, this);
25811 if(this.isSubMenu){
25815 return this.el.select('ul.dropdown-menu', true).first();
25818 onClick : function(e)
25820 this.fireEvent("click", this, e);
25823 onTriggerPress : function(e)
25825 if (this.isVisible()) {
25832 isVisible : function(){
25833 return !this.hidden;
25838 this.fireEvent("beforeshow", this);
25840 this.hidden = false;
25841 this.el.addClass('open');
25843 Roo.get(document).on("mouseup", this.onMouseUp, this);
25845 this.fireEvent("show", this);
25852 this.fireEvent("beforehide", this);
25854 this.hidden = true;
25855 this.el.removeClass('open');
25857 Roo.get(document).un("mouseup", this.onMouseUp);
25859 this.fireEvent("hide", this);
25862 onMouseUp : function()
25876 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25879 * @class Roo.bootstrap.menu.Item
25880 * @extends Roo.bootstrap.Component
25881 * Bootstrap MenuItem class
25882 * @cfg {Boolean} submenu (true | false) default false
25883 * @cfg {String} html text of the item
25884 * @cfg {String} href the link
25885 * @cfg {Boolean} disable (true | false) default false
25886 * @cfg {Boolean} preventDefault (true | false) default true
25887 * @cfg {String} icon Font awesome icon
25888 * @cfg {String} pos Submenu align to (left | right) default right
25892 * Create a new Item
25893 * @param {Object} config The config object
25897 Roo.bootstrap.menu.Item = function(config){
25898 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25902 * Fires when the mouse is hovering over this menu
25903 * @param {Roo.bootstrap.menu.Item} this
25904 * @param {Roo.EventObject} e
25909 * Fires when the mouse exits this menu
25910 * @param {Roo.bootstrap.menu.Item} this
25911 * @param {Roo.EventObject} e
25917 * The raw click event for the entire grid.
25918 * @param {Roo.EventObject} e
25924 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25929 preventDefault: true,
25934 getAutoCreate : function()
25939 cls : 'roo-menu-item-text',
25947 cls : 'fa ' + this.icon
25956 href : this.href || '#',
25963 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25967 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25969 if(this.pos == 'left'){
25970 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25977 initEvents : function()
25979 this.el.on('mouseover', this.onMouseOver, this);
25980 this.el.on('mouseout', this.onMouseOut, this);
25982 this.el.select('a', true).first().on('click', this.onClick, this);
25986 onClick : function(e)
25988 if(this.preventDefault){
25989 e.preventDefault();
25992 this.fireEvent("click", this, e);
25995 onMouseOver : function(e)
25997 if(this.submenu && this.pos == 'left'){
25998 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26001 this.fireEvent("mouseover", this, e);
26004 onMouseOut : function(e)
26006 this.fireEvent("mouseout", this, e);
26018 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26021 * @class Roo.bootstrap.menu.Separator
26022 * @extends Roo.bootstrap.Component
26023 * Bootstrap Separator class
26026 * Create a new Separator
26027 * @param {Object} config The config object
26031 Roo.bootstrap.menu.Separator = function(config){
26032 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26035 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26037 getAutoCreate : function(){
26058 * @class Roo.bootstrap.Tooltip
26059 * Bootstrap Tooltip class
26060 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26061 * to determine which dom element triggers the tooltip.
26063 * It needs to add support for additional attributes like tooltip-position
26066 * Create a new Toolti
26067 * @param {Object} config The config object
26070 Roo.bootstrap.Tooltip = function(config){
26071 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26073 this.alignment = Roo.bootstrap.Tooltip.alignment;
26075 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26076 this.alignment = config.alignment;
26081 Roo.apply(Roo.bootstrap.Tooltip, {
26083 * @function init initialize tooltip monitoring.
26087 currentTip : false,
26088 currentRegion : false,
26094 Roo.get(document).on('mouseover', this.enter ,this);
26095 Roo.get(document).on('mouseout', this.leave, this);
26098 this.currentTip = new Roo.bootstrap.Tooltip();
26101 enter : function(ev)
26103 var dom = ev.getTarget();
26105 //Roo.log(['enter',dom]);
26106 var el = Roo.fly(dom);
26107 if (this.currentEl) {
26109 //Roo.log(this.currentEl);
26110 //Roo.log(this.currentEl.contains(dom));
26111 if (this.currentEl == el) {
26114 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26120 if (this.currentTip.el) {
26121 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26125 if(!el || el.dom == document){
26131 // you can not look for children, as if el is the body.. then everythign is the child..
26132 if (!el.attr('tooltip')) { //
26133 if (!el.select("[tooltip]").elements.length) {
26136 // is the mouse over this child...?
26137 bindEl = el.select("[tooltip]").first();
26138 var xy = ev.getXY();
26139 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26140 //Roo.log("not in region.");
26143 //Roo.log("child element over..");
26146 this.currentEl = bindEl;
26147 this.currentTip.bind(bindEl);
26148 this.currentRegion = Roo.lib.Region.getRegion(dom);
26149 this.currentTip.enter();
26152 leave : function(ev)
26154 var dom = ev.getTarget();
26155 //Roo.log(['leave',dom]);
26156 if (!this.currentEl) {
26161 if (dom != this.currentEl.dom) {
26164 var xy = ev.getXY();
26165 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26168 // only activate leave if mouse cursor is outside... bounding box..
26173 if (this.currentTip) {
26174 this.currentTip.leave();
26176 //Roo.log('clear currentEl');
26177 this.currentEl = false;
26182 'left' : ['r-l', [-2,0], 'right'],
26183 'right' : ['l-r', [2,0], 'left'],
26184 'bottom' : ['t-b', [0,2], 'top'],
26185 'top' : [ 'b-t', [0,-2], 'bottom']
26191 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26196 delay : null, // can be { show : 300 , hide: 500}
26200 hoverState : null, //???
26202 placement : 'bottom',
26206 getAutoCreate : function(){
26213 cls : 'tooltip-arrow'
26216 cls : 'tooltip-inner'
26223 bind : function(el)
26229 enter : function () {
26231 if (this.timeout != null) {
26232 clearTimeout(this.timeout);
26235 this.hoverState = 'in';
26236 //Roo.log("enter - show");
26237 if (!this.delay || !this.delay.show) {
26242 this.timeout = setTimeout(function () {
26243 if (_t.hoverState == 'in') {
26246 }, this.delay.show);
26250 clearTimeout(this.timeout);
26252 this.hoverState = 'out';
26253 if (!this.delay || !this.delay.hide) {
26259 this.timeout = setTimeout(function () {
26260 //Roo.log("leave - timeout");
26262 if (_t.hoverState == 'out') {
26264 Roo.bootstrap.Tooltip.currentEl = false;
26269 show : function (msg)
26272 this.render(document.body);
26275 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26277 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26279 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26281 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26283 var placement = typeof this.placement == 'function' ?
26284 this.placement.call(this, this.el, on_el) :
26287 var autoToken = /\s?auto?\s?/i;
26288 var autoPlace = autoToken.test(placement);
26290 placement = placement.replace(autoToken, '') || 'top';
26294 //this.el.setXY([0,0]);
26296 //this.el.dom.style.display='block';
26298 //this.el.appendTo(on_el);
26300 var p = this.getPosition();
26301 var box = this.el.getBox();
26307 var align = this.alignment[placement];
26309 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26311 if(placement == 'top' || placement == 'bottom'){
26313 placement = 'right';
26316 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26317 placement = 'left';
26320 var scroll = Roo.select('body', true).first().getScroll();
26322 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26326 align = this.alignment[placement];
26329 this.el.alignTo(this.bindEl, align[0],align[1]);
26330 //var arrow = this.el.select('.arrow',true).first();
26331 //arrow.set(align[2],
26333 this.el.addClass(placement);
26335 this.el.addClass('in fade');
26337 this.hoverState = null;
26339 if (this.el.hasClass('fade')) {
26350 //this.el.setXY([0,0]);
26351 this.el.removeClass('in');
26367 * @class Roo.bootstrap.LocationPicker
26368 * @extends Roo.bootstrap.Component
26369 * Bootstrap LocationPicker class
26370 * @cfg {Number} latitude Position when init default 0
26371 * @cfg {Number} longitude Position when init default 0
26372 * @cfg {Number} zoom default 15
26373 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26374 * @cfg {Boolean} mapTypeControl default false
26375 * @cfg {Boolean} disableDoubleClickZoom default false
26376 * @cfg {Boolean} scrollwheel default true
26377 * @cfg {Boolean} streetViewControl default false
26378 * @cfg {Number} radius default 0
26379 * @cfg {String} locationName
26380 * @cfg {Boolean} draggable default true
26381 * @cfg {Boolean} enableAutocomplete default false
26382 * @cfg {Boolean} enableReverseGeocode default true
26383 * @cfg {String} markerTitle
26386 * Create a new LocationPicker
26387 * @param {Object} config The config object
26391 Roo.bootstrap.LocationPicker = function(config){
26393 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26398 * Fires when the picker initialized.
26399 * @param {Roo.bootstrap.LocationPicker} this
26400 * @param {Google Location} location
26404 * @event positionchanged
26405 * Fires when the picker position changed.
26406 * @param {Roo.bootstrap.LocationPicker} this
26407 * @param {Google Location} location
26409 positionchanged : true,
26412 * Fires when the map resize.
26413 * @param {Roo.bootstrap.LocationPicker} this
26418 * Fires when the map show.
26419 * @param {Roo.bootstrap.LocationPicker} this
26424 * Fires when the map hide.
26425 * @param {Roo.bootstrap.LocationPicker} this
26430 * Fires when click the map.
26431 * @param {Roo.bootstrap.LocationPicker} this
26432 * @param {Map event} e
26436 * @event mapRightClick
26437 * Fires when right click the map.
26438 * @param {Roo.bootstrap.LocationPicker} this
26439 * @param {Map event} e
26441 mapRightClick : true,
26443 * @event markerClick
26444 * Fires when click the marker.
26445 * @param {Roo.bootstrap.LocationPicker} this
26446 * @param {Map event} e
26448 markerClick : true,
26450 * @event markerRightClick
26451 * Fires when right click the marker.
26452 * @param {Roo.bootstrap.LocationPicker} this
26453 * @param {Map event} e
26455 markerRightClick : true,
26457 * @event OverlayViewDraw
26458 * Fires when OverlayView Draw
26459 * @param {Roo.bootstrap.LocationPicker} this
26461 OverlayViewDraw : true,
26463 * @event OverlayViewOnAdd
26464 * Fires when OverlayView Draw
26465 * @param {Roo.bootstrap.LocationPicker} this
26467 OverlayViewOnAdd : true,
26469 * @event OverlayViewOnRemove
26470 * Fires when OverlayView Draw
26471 * @param {Roo.bootstrap.LocationPicker} this
26473 OverlayViewOnRemove : true,
26475 * @event OverlayViewShow
26476 * Fires when OverlayView Draw
26477 * @param {Roo.bootstrap.LocationPicker} this
26478 * @param {Pixel} cpx
26480 OverlayViewShow : true,
26482 * @event OverlayViewHide
26483 * Fires when OverlayView Draw
26484 * @param {Roo.bootstrap.LocationPicker} this
26486 OverlayViewHide : true,
26488 * @event loadexception
26489 * Fires when load google lib failed.
26490 * @param {Roo.bootstrap.LocationPicker} this
26492 loadexception : true
26497 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26499 gMapContext: false,
26505 mapTypeControl: false,
26506 disableDoubleClickZoom: false,
26508 streetViewControl: false,
26512 enableAutocomplete: false,
26513 enableReverseGeocode: true,
26516 getAutoCreate: function()
26521 cls: 'roo-location-picker'
26527 initEvents: function(ct, position)
26529 if(!this.el.getWidth() || this.isApplied()){
26533 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26538 initial: function()
26540 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26541 this.fireEvent('loadexception', this);
26545 if(!this.mapTypeId){
26546 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26549 this.gMapContext = this.GMapContext();
26551 this.initOverlayView();
26553 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26557 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26558 _this.setPosition(_this.gMapContext.marker.position);
26561 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26562 _this.fireEvent('mapClick', this, event);
26566 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26567 _this.fireEvent('mapRightClick', this, event);
26571 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26572 _this.fireEvent('markerClick', this, event);
26576 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26577 _this.fireEvent('markerRightClick', this, event);
26581 this.setPosition(this.gMapContext.location);
26583 this.fireEvent('initial', this, this.gMapContext.location);
26586 initOverlayView: function()
26590 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26594 _this.fireEvent('OverlayViewDraw', _this);
26599 _this.fireEvent('OverlayViewOnAdd', _this);
26602 onRemove: function()
26604 _this.fireEvent('OverlayViewOnRemove', _this);
26607 show: function(cpx)
26609 _this.fireEvent('OverlayViewShow', _this, cpx);
26614 _this.fireEvent('OverlayViewHide', _this);
26620 fromLatLngToContainerPixel: function(event)
26622 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26625 isApplied: function()
26627 return this.getGmapContext() == false ? false : true;
26630 getGmapContext: function()
26632 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26635 GMapContext: function()
26637 var position = new google.maps.LatLng(this.latitude, this.longitude);
26639 var _map = new google.maps.Map(this.el.dom, {
26642 mapTypeId: this.mapTypeId,
26643 mapTypeControl: this.mapTypeControl,
26644 disableDoubleClickZoom: this.disableDoubleClickZoom,
26645 scrollwheel: this.scrollwheel,
26646 streetViewControl: this.streetViewControl,
26647 locationName: this.locationName,
26648 draggable: this.draggable,
26649 enableAutocomplete: this.enableAutocomplete,
26650 enableReverseGeocode: this.enableReverseGeocode
26653 var _marker = new google.maps.Marker({
26654 position: position,
26656 title: this.markerTitle,
26657 draggable: this.draggable
26664 location: position,
26665 radius: this.radius,
26666 locationName: this.locationName,
26667 addressComponents: {
26668 formatted_address: null,
26669 addressLine1: null,
26670 addressLine2: null,
26672 streetNumber: null,
26676 stateOrProvince: null
26679 domContainer: this.el.dom,
26680 geodecoder: new google.maps.Geocoder()
26684 drawCircle: function(center, radius, options)
26686 if (this.gMapContext.circle != null) {
26687 this.gMapContext.circle.setMap(null);
26691 options = Roo.apply({}, options, {
26692 strokeColor: "#0000FF",
26693 strokeOpacity: .35,
26695 fillColor: "#0000FF",
26699 options.map = this.gMapContext.map;
26700 options.radius = radius;
26701 options.center = center;
26702 this.gMapContext.circle = new google.maps.Circle(options);
26703 return this.gMapContext.circle;
26709 setPosition: function(location)
26711 this.gMapContext.location = location;
26712 this.gMapContext.marker.setPosition(location);
26713 this.gMapContext.map.panTo(location);
26714 this.drawCircle(location, this.gMapContext.radius, {});
26718 if (this.gMapContext.settings.enableReverseGeocode) {
26719 this.gMapContext.geodecoder.geocode({
26720 latLng: this.gMapContext.location
26721 }, function(results, status) {
26723 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26724 _this.gMapContext.locationName = results[0].formatted_address;
26725 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26727 _this.fireEvent('positionchanged', this, location);
26734 this.fireEvent('positionchanged', this, location);
26739 google.maps.event.trigger(this.gMapContext.map, "resize");
26741 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26743 this.fireEvent('resize', this);
26746 setPositionByLatLng: function(latitude, longitude)
26748 this.setPosition(new google.maps.LatLng(latitude, longitude));
26751 getCurrentPosition: function()
26754 latitude: this.gMapContext.location.lat(),
26755 longitude: this.gMapContext.location.lng()
26759 getAddressName: function()
26761 return this.gMapContext.locationName;
26764 getAddressComponents: function()
26766 return this.gMapContext.addressComponents;
26769 address_component_from_google_geocode: function(address_components)
26773 for (var i = 0; i < address_components.length; i++) {
26774 var component = address_components[i];
26775 if (component.types.indexOf("postal_code") >= 0) {
26776 result.postalCode = component.short_name;
26777 } else if (component.types.indexOf("street_number") >= 0) {
26778 result.streetNumber = component.short_name;
26779 } else if (component.types.indexOf("route") >= 0) {
26780 result.streetName = component.short_name;
26781 } else if (component.types.indexOf("neighborhood") >= 0) {
26782 result.city = component.short_name;
26783 } else if (component.types.indexOf("locality") >= 0) {
26784 result.city = component.short_name;
26785 } else if (component.types.indexOf("sublocality") >= 0) {
26786 result.district = component.short_name;
26787 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26788 result.stateOrProvince = component.short_name;
26789 } else if (component.types.indexOf("country") >= 0) {
26790 result.country = component.short_name;
26794 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26795 result.addressLine2 = "";
26799 setZoomLevel: function(zoom)
26801 this.gMapContext.map.setZoom(zoom);
26814 this.fireEvent('show', this);
26825 this.fireEvent('hide', this);
26830 Roo.apply(Roo.bootstrap.LocationPicker, {
26832 OverlayView : function(map, options)
26834 options = options || {};
26848 * @class Roo.bootstrap.Alert
26849 * @extends Roo.bootstrap.Component
26850 * Bootstrap Alert class
26851 * @cfg {String} title The title of alert
26852 * @cfg {String} html The content of alert
26853 * @cfg {String} weight ( success | info | warning | danger )
26854 * @cfg {String} faicon font-awesomeicon
26857 * Create a new alert
26858 * @param {Object} config The config object
26862 Roo.bootstrap.Alert = function(config){
26863 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26867 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26874 getAutoCreate : function()
26883 cls : 'roo-alert-icon'
26888 cls : 'roo-alert-title',
26893 cls : 'roo-alert-text',
26900 cfg.cn[0].cls += ' fa ' + this.faicon;
26904 cfg.cls += ' alert-' + this.weight;
26910 initEvents: function()
26912 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26915 setTitle : function(str)
26917 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26920 setText : function(str)
26922 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26925 setWeight : function(weight)
26928 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26931 this.weight = weight;
26933 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26936 setIcon : function(icon)
26939 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26942 this.faicon = icon;
26944 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26965 * @class Roo.bootstrap.UploadCropbox
26966 * @extends Roo.bootstrap.Component
26967 * Bootstrap UploadCropbox class
26968 * @cfg {String} emptyText show when image has been loaded
26969 * @cfg {String} rotateNotify show when image too small to rotate
26970 * @cfg {Number} errorTimeout default 3000
26971 * @cfg {Number} minWidth default 300
26972 * @cfg {Number} minHeight default 300
26973 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26974 * @cfg {Boolean} isDocument (true|false) default false
26975 * @cfg {String} url action url
26976 * @cfg {String} paramName default 'imageUpload'
26977 * @cfg {String} method default POST
26978 * @cfg {Boolean} loadMask (true|false) default true
26979 * @cfg {Boolean} loadingText default 'Loading...'
26982 * Create a new UploadCropbox
26983 * @param {Object} config The config object
26986 Roo.bootstrap.UploadCropbox = function(config){
26987 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26991 * @event beforeselectfile
26992 * Fire before select file
26993 * @param {Roo.bootstrap.UploadCropbox} this
26995 "beforeselectfile" : true,
26998 * Fire after initEvent
26999 * @param {Roo.bootstrap.UploadCropbox} this
27004 * Fire after initEvent
27005 * @param {Roo.bootstrap.UploadCropbox} this
27006 * @param {String} data
27011 * Fire when preparing the file data
27012 * @param {Roo.bootstrap.UploadCropbox} this
27013 * @param {Object} file
27018 * Fire when get exception
27019 * @param {Roo.bootstrap.UploadCropbox} this
27020 * @param {XMLHttpRequest} xhr
27022 "exception" : true,
27024 * @event beforeloadcanvas
27025 * Fire before load the canvas
27026 * @param {Roo.bootstrap.UploadCropbox} this
27027 * @param {String} src
27029 "beforeloadcanvas" : true,
27032 * Fire when trash image
27033 * @param {Roo.bootstrap.UploadCropbox} this
27038 * Fire when download the image
27039 * @param {Roo.bootstrap.UploadCropbox} this
27043 * @event footerbuttonclick
27044 * Fire when footerbuttonclick
27045 * @param {Roo.bootstrap.UploadCropbox} this
27046 * @param {String} type
27048 "footerbuttonclick" : true,
27052 * @param {Roo.bootstrap.UploadCropbox} this
27057 * Fire when rotate the image
27058 * @param {Roo.bootstrap.UploadCropbox} this
27059 * @param {String} pos
27064 * Fire when inspect the file
27065 * @param {Roo.bootstrap.UploadCropbox} this
27066 * @param {Object} file
27071 * Fire when xhr upload the file
27072 * @param {Roo.bootstrap.UploadCropbox} this
27073 * @param {Object} data
27078 * Fire when arrange the file data
27079 * @param {Roo.bootstrap.UploadCropbox} this
27080 * @param {Object} formData
27085 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27088 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27090 emptyText : 'Click to upload image',
27091 rotateNotify : 'Image is too small to rotate',
27092 errorTimeout : 3000,
27106 cropType : 'image/jpeg',
27108 canvasLoaded : false,
27109 isDocument : false,
27111 paramName : 'imageUpload',
27113 loadingText : 'Loading...',
27116 getAutoCreate : function()
27120 cls : 'roo-upload-cropbox',
27124 cls : 'roo-upload-cropbox-selector',
27129 cls : 'roo-upload-cropbox-body',
27130 style : 'cursor:pointer',
27134 cls : 'roo-upload-cropbox-preview'
27138 cls : 'roo-upload-cropbox-thumb'
27142 cls : 'roo-upload-cropbox-empty-notify',
27143 html : this.emptyText
27147 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27148 html : this.rotateNotify
27154 cls : 'roo-upload-cropbox-footer',
27157 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27167 onRender : function(ct, position)
27169 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27171 if (this.buttons.length) {
27173 Roo.each(this.buttons, function(bb) {
27175 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27177 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27183 this.maskEl = this.el;
27187 initEvents : function()
27189 this.urlAPI = (window.createObjectURL && window) ||
27190 (window.URL && URL.revokeObjectURL && URL) ||
27191 (window.webkitURL && webkitURL);
27193 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27194 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27196 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27197 this.selectorEl.hide();
27199 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27200 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27202 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27203 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27204 this.thumbEl.hide();
27206 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27207 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27209 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27210 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27211 this.errorEl.hide();
27213 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27214 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27215 this.footerEl.hide();
27217 this.setThumbBoxSize();
27223 this.fireEvent('initial', this);
27230 window.addEventListener("resize", function() { _this.resize(); } );
27232 this.bodyEl.on('click', this.beforeSelectFile, this);
27235 this.bodyEl.on('touchstart', this.onTouchStart, this);
27236 this.bodyEl.on('touchmove', this.onTouchMove, this);
27237 this.bodyEl.on('touchend', this.onTouchEnd, this);
27241 this.bodyEl.on('mousedown', this.onMouseDown, this);
27242 this.bodyEl.on('mousemove', this.onMouseMove, this);
27243 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27244 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27245 Roo.get(document).on('mouseup', this.onMouseUp, this);
27248 this.selectorEl.on('change', this.onFileSelected, this);
27254 this.baseScale = 1;
27256 this.baseRotate = 1;
27257 this.dragable = false;
27258 this.pinching = false;
27261 this.cropData = false;
27262 this.notifyEl.dom.innerHTML = this.emptyText;
27264 this.selectorEl.dom.value = '';
27268 resize : function()
27270 if(this.fireEvent('resize', this) != false){
27271 this.setThumbBoxPosition();
27272 this.setCanvasPosition();
27276 onFooterButtonClick : function(e, el, o, type)
27279 case 'rotate-left' :
27280 this.onRotateLeft(e);
27282 case 'rotate-right' :
27283 this.onRotateRight(e);
27286 this.beforeSelectFile(e);
27301 this.fireEvent('footerbuttonclick', this, type);
27304 beforeSelectFile : function(e)
27306 e.preventDefault();
27308 if(this.fireEvent('beforeselectfile', this) != false){
27309 this.selectorEl.dom.click();
27313 onFileSelected : function(e)
27315 e.preventDefault();
27317 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27321 var file = this.selectorEl.dom.files[0];
27323 if(this.fireEvent('inspect', this, file) != false){
27324 this.prepare(file);
27329 trash : function(e)
27331 this.fireEvent('trash', this);
27334 download : function(e)
27336 this.fireEvent('download', this);
27339 loadCanvas : function(src)
27341 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27345 this.imageEl = document.createElement('img');
27349 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27351 this.imageEl.src = src;
27355 onLoadCanvas : function()
27357 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27358 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27360 this.bodyEl.un('click', this.beforeSelectFile, this);
27362 this.notifyEl.hide();
27363 this.thumbEl.show();
27364 this.footerEl.show();
27366 this.baseRotateLevel();
27368 if(this.isDocument){
27369 this.setThumbBoxSize();
27372 this.setThumbBoxPosition();
27374 this.baseScaleLevel();
27380 this.canvasLoaded = true;
27383 this.maskEl.unmask();
27388 setCanvasPosition : function()
27390 if(!this.canvasEl){
27394 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27395 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27397 this.previewEl.setLeft(pw);
27398 this.previewEl.setTop(ph);
27402 onMouseDown : function(e)
27406 this.dragable = true;
27407 this.pinching = false;
27409 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27410 this.dragable = false;
27414 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27415 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27419 onMouseMove : function(e)
27423 if(!this.canvasLoaded){
27427 if (!this.dragable){
27431 var minX = Math.ceil(this.thumbEl.getLeft(true));
27432 var minY = Math.ceil(this.thumbEl.getTop(true));
27434 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27435 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27437 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27438 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27440 x = x - this.mouseX;
27441 y = y - this.mouseY;
27443 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27444 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27446 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27447 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27449 this.previewEl.setLeft(bgX);
27450 this.previewEl.setTop(bgY);
27452 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27453 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27456 onMouseUp : function(e)
27460 this.dragable = false;
27463 onMouseWheel : function(e)
27467 this.startScale = this.scale;
27469 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27471 if(!this.zoomable()){
27472 this.scale = this.startScale;
27481 zoomable : function()
27483 var minScale = this.thumbEl.getWidth() / this.minWidth;
27485 if(this.minWidth < this.minHeight){
27486 minScale = this.thumbEl.getHeight() / this.minHeight;
27489 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27490 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27494 (this.rotate == 0 || this.rotate == 180) &&
27496 width > this.imageEl.OriginWidth ||
27497 height > this.imageEl.OriginHeight ||
27498 (width < this.minWidth && height < this.minHeight)
27506 (this.rotate == 90 || this.rotate == 270) &&
27508 width > this.imageEl.OriginWidth ||
27509 height > this.imageEl.OriginHeight ||
27510 (width < this.minHeight && height < this.minWidth)
27517 !this.isDocument &&
27518 (this.rotate == 0 || this.rotate == 180) &&
27520 width < this.minWidth ||
27521 width > this.imageEl.OriginWidth ||
27522 height < this.minHeight ||
27523 height > this.imageEl.OriginHeight
27530 !this.isDocument &&
27531 (this.rotate == 90 || this.rotate == 270) &&
27533 width < this.minHeight ||
27534 width > this.imageEl.OriginWidth ||
27535 height < this.minWidth ||
27536 height > this.imageEl.OriginHeight
27546 onRotateLeft : function(e)
27548 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27550 var minScale = this.thumbEl.getWidth() / this.minWidth;
27552 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27553 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27555 this.startScale = this.scale;
27557 while (this.getScaleLevel() < minScale){
27559 this.scale = this.scale + 1;
27561 if(!this.zoomable()){
27566 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27567 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27572 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27579 this.scale = this.startScale;
27581 this.onRotateFail();
27586 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27588 if(this.isDocument){
27589 this.setThumbBoxSize();
27590 this.setThumbBoxPosition();
27591 this.setCanvasPosition();
27596 this.fireEvent('rotate', this, 'left');
27600 onRotateRight : function(e)
27602 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27604 var minScale = this.thumbEl.getWidth() / this.minWidth;
27606 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27607 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27609 this.startScale = this.scale;
27611 while (this.getScaleLevel() < minScale){
27613 this.scale = this.scale + 1;
27615 if(!this.zoomable()){
27620 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27621 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27626 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27633 this.scale = this.startScale;
27635 this.onRotateFail();
27640 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27642 if(this.isDocument){
27643 this.setThumbBoxSize();
27644 this.setThumbBoxPosition();
27645 this.setCanvasPosition();
27650 this.fireEvent('rotate', this, 'right');
27653 onRotateFail : function()
27655 this.errorEl.show(true);
27659 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27664 this.previewEl.dom.innerHTML = '';
27666 var canvasEl = document.createElement("canvas");
27668 var contextEl = canvasEl.getContext("2d");
27670 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27671 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27672 var center = this.imageEl.OriginWidth / 2;
27674 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27675 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27676 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27677 center = this.imageEl.OriginHeight / 2;
27680 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27682 contextEl.translate(center, center);
27683 contextEl.rotate(this.rotate * Math.PI / 180);
27685 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27687 this.canvasEl = document.createElement("canvas");
27689 this.contextEl = this.canvasEl.getContext("2d");
27691 switch (this.rotate) {
27694 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27695 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27697 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27702 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27703 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27705 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27706 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);
27710 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27715 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27716 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27718 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27719 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);
27723 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);
27728 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27729 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27731 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27732 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27736 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);
27743 this.previewEl.appendChild(this.canvasEl);
27745 this.setCanvasPosition();
27750 if(!this.canvasLoaded){
27754 var imageCanvas = document.createElement("canvas");
27756 var imageContext = imageCanvas.getContext("2d");
27758 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27759 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27761 var center = imageCanvas.width / 2;
27763 imageContext.translate(center, center);
27765 imageContext.rotate(this.rotate * Math.PI / 180);
27767 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27769 var canvas = document.createElement("canvas");
27771 var context = canvas.getContext("2d");
27773 canvas.width = this.minWidth;
27774 canvas.height = this.minHeight;
27776 switch (this.rotate) {
27779 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27780 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27782 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27783 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27785 var targetWidth = this.minWidth - 2 * x;
27786 var targetHeight = this.minHeight - 2 * y;
27790 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27791 scale = targetWidth / width;
27794 if(x > 0 && y == 0){
27795 scale = targetHeight / height;
27798 if(x > 0 && y > 0){
27799 scale = targetWidth / width;
27801 if(width < height){
27802 scale = targetHeight / height;
27806 context.scale(scale, scale);
27808 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27809 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27811 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27812 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27814 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27819 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27820 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27822 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27823 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27825 var targetWidth = this.minWidth - 2 * x;
27826 var targetHeight = this.minHeight - 2 * y;
27830 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27831 scale = targetWidth / width;
27834 if(x > 0 && y == 0){
27835 scale = targetHeight / height;
27838 if(x > 0 && y > 0){
27839 scale = targetWidth / width;
27841 if(width < height){
27842 scale = targetHeight / height;
27846 context.scale(scale, scale);
27848 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27849 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27851 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27852 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27854 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27856 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27861 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27862 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27864 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27865 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27867 var targetWidth = this.minWidth - 2 * x;
27868 var targetHeight = this.minHeight - 2 * y;
27872 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27873 scale = targetWidth / width;
27876 if(x > 0 && y == 0){
27877 scale = targetHeight / height;
27880 if(x > 0 && y > 0){
27881 scale = targetWidth / width;
27883 if(width < height){
27884 scale = targetHeight / height;
27888 context.scale(scale, scale);
27890 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27891 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27893 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27894 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27896 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27897 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27899 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27904 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27905 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27907 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27908 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27910 var targetWidth = this.minWidth - 2 * x;
27911 var targetHeight = this.minHeight - 2 * y;
27915 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27916 scale = targetWidth / width;
27919 if(x > 0 && y == 0){
27920 scale = targetHeight / height;
27923 if(x > 0 && y > 0){
27924 scale = targetWidth / width;
27926 if(width < height){
27927 scale = targetHeight / height;
27931 context.scale(scale, scale);
27933 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27934 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27936 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27937 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27939 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27941 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27948 this.cropData = canvas.toDataURL(this.cropType);
27950 if(this.fireEvent('crop', this, this.cropData) !== false){
27951 this.process(this.file, this.cropData);
27958 setThumbBoxSize : function()
27962 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27963 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27964 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27966 this.minWidth = width;
27967 this.minHeight = height;
27969 if(this.rotate == 90 || this.rotate == 270){
27970 this.minWidth = height;
27971 this.minHeight = width;
27976 width = Math.ceil(this.minWidth * height / this.minHeight);
27978 if(this.minWidth > this.minHeight){
27980 height = Math.ceil(this.minHeight * width / this.minWidth);
27983 this.thumbEl.setStyle({
27984 width : width + 'px',
27985 height : height + 'px'
27992 setThumbBoxPosition : function()
27994 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27995 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27997 this.thumbEl.setLeft(x);
27998 this.thumbEl.setTop(y);
28002 baseRotateLevel : function()
28004 this.baseRotate = 1;
28007 typeof(this.exif) != 'undefined' &&
28008 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28009 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28011 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28014 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28018 baseScaleLevel : function()
28022 if(this.isDocument){
28024 if(this.baseRotate == 6 || this.baseRotate == 8){
28026 height = this.thumbEl.getHeight();
28027 this.baseScale = height / this.imageEl.OriginWidth;
28029 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28030 width = this.thumbEl.getWidth();
28031 this.baseScale = width / this.imageEl.OriginHeight;
28037 height = this.thumbEl.getHeight();
28038 this.baseScale = height / this.imageEl.OriginHeight;
28040 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28041 width = this.thumbEl.getWidth();
28042 this.baseScale = width / this.imageEl.OriginWidth;
28048 if(this.baseRotate == 6 || this.baseRotate == 8){
28050 width = this.thumbEl.getHeight();
28051 this.baseScale = width / this.imageEl.OriginHeight;
28053 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28054 height = this.thumbEl.getWidth();
28055 this.baseScale = height / this.imageEl.OriginHeight;
28058 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28059 height = this.thumbEl.getWidth();
28060 this.baseScale = height / this.imageEl.OriginHeight;
28062 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28063 width = this.thumbEl.getHeight();
28064 this.baseScale = width / this.imageEl.OriginWidth;
28071 width = this.thumbEl.getWidth();
28072 this.baseScale = width / this.imageEl.OriginWidth;
28074 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28075 height = this.thumbEl.getHeight();
28076 this.baseScale = height / this.imageEl.OriginHeight;
28079 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28081 height = this.thumbEl.getHeight();
28082 this.baseScale = height / this.imageEl.OriginHeight;
28084 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28085 width = this.thumbEl.getWidth();
28086 this.baseScale = width / this.imageEl.OriginWidth;
28094 getScaleLevel : function()
28096 return this.baseScale * Math.pow(1.1, this.scale);
28099 onTouchStart : function(e)
28101 if(!this.canvasLoaded){
28102 this.beforeSelectFile(e);
28106 var touches = e.browserEvent.touches;
28112 if(touches.length == 1){
28113 this.onMouseDown(e);
28117 if(touches.length != 2){
28123 for(var i = 0, finger; finger = touches[i]; i++){
28124 coords.push(finger.pageX, finger.pageY);
28127 var x = Math.pow(coords[0] - coords[2], 2);
28128 var y = Math.pow(coords[1] - coords[3], 2);
28130 this.startDistance = Math.sqrt(x + y);
28132 this.startScale = this.scale;
28134 this.pinching = true;
28135 this.dragable = false;
28139 onTouchMove : function(e)
28141 if(!this.pinching && !this.dragable){
28145 var touches = e.browserEvent.touches;
28152 this.onMouseMove(e);
28158 for(var i = 0, finger; finger = touches[i]; i++){
28159 coords.push(finger.pageX, finger.pageY);
28162 var x = Math.pow(coords[0] - coords[2], 2);
28163 var y = Math.pow(coords[1] - coords[3], 2);
28165 this.endDistance = Math.sqrt(x + y);
28167 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28169 if(!this.zoomable()){
28170 this.scale = this.startScale;
28178 onTouchEnd : function(e)
28180 this.pinching = false;
28181 this.dragable = false;
28185 process : function(file, crop)
28188 this.maskEl.mask(this.loadingText);
28191 this.xhr = new XMLHttpRequest();
28193 file.xhr = this.xhr;
28195 this.xhr.open(this.method, this.url, true);
28198 "Accept": "application/json",
28199 "Cache-Control": "no-cache",
28200 "X-Requested-With": "XMLHttpRequest"
28203 for (var headerName in headers) {
28204 var headerValue = headers[headerName];
28206 this.xhr.setRequestHeader(headerName, headerValue);
28212 this.xhr.onload = function()
28214 _this.xhrOnLoad(_this.xhr);
28217 this.xhr.onerror = function()
28219 _this.xhrOnError(_this.xhr);
28222 var formData = new FormData();
28224 formData.append('returnHTML', 'NO');
28227 formData.append('crop', crop);
28230 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28231 formData.append(this.paramName, file, file.name);
28234 if(typeof(file.filename) != 'undefined'){
28235 formData.append('filename', file.filename);
28238 if(typeof(file.mimetype) != 'undefined'){
28239 formData.append('mimetype', file.mimetype);
28242 if(this.fireEvent('arrange', this, formData) != false){
28243 this.xhr.send(formData);
28247 xhrOnLoad : function(xhr)
28250 this.maskEl.unmask();
28253 if (xhr.readyState !== 4) {
28254 this.fireEvent('exception', this, xhr);
28258 var response = Roo.decode(xhr.responseText);
28260 if(!response.success){
28261 this.fireEvent('exception', this, xhr);
28265 var response = Roo.decode(xhr.responseText);
28267 this.fireEvent('upload', this, response);
28271 xhrOnError : function()
28274 this.maskEl.unmask();
28277 Roo.log('xhr on error');
28279 var response = Roo.decode(xhr.responseText);
28285 prepare : function(file)
28288 this.maskEl.mask(this.loadingText);
28294 if(typeof(file) === 'string'){
28295 this.loadCanvas(file);
28299 if(!file || !this.urlAPI){
28304 this.cropType = file.type;
28308 if(this.fireEvent('prepare', this, this.file) != false){
28310 var reader = new FileReader();
28312 reader.onload = function (e) {
28313 if (e.target.error) {
28314 Roo.log(e.target.error);
28318 var buffer = e.target.result,
28319 dataView = new DataView(buffer),
28321 maxOffset = dataView.byteLength - 4,
28325 if (dataView.getUint16(0) === 0xffd8) {
28326 while (offset < maxOffset) {
28327 markerBytes = dataView.getUint16(offset);
28329 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28330 markerLength = dataView.getUint16(offset + 2) + 2;
28331 if (offset + markerLength > dataView.byteLength) {
28332 Roo.log('Invalid meta data: Invalid segment size.');
28336 if(markerBytes == 0xffe1){
28337 _this.parseExifData(
28344 offset += markerLength;
28354 var url = _this.urlAPI.createObjectURL(_this.file);
28356 _this.loadCanvas(url);
28361 reader.readAsArrayBuffer(this.file);
28367 parseExifData : function(dataView, offset, length)
28369 var tiffOffset = offset + 10,
28373 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28374 // No Exif data, might be XMP data instead
28378 // Check for the ASCII code for "Exif" (0x45786966):
28379 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28380 // No Exif data, might be XMP data instead
28383 if (tiffOffset + 8 > dataView.byteLength) {
28384 Roo.log('Invalid Exif data: Invalid segment size.');
28387 // Check for the two null bytes:
28388 if (dataView.getUint16(offset + 8) !== 0x0000) {
28389 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28392 // Check the byte alignment:
28393 switch (dataView.getUint16(tiffOffset)) {
28395 littleEndian = true;
28398 littleEndian = false;
28401 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28404 // Check for the TIFF tag marker (0x002A):
28405 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28406 Roo.log('Invalid Exif data: Missing TIFF marker.');
28409 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28410 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28412 this.parseExifTags(
28415 tiffOffset + dirOffset,
28420 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28425 if (dirOffset + 6 > dataView.byteLength) {
28426 Roo.log('Invalid Exif data: Invalid directory offset.');
28429 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28430 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28431 if (dirEndOffset + 4 > dataView.byteLength) {
28432 Roo.log('Invalid Exif data: Invalid directory size.');
28435 for (i = 0; i < tagsNumber; i += 1) {
28439 dirOffset + 2 + 12 * i, // tag offset
28443 // Return the offset to the next directory:
28444 return dataView.getUint32(dirEndOffset, littleEndian);
28447 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28449 var tag = dataView.getUint16(offset, littleEndian);
28451 this.exif[tag] = this.getExifValue(
28455 dataView.getUint16(offset + 2, littleEndian), // tag type
28456 dataView.getUint32(offset + 4, littleEndian), // tag length
28461 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28463 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28472 Roo.log('Invalid Exif data: Invalid tag type.');
28476 tagSize = tagType.size * length;
28477 // Determine if the value is contained in the dataOffset bytes,
28478 // or if the value at the dataOffset is a pointer to the actual data:
28479 dataOffset = tagSize > 4 ?
28480 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28481 if (dataOffset + tagSize > dataView.byteLength) {
28482 Roo.log('Invalid Exif data: Invalid data offset.');
28485 if (length === 1) {
28486 return tagType.getValue(dataView, dataOffset, littleEndian);
28489 for (i = 0; i < length; i += 1) {
28490 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28493 if (tagType.ascii) {
28495 // Concatenate the chars:
28496 for (i = 0; i < values.length; i += 1) {
28498 // Ignore the terminating NULL byte(s):
28499 if (c === '\u0000') {
28511 Roo.apply(Roo.bootstrap.UploadCropbox, {
28513 'Orientation': 0x0112
28517 1: 0, //'top-left',
28519 3: 180, //'bottom-right',
28520 // 4: 'bottom-left',
28522 6: 90, //'right-top',
28523 // 7: 'right-bottom',
28524 8: 270 //'left-bottom'
28528 // byte, 8-bit unsigned int:
28530 getValue: function (dataView, dataOffset) {
28531 return dataView.getUint8(dataOffset);
28535 // ascii, 8-bit byte:
28537 getValue: function (dataView, dataOffset) {
28538 return String.fromCharCode(dataView.getUint8(dataOffset));
28543 // short, 16 bit int:
28545 getValue: function (dataView, dataOffset, littleEndian) {
28546 return dataView.getUint16(dataOffset, littleEndian);
28550 // long, 32 bit int:
28552 getValue: function (dataView, dataOffset, littleEndian) {
28553 return dataView.getUint32(dataOffset, littleEndian);
28557 // rational = two long values, first is numerator, second is denominator:
28559 getValue: function (dataView, dataOffset, littleEndian) {
28560 return dataView.getUint32(dataOffset, littleEndian) /
28561 dataView.getUint32(dataOffset + 4, littleEndian);
28565 // slong, 32 bit signed int:
28567 getValue: function (dataView, dataOffset, littleEndian) {
28568 return dataView.getInt32(dataOffset, littleEndian);
28572 // srational, two slongs, first is numerator, second is denominator:
28574 getValue: function (dataView, dataOffset, littleEndian) {
28575 return dataView.getInt32(dataOffset, littleEndian) /
28576 dataView.getInt32(dataOffset + 4, littleEndian);
28586 cls : 'btn-group roo-upload-cropbox-rotate-left',
28587 action : 'rotate-left',
28591 cls : 'btn btn-default',
28592 html : '<i class="fa fa-undo"></i>'
28598 cls : 'btn-group roo-upload-cropbox-picture',
28599 action : 'picture',
28603 cls : 'btn btn-default',
28604 html : '<i class="fa fa-picture-o"></i>'
28610 cls : 'btn-group roo-upload-cropbox-rotate-right',
28611 action : 'rotate-right',
28615 cls : 'btn btn-default',
28616 html : '<i class="fa fa-repeat"></i>'
28624 cls : 'btn-group roo-upload-cropbox-rotate-left',
28625 action : 'rotate-left',
28629 cls : 'btn btn-default',
28630 html : '<i class="fa fa-undo"></i>'
28636 cls : 'btn-group roo-upload-cropbox-download',
28637 action : 'download',
28641 cls : 'btn btn-default',
28642 html : '<i class="fa fa-download"></i>'
28648 cls : 'btn-group roo-upload-cropbox-crop',
28653 cls : 'btn btn-default',
28654 html : '<i class="fa fa-crop"></i>'
28660 cls : 'btn-group roo-upload-cropbox-trash',
28665 cls : 'btn btn-default',
28666 html : '<i class="fa fa-trash"></i>'
28672 cls : 'btn-group roo-upload-cropbox-rotate-right',
28673 action : 'rotate-right',
28677 cls : 'btn btn-default',
28678 html : '<i class="fa fa-repeat"></i>'
28686 cls : 'btn-group roo-upload-cropbox-rotate-left',
28687 action : 'rotate-left',
28691 cls : 'btn btn-default',
28692 html : '<i class="fa fa-undo"></i>'
28698 cls : 'btn-group roo-upload-cropbox-rotate-right',
28699 action : 'rotate-right',
28703 cls : 'btn btn-default',
28704 html : '<i class="fa fa-repeat"></i>'
28717 * @class Roo.bootstrap.DocumentManager
28718 * @extends Roo.bootstrap.Component
28719 * Bootstrap DocumentManager class
28720 * @cfg {String} paramName default 'imageUpload'
28721 * @cfg {String} toolTipName default 'filename'
28722 * @cfg {String} method default POST
28723 * @cfg {String} url action url
28724 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28725 * @cfg {Boolean} multiple multiple upload default true
28726 * @cfg {Number} thumbSize default 300
28727 * @cfg {String} fieldLabel
28728 * @cfg {Number} labelWidth default 4
28729 * @cfg {String} labelAlign (left|top) default left
28730 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28731 * @cfg {Number} labellg set the width of label (1-12)
28732 * @cfg {Number} labelmd set the width of label (1-12)
28733 * @cfg {Number} labelsm set the width of label (1-12)
28734 * @cfg {Number} labelxs set the width of label (1-12)
28737 * Create a new DocumentManager
28738 * @param {Object} config The config object
28741 Roo.bootstrap.DocumentManager = function(config){
28742 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28745 this.delegates = [];
28750 * Fire when initial the DocumentManager
28751 * @param {Roo.bootstrap.DocumentManager} this
28756 * inspect selected file
28757 * @param {Roo.bootstrap.DocumentManager} this
28758 * @param {File} file
28763 * Fire when xhr load exception
28764 * @param {Roo.bootstrap.DocumentManager} this
28765 * @param {XMLHttpRequest} xhr
28767 "exception" : true,
28769 * @event afterupload
28770 * Fire when xhr load exception
28771 * @param {Roo.bootstrap.DocumentManager} this
28772 * @param {XMLHttpRequest} xhr
28774 "afterupload" : true,
28777 * prepare the form data
28778 * @param {Roo.bootstrap.DocumentManager} this
28779 * @param {Object} formData
28784 * Fire when remove the file
28785 * @param {Roo.bootstrap.DocumentManager} this
28786 * @param {Object} file
28791 * Fire after refresh the file
28792 * @param {Roo.bootstrap.DocumentManager} this
28797 * Fire after click the image
28798 * @param {Roo.bootstrap.DocumentManager} this
28799 * @param {Object} file
28804 * Fire when upload a image and editable set to true
28805 * @param {Roo.bootstrap.DocumentManager} this
28806 * @param {Object} file
28810 * @event beforeselectfile
28811 * Fire before select file
28812 * @param {Roo.bootstrap.DocumentManager} this
28814 "beforeselectfile" : true,
28817 * Fire before process file
28818 * @param {Roo.bootstrap.DocumentManager} this
28819 * @param {Object} file
28823 * @event previewrendered
28824 * Fire when preview rendered
28825 * @param {Roo.bootstrap.DocumentManager} this
28826 * @param {Object} file
28828 "previewrendered" : true,
28831 "previewResize" : true
28836 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28845 paramName : 'imageUpload',
28846 toolTipName : 'filename',
28849 labelAlign : 'left',
28859 getAutoCreate : function()
28861 var managerWidget = {
28863 cls : 'roo-document-manager',
28867 cls : 'roo-document-manager-selector',
28872 cls : 'roo-document-manager-uploader',
28876 cls : 'roo-document-manager-upload-btn',
28877 html : '<i class="fa fa-plus"></i>'
28888 cls : 'column col-md-12',
28893 if(this.fieldLabel.length){
28898 cls : 'column col-md-12',
28899 html : this.fieldLabel
28903 cls : 'column col-md-12',
28908 if(this.labelAlign == 'left'){
28913 html : this.fieldLabel
28922 if(this.labelWidth > 12){
28923 content[0].style = "width: " + this.labelWidth + 'px';
28926 if(this.labelWidth < 13 && this.labelmd == 0){
28927 this.labelmd = this.labelWidth;
28930 if(this.labellg > 0){
28931 content[0].cls += ' col-lg-' + this.labellg;
28932 content[1].cls += ' col-lg-' + (12 - this.labellg);
28935 if(this.labelmd > 0){
28936 content[0].cls += ' col-md-' + this.labelmd;
28937 content[1].cls += ' col-md-' + (12 - this.labelmd);
28940 if(this.labelsm > 0){
28941 content[0].cls += ' col-sm-' + this.labelsm;
28942 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28945 if(this.labelxs > 0){
28946 content[0].cls += ' col-xs-' + this.labelxs;
28947 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28955 cls : 'row clearfix',
28963 initEvents : function()
28965 this.managerEl = this.el.select('.roo-document-manager', true).first();
28966 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28968 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28969 this.selectorEl.hide();
28972 this.selectorEl.attr('multiple', 'multiple');
28975 this.selectorEl.on('change', this.onFileSelected, this);
28977 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28978 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28980 this.uploader.on('click', this.onUploaderClick, this);
28982 this.renderProgressDialog();
28986 window.addEventListener("resize", function() { _this.refresh(); } );
28988 this.fireEvent('initial', this);
28991 renderProgressDialog : function()
28995 this.progressDialog = new Roo.bootstrap.Modal({
28996 cls : 'roo-document-manager-progress-dialog',
28997 allow_close : false,
29007 btnclick : function() {
29008 _this.uploadCancel();
29014 this.progressDialog.render(Roo.get(document.body));
29016 this.progress = new Roo.bootstrap.Progress({
29017 cls : 'roo-document-manager-progress',
29022 this.progress.render(this.progressDialog.getChildContainer());
29024 this.progressBar = new Roo.bootstrap.ProgressBar({
29025 cls : 'roo-document-manager-progress-bar',
29028 aria_valuemax : 12,
29032 this.progressBar.render(this.progress.getChildContainer());
29035 onUploaderClick : function(e)
29037 e.preventDefault();
29039 if(this.fireEvent('beforeselectfile', this) != false){
29040 this.selectorEl.dom.click();
29045 onFileSelected : function(e)
29047 e.preventDefault();
29049 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29053 Roo.each(this.selectorEl.dom.files, function(file){
29054 if(this.fireEvent('inspect', this, file) != false){
29055 this.files.push(file);
29065 this.selectorEl.dom.value = '';
29067 if(!this.files || !this.files.length){
29071 if(this.boxes > 0 && this.files.length > this.boxes){
29072 this.files = this.files.slice(0, this.boxes);
29075 this.uploader.show();
29077 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29078 this.uploader.hide();
29087 Roo.each(this.files, function(file){
29089 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29090 var f = this.renderPreview(file);
29095 if(file.type.indexOf('image') != -1){
29096 this.delegates.push(
29098 _this.process(file);
29099 }).createDelegate(this)
29107 _this.process(file);
29108 }).createDelegate(this)
29113 this.files = files;
29115 this.delegates = this.delegates.concat(docs);
29117 if(!this.delegates.length){
29122 this.progressBar.aria_valuemax = this.delegates.length;
29129 arrange : function()
29131 if(!this.delegates.length){
29132 this.progressDialog.hide();
29137 var delegate = this.delegates.shift();
29139 this.progressDialog.show();
29141 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29143 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29148 refresh : function()
29150 this.uploader.show();
29152 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29153 this.uploader.hide();
29156 Roo.isTouch ? this.closable(false) : this.closable(true);
29158 this.fireEvent('refresh', this);
29161 onRemove : function(e, el, o)
29163 e.preventDefault();
29165 this.fireEvent('remove', this, o);
29169 remove : function(o)
29173 Roo.each(this.files, function(file){
29174 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29183 this.files = files;
29190 Roo.each(this.files, function(file){
29195 file.target.remove();
29204 onClick : function(e, el, o)
29206 e.preventDefault();
29208 this.fireEvent('click', this, o);
29212 closable : function(closable)
29214 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29216 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29228 xhrOnLoad : function(xhr)
29230 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29234 if (xhr.readyState !== 4) {
29236 this.fireEvent('exception', this, xhr);
29240 var response = Roo.decode(xhr.responseText);
29242 if(!response.success){
29244 this.fireEvent('exception', this, xhr);
29248 var file = this.renderPreview(response.data);
29250 this.files.push(file);
29254 this.fireEvent('afterupload', this, xhr);
29258 xhrOnError : function(xhr)
29260 Roo.log('xhr on error');
29262 var response = Roo.decode(xhr.responseText);
29269 process : function(file)
29271 if(this.fireEvent('process', this, file) !== false){
29272 if(this.editable && file.type.indexOf('image') != -1){
29273 this.fireEvent('edit', this, file);
29277 this.uploadStart(file, false);
29284 uploadStart : function(file, crop)
29286 this.xhr = new XMLHttpRequest();
29288 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29293 file.xhr = this.xhr;
29295 this.managerEl.createChild({
29297 cls : 'roo-document-manager-loading',
29301 tooltip : file.name,
29302 cls : 'roo-document-manager-thumb',
29303 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29309 this.xhr.open(this.method, this.url, true);
29312 "Accept": "application/json",
29313 "Cache-Control": "no-cache",
29314 "X-Requested-With": "XMLHttpRequest"
29317 for (var headerName in headers) {
29318 var headerValue = headers[headerName];
29320 this.xhr.setRequestHeader(headerName, headerValue);
29326 this.xhr.onload = function()
29328 _this.xhrOnLoad(_this.xhr);
29331 this.xhr.onerror = function()
29333 _this.xhrOnError(_this.xhr);
29336 var formData = new FormData();
29338 formData.append('returnHTML', 'NO');
29341 formData.append('crop', crop);
29344 formData.append(this.paramName, file, file.name);
29351 if(this.fireEvent('prepare', this, formData, options) != false){
29353 if(options.manually){
29357 this.xhr.send(formData);
29361 this.uploadCancel();
29364 uploadCancel : function()
29370 this.delegates = [];
29372 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29379 renderPreview : function(file)
29381 if(typeof(file.target) != 'undefined' && file.target){
29385 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29387 var previewEl = this.managerEl.createChild({
29389 cls : 'roo-document-manager-preview',
29393 tooltip : file[this.toolTipName],
29394 cls : 'roo-document-manager-thumb',
29395 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29400 html : '<i class="fa fa-times-circle"></i>'
29405 var close = previewEl.select('button.close', true).first();
29407 close.on('click', this.onRemove, this, file);
29409 file.target = previewEl;
29411 var image = previewEl.select('img', true).first();
29415 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29417 image.on('click', this.onClick, this, file);
29419 this.fireEvent('previewrendered', this, file);
29425 onPreviewLoad : function(file, image)
29427 if(typeof(file.target) == 'undefined' || !file.target){
29431 var width = image.dom.naturalWidth || image.dom.width;
29432 var height = image.dom.naturalHeight || image.dom.height;
29434 if(!this.previewResize) {
29438 if(width > height){
29439 file.target.addClass('wide');
29443 file.target.addClass('tall');
29448 uploadFromSource : function(file, crop)
29450 this.xhr = new XMLHttpRequest();
29452 this.managerEl.createChild({
29454 cls : 'roo-document-manager-loading',
29458 tooltip : file.name,
29459 cls : 'roo-document-manager-thumb',
29460 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29466 this.xhr.open(this.method, this.url, true);
29469 "Accept": "application/json",
29470 "Cache-Control": "no-cache",
29471 "X-Requested-With": "XMLHttpRequest"
29474 for (var headerName in headers) {
29475 var headerValue = headers[headerName];
29477 this.xhr.setRequestHeader(headerName, headerValue);
29483 this.xhr.onload = function()
29485 _this.xhrOnLoad(_this.xhr);
29488 this.xhr.onerror = function()
29490 _this.xhrOnError(_this.xhr);
29493 var formData = new FormData();
29495 formData.append('returnHTML', 'NO');
29497 formData.append('crop', crop);
29499 if(typeof(file.filename) != 'undefined'){
29500 formData.append('filename', file.filename);
29503 if(typeof(file.mimetype) != 'undefined'){
29504 formData.append('mimetype', file.mimetype);
29509 if(this.fireEvent('prepare', this, formData) != false){
29510 this.xhr.send(formData);
29520 * @class Roo.bootstrap.DocumentViewer
29521 * @extends Roo.bootstrap.Component
29522 * Bootstrap DocumentViewer class
29523 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29524 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29527 * Create a new DocumentViewer
29528 * @param {Object} config The config object
29531 Roo.bootstrap.DocumentViewer = function(config){
29532 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29537 * Fire after initEvent
29538 * @param {Roo.bootstrap.DocumentViewer} this
29544 * @param {Roo.bootstrap.DocumentViewer} this
29549 * Fire after download button
29550 * @param {Roo.bootstrap.DocumentViewer} this
29555 * Fire after trash button
29556 * @param {Roo.bootstrap.DocumentViewer} this
29563 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29565 showDownload : true,
29569 getAutoCreate : function()
29573 cls : 'roo-document-viewer',
29577 cls : 'roo-document-viewer-body',
29581 cls : 'roo-document-viewer-thumb',
29585 cls : 'roo-document-viewer-image'
29593 cls : 'roo-document-viewer-footer',
29596 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29600 cls : 'btn-group roo-document-viewer-download',
29604 cls : 'btn btn-default',
29605 html : '<i class="fa fa-download"></i>'
29611 cls : 'btn-group roo-document-viewer-trash',
29615 cls : 'btn btn-default',
29616 html : '<i class="fa fa-trash"></i>'
29629 initEvents : function()
29631 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29632 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29634 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29635 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29637 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29638 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29640 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29641 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29643 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29644 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29646 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29647 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29649 this.bodyEl.on('click', this.onClick, this);
29650 this.downloadBtn.on('click', this.onDownload, this);
29651 this.trashBtn.on('click', this.onTrash, this);
29653 this.downloadBtn.hide();
29654 this.trashBtn.hide();
29656 if(this.showDownload){
29657 this.downloadBtn.show();
29660 if(this.showTrash){
29661 this.trashBtn.show();
29664 if(!this.showDownload && !this.showTrash) {
29665 this.footerEl.hide();
29670 initial : function()
29672 this.fireEvent('initial', this);
29676 onClick : function(e)
29678 e.preventDefault();
29680 this.fireEvent('click', this);
29683 onDownload : function(e)
29685 e.preventDefault();
29687 this.fireEvent('download', this);
29690 onTrash : function(e)
29692 e.preventDefault();
29694 this.fireEvent('trash', this);
29706 * @class Roo.bootstrap.NavProgressBar
29707 * @extends Roo.bootstrap.Component
29708 * Bootstrap NavProgressBar class
29711 * Create a new nav progress bar
29712 * @param {Object} config The config object
29715 Roo.bootstrap.NavProgressBar = function(config){
29716 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29718 this.bullets = this.bullets || [];
29720 // Roo.bootstrap.NavProgressBar.register(this);
29724 * Fires when the active item changes
29725 * @param {Roo.bootstrap.NavProgressBar} this
29726 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29727 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29734 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29739 getAutoCreate : function()
29741 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29745 cls : 'roo-navigation-bar-group',
29749 cls : 'roo-navigation-top-bar'
29753 cls : 'roo-navigation-bullets-bar',
29757 cls : 'roo-navigation-bar'
29764 cls : 'roo-navigation-bottom-bar'
29774 initEvents: function()
29779 onRender : function(ct, position)
29781 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29783 if(this.bullets.length){
29784 Roo.each(this.bullets, function(b){
29793 addItem : function(cfg)
29795 var item = new Roo.bootstrap.NavProgressItem(cfg);
29797 item.parentId = this.id;
29798 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29801 var top = new Roo.bootstrap.Element({
29803 cls : 'roo-navigation-bar-text'
29806 var bottom = new Roo.bootstrap.Element({
29808 cls : 'roo-navigation-bar-text'
29811 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29812 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29814 var topText = new Roo.bootstrap.Element({
29816 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29819 var bottomText = new Roo.bootstrap.Element({
29821 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29824 topText.onRender(top.el, null);
29825 bottomText.onRender(bottom.el, null);
29828 item.bottomEl = bottom;
29831 this.barItems.push(item);
29836 getActive : function()
29838 var active = false;
29840 Roo.each(this.barItems, function(v){
29842 if (!v.isActive()) {
29854 setActiveItem : function(item)
29858 Roo.each(this.barItems, function(v){
29859 if (v.rid == item.rid) {
29863 if (v.isActive()) {
29864 v.setActive(false);
29869 item.setActive(true);
29871 this.fireEvent('changed', this, item, prev);
29874 getBarItem: function(rid)
29878 Roo.each(this.barItems, function(e) {
29879 if (e.rid != rid) {
29890 indexOfItem : function(item)
29894 Roo.each(this.barItems, function(v, i){
29896 if (v.rid != item.rid) {
29907 setActiveNext : function()
29909 var i = this.indexOfItem(this.getActive());
29911 if (i > this.barItems.length) {
29915 this.setActiveItem(this.barItems[i+1]);
29918 setActivePrev : function()
29920 var i = this.indexOfItem(this.getActive());
29926 this.setActiveItem(this.barItems[i-1]);
29929 format : function()
29931 if(!this.barItems.length){
29935 var width = 100 / this.barItems.length;
29937 Roo.each(this.barItems, function(i){
29938 i.el.setStyle('width', width + '%');
29939 i.topEl.el.setStyle('width', width + '%');
29940 i.bottomEl.el.setStyle('width', width + '%');
29949 * Nav Progress Item
29954 * @class Roo.bootstrap.NavProgressItem
29955 * @extends Roo.bootstrap.Component
29956 * Bootstrap NavProgressItem class
29957 * @cfg {String} rid the reference id
29958 * @cfg {Boolean} active (true|false) Is item active default false
29959 * @cfg {Boolean} disabled (true|false) Is item active default false
29960 * @cfg {String} html
29961 * @cfg {String} position (top|bottom) text position default bottom
29962 * @cfg {String} icon show icon instead of number
29965 * Create a new NavProgressItem
29966 * @param {Object} config The config object
29968 Roo.bootstrap.NavProgressItem = function(config){
29969 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29974 * The raw click event for the entire grid.
29975 * @param {Roo.bootstrap.NavProgressItem} this
29976 * @param {Roo.EventObject} e
29983 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29989 position : 'bottom',
29992 getAutoCreate : function()
29994 var iconCls = 'roo-navigation-bar-item-icon';
29996 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30000 cls: 'roo-navigation-bar-item',
30010 cfg.cls += ' active';
30013 cfg.cls += ' disabled';
30019 disable : function()
30021 this.setDisabled(true);
30024 enable : function()
30026 this.setDisabled(false);
30029 initEvents: function()
30031 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30033 this.iconEl.on('click', this.onClick, this);
30036 onClick : function(e)
30038 e.preventDefault();
30044 if(this.fireEvent('click', this, e) === false){
30048 this.parent().setActiveItem(this);
30051 isActive: function ()
30053 return this.active;
30056 setActive : function(state)
30058 if(this.active == state){
30062 this.active = state;
30065 this.el.addClass('active');
30069 this.el.removeClass('active');
30074 setDisabled : function(state)
30076 if(this.disabled == state){
30080 this.disabled = state;
30083 this.el.addClass('disabled');
30087 this.el.removeClass('disabled');
30090 tooltipEl : function()
30092 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30105 * @class Roo.bootstrap.FieldLabel
30106 * @extends Roo.bootstrap.Component
30107 * Bootstrap FieldLabel class
30108 * @cfg {String} html contents of the element
30109 * @cfg {String} tag tag of the element default label
30110 * @cfg {String} cls class of the element
30111 * @cfg {String} target label target
30112 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30113 * @cfg {String} invalidClass default "text-warning"
30114 * @cfg {String} validClass default "text-success"
30115 * @cfg {String} iconTooltip default "This field is required"
30116 * @cfg {String} indicatorpos (left|right) default left
30119 * Create a new FieldLabel
30120 * @param {Object} config The config object
30123 Roo.bootstrap.FieldLabel = function(config){
30124 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30129 * Fires after the field has been marked as invalid.
30130 * @param {Roo.form.FieldLabel} this
30131 * @param {String} msg The validation message
30136 * Fires after the field has been validated with no errors.
30137 * @param {Roo.form.FieldLabel} this
30143 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30150 invalidClass : 'has-warning',
30151 validClass : 'has-success',
30152 iconTooltip : 'This field is required',
30153 indicatorpos : 'left',
30155 getAutoCreate : function(){
30158 if (!this.allowBlank) {
30164 cls : 'roo-bootstrap-field-label ' + this.cls,
30169 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30170 tooltip : this.iconTooltip
30179 if(this.indicatorpos == 'right'){
30182 cls : 'roo-bootstrap-field-label ' + this.cls,
30191 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30192 tooltip : this.iconTooltip
30201 initEvents: function()
30203 Roo.bootstrap.Element.superclass.initEvents.call(this);
30205 this.indicator = this.indicatorEl();
30207 if(this.indicator){
30208 this.indicator.removeClass('visible');
30209 this.indicator.addClass('invisible');
30212 Roo.bootstrap.FieldLabel.register(this);
30215 indicatorEl : function()
30217 var indicator = this.el.select('i.roo-required-indicator',true).first();
30228 * Mark this field as valid
30230 markValid : function()
30232 if(this.indicator){
30233 this.indicator.removeClass('visible');
30234 this.indicator.addClass('invisible');
30237 this.el.removeClass(this.invalidClass);
30239 this.el.addClass(this.validClass);
30241 this.fireEvent('valid', this);
30245 * Mark this field as invalid
30246 * @param {String} msg The validation message
30248 markInvalid : function(msg)
30250 if(this.indicator){
30251 this.indicator.removeClass('invisible');
30252 this.indicator.addClass('visible');
30255 this.el.removeClass(this.validClass);
30257 this.el.addClass(this.invalidClass);
30259 this.fireEvent('invalid', this, msg);
30265 Roo.apply(Roo.bootstrap.FieldLabel, {
30270 * register a FieldLabel Group
30271 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30273 register : function(label)
30275 if(this.groups.hasOwnProperty(label.target)){
30279 this.groups[label.target] = label;
30283 * fetch a FieldLabel Group based on the target
30284 * @param {string} target
30285 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30287 get: function(target) {
30288 if (typeof(this.groups[target]) == 'undefined') {
30292 return this.groups[target] ;
30301 * page DateSplitField.
30307 * @class Roo.bootstrap.DateSplitField
30308 * @extends Roo.bootstrap.Component
30309 * Bootstrap DateSplitField class
30310 * @cfg {string} fieldLabel - the label associated
30311 * @cfg {Number} labelWidth set the width of label (0-12)
30312 * @cfg {String} labelAlign (top|left)
30313 * @cfg {Boolean} dayAllowBlank (true|false) default false
30314 * @cfg {Boolean} monthAllowBlank (true|false) default false
30315 * @cfg {Boolean} yearAllowBlank (true|false) default false
30316 * @cfg {string} dayPlaceholder
30317 * @cfg {string} monthPlaceholder
30318 * @cfg {string} yearPlaceholder
30319 * @cfg {string} dayFormat default 'd'
30320 * @cfg {string} monthFormat default 'm'
30321 * @cfg {string} yearFormat default 'Y'
30322 * @cfg {Number} labellg set the width of label (1-12)
30323 * @cfg {Number} labelmd set the width of label (1-12)
30324 * @cfg {Number} labelsm set the width of label (1-12)
30325 * @cfg {Number} labelxs set the width of label (1-12)
30329 * Create a new DateSplitField
30330 * @param {Object} config The config object
30333 Roo.bootstrap.DateSplitField = function(config){
30334 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30340 * getting the data of years
30341 * @param {Roo.bootstrap.DateSplitField} this
30342 * @param {Object} years
30347 * getting the data of days
30348 * @param {Roo.bootstrap.DateSplitField} this
30349 * @param {Object} days
30354 * Fires after the field has been marked as invalid.
30355 * @param {Roo.form.Field} this
30356 * @param {String} msg The validation message
30361 * Fires after the field has been validated with no errors.
30362 * @param {Roo.form.Field} this
30368 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30371 labelAlign : 'top',
30373 dayAllowBlank : false,
30374 monthAllowBlank : false,
30375 yearAllowBlank : false,
30376 dayPlaceholder : '',
30377 monthPlaceholder : '',
30378 yearPlaceholder : '',
30382 isFormField : true,
30388 getAutoCreate : function()
30392 cls : 'row roo-date-split-field-group',
30397 cls : 'form-hidden-field roo-date-split-field-group-value',
30403 var labelCls = 'col-md-12';
30404 var contentCls = 'col-md-4';
30406 if(this.fieldLabel){
30410 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30414 html : this.fieldLabel
30419 if(this.labelAlign == 'left'){
30421 if(this.labelWidth > 12){
30422 label.style = "width: " + this.labelWidth + 'px';
30425 if(this.labelWidth < 13 && this.labelmd == 0){
30426 this.labelmd = this.labelWidth;
30429 if(this.labellg > 0){
30430 labelCls = ' col-lg-' + this.labellg;
30431 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30434 if(this.labelmd > 0){
30435 labelCls = ' col-md-' + this.labelmd;
30436 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30439 if(this.labelsm > 0){
30440 labelCls = ' col-sm-' + this.labelsm;
30441 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30444 if(this.labelxs > 0){
30445 labelCls = ' col-xs-' + this.labelxs;
30446 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30450 label.cls += ' ' + labelCls;
30452 cfg.cn.push(label);
30455 Roo.each(['day', 'month', 'year'], function(t){
30458 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30465 inputEl: function ()
30467 return this.el.select('.roo-date-split-field-group-value', true).first();
30470 onRender : function(ct, position)
30474 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30476 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30478 this.dayField = new Roo.bootstrap.ComboBox({
30479 allowBlank : this.dayAllowBlank,
30480 alwaysQuery : true,
30481 displayField : 'value',
30484 forceSelection : true,
30486 placeholder : this.dayPlaceholder,
30487 selectOnFocus : true,
30488 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30489 triggerAction : 'all',
30491 valueField : 'value',
30492 store : new Roo.data.SimpleStore({
30493 data : (function() {
30495 _this.fireEvent('days', _this, days);
30498 fields : [ 'value' ]
30501 select : function (_self, record, index)
30503 _this.setValue(_this.getValue());
30508 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30510 this.monthField = new Roo.bootstrap.MonthField({
30511 after : '<i class=\"fa fa-calendar\"></i>',
30512 allowBlank : this.monthAllowBlank,
30513 placeholder : this.monthPlaceholder,
30516 render : function (_self)
30518 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30519 e.preventDefault();
30523 select : function (_self, oldvalue, newvalue)
30525 _this.setValue(_this.getValue());
30530 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30532 this.yearField = new Roo.bootstrap.ComboBox({
30533 allowBlank : this.yearAllowBlank,
30534 alwaysQuery : true,
30535 displayField : 'value',
30538 forceSelection : true,
30540 placeholder : this.yearPlaceholder,
30541 selectOnFocus : true,
30542 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30543 triggerAction : 'all',
30545 valueField : 'value',
30546 store : new Roo.data.SimpleStore({
30547 data : (function() {
30549 _this.fireEvent('years', _this, years);
30552 fields : [ 'value' ]
30555 select : function (_self, record, index)
30557 _this.setValue(_this.getValue());
30562 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30565 setValue : function(v, format)
30567 this.inputEl.dom.value = v;
30569 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30571 var d = Date.parseDate(v, f);
30578 this.setDay(d.format(this.dayFormat));
30579 this.setMonth(d.format(this.monthFormat));
30580 this.setYear(d.format(this.yearFormat));
30587 setDay : function(v)
30589 this.dayField.setValue(v);
30590 this.inputEl.dom.value = this.getValue();
30595 setMonth : function(v)
30597 this.monthField.setValue(v, true);
30598 this.inputEl.dom.value = this.getValue();
30603 setYear : function(v)
30605 this.yearField.setValue(v);
30606 this.inputEl.dom.value = this.getValue();
30611 getDay : function()
30613 return this.dayField.getValue();
30616 getMonth : function()
30618 return this.monthField.getValue();
30621 getYear : function()
30623 return this.yearField.getValue();
30626 getValue : function()
30628 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30630 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30640 this.inputEl.dom.value = '';
30645 validate : function()
30647 var d = this.dayField.validate();
30648 var m = this.monthField.validate();
30649 var y = this.yearField.validate();
30654 (!this.dayAllowBlank && !d) ||
30655 (!this.monthAllowBlank && !m) ||
30656 (!this.yearAllowBlank && !y)
30661 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30670 this.markInvalid();
30675 markValid : function()
30678 var label = this.el.select('label', true).first();
30679 var icon = this.el.select('i.fa-star', true).first();
30685 this.fireEvent('valid', this);
30689 * Mark this field as invalid
30690 * @param {String} msg The validation message
30692 markInvalid : function(msg)
30695 var label = this.el.select('label', true).first();
30696 var icon = this.el.select('i.fa-star', true).first();
30698 if(label && !icon){
30699 this.el.select('.roo-date-split-field-label', true).createChild({
30701 cls : 'text-danger fa fa-lg fa-star',
30702 tooltip : 'This field is required',
30703 style : 'margin-right:5px;'
30707 this.fireEvent('invalid', this, msg);
30710 clearInvalid : function()
30712 var label = this.el.select('label', true).first();
30713 var icon = this.el.select('i.fa-star', true).first();
30719 this.fireEvent('valid', this);
30722 getName: function()
30732 * http://masonry.desandro.com
30734 * The idea is to render all the bricks based on vertical width...
30736 * The original code extends 'outlayer' - we might need to use that....
30742 * @class Roo.bootstrap.LayoutMasonry
30743 * @extends Roo.bootstrap.Component
30744 * Bootstrap Layout Masonry class
30747 * Create a new Element
30748 * @param {Object} config The config object
30751 Roo.bootstrap.LayoutMasonry = function(config){
30753 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30757 Roo.bootstrap.LayoutMasonry.register(this);
30763 * Fire after layout the items
30764 * @param {Roo.bootstrap.LayoutMasonry} this
30765 * @param {Roo.EventObject} e
30772 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30775 * @cfg {Boolean} isLayoutInstant = no animation?
30777 isLayoutInstant : false, // needed?
30780 * @cfg {Number} boxWidth width of the columns
30785 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30790 * @cfg {Number} padWidth padding below box..
30795 * @cfg {Number} gutter gutter width..
30800 * @cfg {Number} maxCols maximum number of columns
30806 * @cfg {Boolean} isAutoInitial defalut true
30808 isAutoInitial : true,
30813 * @cfg {Boolean} isHorizontal defalut false
30815 isHorizontal : false,
30817 currentSize : null,
30823 bricks: null, //CompositeElement
30827 _isLayoutInited : false,
30829 // isAlternative : false, // only use for vertical layout...
30832 * @cfg {Number} alternativePadWidth padding below box..
30834 alternativePadWidth : 50,
30836 selectedBrick : [],
30838 getAutoCreate : function(){
30840 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30844 cls: 'blog-masonary-wrapper ' + this.cls,
30846 cls : 'mas-boxes masonary'
30853 getChildContainer: function( )
30855 if (this.boxesEl) {
30856 return this.boxesEl;
30859 this.boxesEl = this.el.select('.mas-boxes').first();
30861 return this.boxesEl;
30865 initEvents : function()
30869 if(this.isAutoInitial){
30870 Roo.log('hook children rendered');
30871 this.on('childrenrendered', function() {
30872 Roo.log('children rendered');
30878 initial : function()
30880 this.selectedBrick = [];
30882 this.currentSize = this.el.getBox(true);
30884 Roo.EventManager.onWindowResize(this.resize, this);
30886 if(!this.isAutoInitial){
30894 //this.layout.defer(500,this);
30898 resize : function()
30900 var cs = this.el.getBox(true);
30903 this.currentSize.width == cs.width &&
30904 this.currentSize.x == cs.x &&
30905 this.currentSize.height == cs.height &&
30906 this.currentSize.y == cs.y
30908 Roo.log("no change in with or X or Y");
30912 this.currentSize = cs;
30918 layout : function()
30920 this._resetLayout();
30922 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30924 this.layoutItems( isInstant );
30926 this._isLayoutInited = true;
30928 this.fireEvent('layout', this);
30932 _resetLayout : function()
30934 if(this.isHorizontal){
30935 this.horizontalMeasureColumns();
30939 this.verticalMeasureColumns();
30943 verticalMeasureColumns : function()
30945 this.getContainerWidth();
30947 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30948 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30952 var boxWidth = this.boxWidth + this.padWidth;
30954 if(this.containerWidth < this.boxWidth){
30955 boxWidth = this.containerWidth
30958 var containerWidth = this.containerWidth;
30960 var cols = Math.floor(containerWidth / boxWidth);
30962 this.cols = Math.max( cols, 1 );
30964 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30966 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30968 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30970 this.colWidth = boxWidth + avail - this.padWidth;
30972 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30973 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30976 horizontalMeasureColumns : function()
30978 this.getContainerWidth();
30980 var boxWidth = this.boxWidth;
30982 if(this.containerWidth < boxWidth){
30983 boxWidth = this.containerWidth;
30986 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30988 this.el.setHeight(boxWidth);
30992 getContainerWidth : function()
30994 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30997 layoutItems : function( isInstant )
30999 Roo.log(this.bricks);
31001 var items = Roo.apply([], this.bricks);
31003 if(this.isHorizontal){
31004 this._horizontalLayoutItems( items , isInstant );
31008 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31009 // this._verticalAlternativeLayoutItems( items , isInstant );
31013 this._verticalLayoutItems( items , isInstant );
31017 _verticalLayoutItems : function ( items , isInstant)
31019 if ( !items || !items.length ) {
31024 ['xs', 'xs', 'xs', 'tall'],
31025 ['xs', 'xs', 'tall'],
31026 ['xs', 'xs', 'sm'],
31027 ['xs', 'xs', 'xs'],
31033 ['sm', 'xs', 'xs'],
31037 ['tall', 'xs', 'xs', 'xs'],
31038 ['tall', 'xs', 'xs'],
31050 Roo.each(items, function(item, k){
31052 switch (item.size) {
31053 // these layouts take up a full box,
31064 boxes.push([item]);
31087 var filterPattern = function(box, length)
31095 var pattern = box.slice(0, length);
31099 Roo.each(pattern, function(i){
31100 format.push(i.size);
31103 Roo.each(standard, function(s){
31105 if(String(s) != String(format)){
31114 if(!match && length == 1){
31119 filterPattern(box, length - 1);
31123 queue.push(pattern);
31125 box = box.slice(length, box.length);
31127 filterPattern(box, 4);
31133 Roo.each(boxes, function(box, k){
31139 if(box.length == 1){
31144 filterPattern(box, 4);
31148 this._processVerticalLayoutQueue( queue, isInstant );
31152 // _verticalAlternativeLayoutItems : function( items , isInstant )
31154 // if ( !items || !items.length ) {
31158 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31162 _horizontalLayoutItems : function ( items , isInstant)
31164 if ( !items || !items.length || items.length < 3) {
31170 var eItems = items.slice(0, 3);
31172 items = items.slice(3, items.length);
31175 ['xs', 'xs', 'xs', 'wide'],
31176 ['xs', 'xs', 'wide'],
31177 ['xs', 'xs', 'sm'],
31178 ['xs', 'xs', 'xs'],
31184 ['sm', 'xs', 'xs'],
31188 ['wide', 'xs', 'xs', 'xs'],
31189 ['wide', 'xs', 'xs'],
31202 Roo.each(items, function(item, k){
31204 switch (item.size) {
31215 boxes.push([item]);
31239 var filterPattern = function(box, length)
31247 var pattern = box.slice(0, length);
31251 Roo.each(pattern, function(i){
31252 format.push(i.size);
31255 Roo.each(standard, function(s){
31257 if(String(s) != String(format)){
31266 if(!match && length == 1){
31271 filterPattern(box, length - 1);
31275 queue.push(pattern);
31277 box = box.slice(length, box.length);
31279 filterPattern(box, 4);
31285 Roo.each(boxes, function(box, k){
31291 if(box.length == 1){
31296 filterPattern(box, 4);
31303 var pos = this.el.getBox(true);
31307 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31309 var hit_end = false;
31311 Roo.each(queue, function(box){
31315 Roo.each(box, function(b){
31317 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31327 Roo.each(box, function(b){
31329 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31332 mx = Math.max(mx, b.x);
31336 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31340 Roo.each(box, function(b){
31342 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31356 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31359 /** Sets position of item in DOM
31360 * @param {Element} item
31361 * @param {Number} x - horizontal position
31362 * @param {Number} y - vertical position
31363 * @param {Boolean} isInstant - disables transitions
31365 _processVerticalLayoutQueue : function( queue, isInstant )
31367 var pos = this.el.getBox(true);
31372 for (var i = 0; i < this.cols; i++){
31376 Roo.each(queue, function(box, k){
31378 var col = k % this.cols;
31380 Roo.each(box, function(b,kk){
31382 b.el.position('absolute');
31384 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31385 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31387 if(b.size == 'md-left' || b.size == 'md-right'){
31388 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31389 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31392 b.el.setWidth(width);
31393 b.el.setHeight(height);
31395 b.el.select('iframe',true).setSize(width,height);
31399 for (var i = 0; i < this.cols; i++){
31401 if(maxY[i] < maxY[col]){
31406 col = Math.min(col, i);
31410 x = pos.x + col * (this.colWidth + this.padWidth);
31414 var positions = [];
31416 switch (box.length){
31418 positions = this.getVerticalOneBoxColPositions(x, y, box);
31421 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31424 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31427 positions = this.getVerticalFourBoxColPositions(x, y, box);
31433 Roo.each(box, function(b,kk){
31435 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31437 var sz = b.el.getSize();
31439 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31447 for (var i = 0; i < this.cols; i++){
31448 mY = Math.max(mY, maxY[i]);
31451 this.el.setHeight(mY - pos.y);
31455 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31457 // var pos = this.el.getBox(true);
31460 // var maxX = pos.right;
31462 // var maxHeight = 0;
31464 // Roo.each(items, function(item, k){
31468 // item.el.position('absolute');
31470 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31472 // item.el.setWidth(width);
31474 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31476 // item.el.setHeight(height);
31479 // item.el.setXY([x, y], isInstant ? false : true);
31481 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31484 // y = y + height + this.alternativePadWidth;
31486 // maxHeight = maxHeight + height + this.alternativePadWidth;
31490 // this.el.setHeight(maxHeight);
31494 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31496 var pos = this.el.getBox(true);
31501 var maxX = pos.right;
31503 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31505 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31507 Roo.each(queue, function(box, k){
31509 Roo.each(box, function(b, kk){
31511 b.el.position('absolute');
31513 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31514 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31516 if(b.size == 'md-left' || b.size == 'md-right'){
31517 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31518 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31521 b.el.setWidth(width);
31522 b.el.setHeight(height);
31530 var positions = [];
31532 switch (box.length){
31534 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31537 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31540 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31543 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31549 Roo.each(box, function(b,kk){
31551 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31553 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31561 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31563 Roo.each(eItems, function(b,k){
31565 b.size = (k == 0) ? 'sm' : 'xs';
31566 b.x = (k == 0) ? 2 : 1;
31567 b.y = (k == 0) ? 2 : 1;
31569 b.el.position('absolute');
31571 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31573 b.el.setWidth(width);
31575 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31577 b.el.setHeight(height);
31581 var positions = [];
31584 x : maxX - this.unitWidth * 2 - this.gutter,
31589 x : maxX - this.unitWidth,
31590 y : minY + (this.unitWidth + this.gutter) * 2
31594 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31598 Roo.each(eItems, function(b,k){
31600 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31606 getVerticalOneBoxColPositions : function(x, y, box)
31610 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31612 if(box[0].size == 'md-left'){
31616 if(box[0].size == 'md-right'){
31621 x : x + (this.unitWidth + this.gutter) * rand,
31628 getVerticalTwoBoxColPositions : function(x, y, box)
31632 if(box[0].size == 'xs'){
31636 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31640 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31654 x : x + (this.unitWidth + this.gutter) * 2,
31655 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31662 getVerticalThreeBoxColPositions : function(x, y, box)
31666 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31674 x : x + (this.unitWidth + this.gutter) * 1,
31679 x : x + (this.unitWidth + this.gutter) * 2,
31687 if(box[0].size == 'xs' && box[1].size == 'xs'){
31696 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31700 x : x + (this.unitWidth + this.gutter) * 1,
31714 x : x + (this.unitWidth + this.gutter) * 2,
31719 x : x + (this.unitWidth + this.gutter) * 2,
31720 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31727 getVerticalFourBoxColPositions : function(x, y, box)
31731 if(box[0].size == 'xs'){
31740 y : y + (this.unitHeight + this.gutter) * 1
31745 y : y + (this.unitHeight + this.gutter) * 2
31749 x : x + (this.unitWidth + this.gutter) * 1,
31763 x : x + (this.unitWidth + this.gutter) * 2,
31768 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31769 y : y + (this.unitHeight + this.gutter) * 1
31773 x : x + (this.unitWidth + this.gutter) * 2,
31774 y : y + (this.unitWidth + this.gutter) * 2
31781 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31785 if(box[0].size == 'md-left'){
31787 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31794 if(box[0].size == 'md-right'){
31796 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31797 y : minY + (this.unitWidth + this.gutter) * 1
31803 var rand = Math.floor(Math.random() * (4 - box[0].y));
31806 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31807 y : minY + (this.unitWidth + this.gutter) * rand
31814 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31818 if(box[0].size == 'xs'){
31821 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31826 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31827 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31835 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31840 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31841 y : minY + (this.unitWidth + this.gutter) * 2
31848 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31852 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31855 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31860 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31861 y : minY + (this.unitWidth + this.gutter) * 1
31865 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31866 y : minY + (this.unitWidth + this.gutter) * 2
31873 if(box[0].size == 'xs' && box[1].size == 'xs'){
31876 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31881 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31886 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31887 y : minY + (this.unitWidth + this.gutter) * 1
31895 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31900 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31901 y : minY + (this.unitWidth + this.gutter) * 2
31905 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31906 y : minY + (this.unitWidth + this.gutter) * 2
31913 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31917 if(box[0].size == 'xs'){
31920 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31925 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31930 x : maxX - this.unitWidth * box[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),
31935 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31936 y : minY + (this.unitWidth + this.gutter) * 1
31944 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31949 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31950 y : minY + (this.unitWidth + this.gutter) * 2
31954 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31955 y : minY + (this.unitWidth + this.gutter) * 2
31959 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),
31960 y : minY + (this.unitWidth + this.gutter) * 2
31968 * remove a Masonry Brick
31969 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31971 removeBrick : function(brick_id)
31977 for (var i = 0; i<this.bricks.length; i++) {
31978 if (this.bricks[i].id == brick_id) {
31979 this.bricks.splice(i,1);
31980 this.el.dom.removeChild(Roo.get(brick_id).dom);
31987 * adds a Masonry Brick
31988 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31990 addBrick : function(cfg)
31992 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31993 //this.register(cn);
31994 cn.parentId = this.id;
31995 cn.render(this.el);
32000 * register a Masonry Brick
32001 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32004 register : function(brick)
32006 this.bricks.push(brick);
32007 brick.masonryId = this.id;
32011 * clear all the Masonry Brick
32013 clearAll : function()
32016 //this.getChildContainer().dom.innerHTML = "";
32017 this.el.dom.innerHTML = '';
32020 getSelected : function()
32022 if (!this.selectedBrick) {
32026 return this.selectedBrick;
32030 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32034 * register a Masonry Layout
32035 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32038 register : function(layout)
32040 this.groups[layout.id] = layout;
32043 * fetch a Masonry Layout based on the masonry layout ID
32044 * @param {string} the masonry layout to add
32045 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32048 get: function(layout_id) {
32049 if (typeof(this.groups[layout_id]) == 'undefined') {
32052 return this.groups[layout_id] ;
32064 * http://masonry.desandro.com
32066 * The idea is to render all the bricks based on vertical width...
32068 * The original code extends 'outlayer' - we might need to use that....
32074 * @class Roo.bootstrap.LayoutMasonryAuto
32075 * @extends Roo.bootstrap.Component
32076 * Bootstrap Layout Masonry class
32079 * Create a new Element
32080 * @param {Object} config The config object
32083 Roo.bootstrap.LayoutMasonryAuto = function(config){
32084 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32087 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32090 * @cfg {Boolean} isFitWidth - resize the width..
32092 isFitWidth : false, // options..
32094 * @cfg {Boolean} isOriginLeft = left align?
32096 isOriginLeft : true,
32098 * @cfg {Boolean} isOriginTop = top align?
32100 isOriginTop : false,
32102 * @cfg {Boolean} isLayoutInstant = no animation?
32104 isLayoutInstant : false, // needed?
32106 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32108 isResizingContainer : true,
32110 * @cfg {Number} columnWidth width of the columns
32116 * @cfg {Number} maxCols maximum number of columns
32121 * @cfg {Number} padHeight padding below box..
32127 * @cfg {Boolean} isAutoInitial defalut true
32130 isAutoInitial : true,
32136 initialColumnWidth : 0,
32137 currentSize : null,
32139 colYs : null, // array.
32146 bricks: null, //CompositeElement
32147 cols : 0, // array?
32148 // element : null, // wrapped now this.el
32149 _isLayoutInited : null,
32152 getAutoCreate : function(){
32156 cls: 'blog-masonary-wrapper ' + this.cls,
32158 cls : 'mas-boxes masonary'
32165 getChildContainer: function( )
32167 if (this.boxesEl) {
32168 return this.boxesEl;
32171 this.boxesEl = this.el.select('.mas-boxes').first();
32173 return this.boxesEl;
32177 initEvents : function()
32181 if(this.isAutoInitial){
32182 Roo.log('hook children rendered');
32183 this.on('childrenrendered', function() {
32184 Roo.log('children rendered');
32191 initial : function()
32193 this.reloadItems();
32195 this.currentSize = this.el.getBox(true);
32197 /// was window resize... - let's see if this works..
32198 Roo.EventManager.onWindowResize(this.resize, this);
32200 if(!this.isAutoInitial){
32205 this.layout.defer(500,this);
32208 reloadItems: function()
32210 this.bricks = this.el.select('.masonry-brick', true);
32212 this.bricks.each(function(b) {
32213 //Roo.log(b.getSize());
32214 if (!b.attr('originalwidth')) {
32215 b.attr('originalwidth', b.getSize().width);
32220 Roo.log(this.bricks.elements.length);
32223 resize : function()
32226 var cs = this.el.getBox(true);
32228 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32229 Roo.log("no change in with or X");
32232 this.currentSize = cs;
32236 layout : function()
32239 this._resetLayout();
32240 //this._manageStamps();
32242 // don't animate first layout
32243 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32244 this.layoutItems( isInstant );
32246 // flag for initalized
32247 this._isLayoutInited = true;
32250 layoutItems : function( isInstant )
32252 //var items = this._getItemsForLayout( this.items );
32253 // original code supports filtering layout items.. we just ignore it..
32255 this._layoutItems( this.bricks , isInstant );
32257 this._postLayout();
32259 _layoutItems : function ( items , isInstant)
32261 //this.fireEvent( 'layout', this, items );
32264 if ( !items || !items.elements.length ) {
32265 // no items, emit event with empty array
32270 items.each(function(item) {
32271 Roo.log("layout item");
32273 // get x/y object from method
32274 var position = this._getItemLayoutPosition( item );
32276 position.item = item;
32277 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32278 queue.push( position );
32281 this._processLayoutQueue( queue );
32283 /** Sets position of item in DOM
32284 * @param {Element} item
32285 * @param {Number} x - horizontal position
32286 * @param {Number} y - vertical position
32287 * @param {Boolean} isInstant - disables transitions
32289 _processLayoutQueue : function( queue )
32291 for ( var i=0, len = queue.length; i < len; i++ ) {
32292 var obj = queue[i];
32293 obj.item.position('absolute');
32294 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32300 * Any logic you want to do after each layout,
32301 * i.e. size the container
32303 _postLayout : function()
32305 this.resizeContainer();
32308 resizeContainer : function()
32310 if ( !this.isResizingContainer ) {
32313 var size = this._getContainerSize();
32315 this.el.setSize(size.width,size.height);
32316 this.boxesEl.setSize(size.width,size.height);
32322 _resetLayout : function()
32324 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32325 this.colWidth = this.el.getWidth();
32326 //this.gutter = this.el.getWidth();
32328 this.measureColumns();
32334 this.colYs.push( 0 );
32340 measureColumns : function()
32342 this.getContainerWidth();
32343 // if columnWidth is 0, default to outerWidth of first item
32344 if ( !this.columnWidth ) {
32345 var firstItem = this.bricks.first();
32346 Roo.log(firstItem);
32347 this.columnWidth = this.containerWidth;
32348 if (firstItem && firstItem.attr('originalwidth') ) {
32349 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32351 // columnWidth fall back to item of first element
32352 Roo.log("set column width?");
32353 this.initialColumnWidth = this.columnWidth ;
32355 // if first elem has no width, default to size of container
32360 if (this.initialColumnWidth) {
32361 this.columnWidth = this.initialColumnWidth;
32366 // column width is fixed at the top - however if container width get's smaller we should
32369 // this bit calcs how man columns..
32371 var columnWidth = this.columnWidth += this.gutter;
32373 // calculate columns
32374 var containerWidth = this.containerWidth + this.gutter;
32376 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32377 // fix rounding errors, typically with gutters
32378 var excess = columnWidth - containerWidth % columnWidth;
32381 // if overshoot is less than a pixel, round up, otherwise floor it
32382 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32383 cols = Math[ mathMethod ]( cols );
32384 this.cols = Math.max( cols, 1 );
32385 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32387 // padding positioning..
32388 var totalColWidth = this.cols * this.columnWidth;
32389 var padavail = this.containerWidth - totalColWidth;
32390 // so for 2 columns - we need 3 'pads'
32392 var padNeeded = (1+this.cols) * this.padWidth;
32394 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32396 this.columnWidth += padExtra
32397 //this.padWidth = Math.floor(padavail / ( this.cols));
32399 // adjust colum width so that padding is fixed??
32401 // we have 3 columns ... total = width * 3
32402 // we have X left over... that should be used by
32404 //if (this.expandC) {
32412 getContainerWidth : function()
32414 /* // container is parent if fit width
32415 var container = this.isFitWidth ? this.element.parentNode : this.element;
32416 // check that this.size and size are there
32417 // IE8 triggers resize on body size change, so they might not be
32419 var size = getSize( container ); //FIXME
32420 this.containerWidth = size && size.innerWidth; //FIXME
32423 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32427 _getItemLayoutPosition : function( item ) // what is item?
32429 // we resize the item to our columnWidth..
32431 item.setWidth(this.columnWidth);
32432 item.autoBoxAdjust = false;
32434 var sz = item.getSize();
32436 // how many columns does this brick span
32437 var remainder = this.containerWidth % this.columnWidth;
32439 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32440 // round if off by 1 pixel, otherwise use ceil
32441 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32442 colSpan = Math.min( colSpan, this.cols );
32444 // normally this should be '1' as we dont' currently allow multi width columns..
32446 var colGroup = this._getColGroup( colSpan );
32447 // get the minimum Y value from the columns
32448 var minimumY = Math.min.apply( Math, colGroup );
32449 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32451 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32453 // position the brick
32455 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32456 y: this.currentSize.y + minimumY + this.padHeight
32460 // apply setHeight to necessary columns
32461 var setHeight = minimumY + sz.height + this.padHeight;
32462 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32464 var setSpan = this.cols + 1 - colGroup.length;
32465 for ( var i = 0; i < setSpan; i++ ) {
32466 this.colYs[ shortColIndex + i ] = setHeight ;
32473 * @param {Number} colSpan - number of columns the element spans
32474 * @returns {Array} colGroup
32476 _getColGroup : function( colSpan )
32478 if ( colSpan < 2 ) {
32479 // if brick spans only one column, use all the column Ys
32484 // how many different places could this brick fit horizontally
32485 var groupCount = this.cols + 1 - colSpan;
32486 // for each group potential horizontal position
32487 for ( var i = 0; i < groupCount; i++ ) {
32488 // make an array of colY values for that one group
32489 var groupColYs = this.colYs.slice( i, i + colSpan );
32490 // and get the max value of the array
32491 colGroup[i] = Math.max.apply( Math, groupColYs );
32496 _manageStamp : function( stamp )
32498 var stampSize = stamp.getSize();
32499 var offset = stamp.getBox();
32500 // get the columns that this stamp affects
32501 var firstX = this.isOriginLeft ? offset.x : offset.right;
32502 var lastX = firstX + stampSize.width;
32503 var firstCol = Math.floor( firstX / this.columnWidth );
32504 firstCol = Math.max( 0, firstCol );
32506 var lastCol = Math.floor( lastX / this.columnWidth );
32507 // lastCol should not go over if multiple of columnWidth #425
32508 lastCol -= lastX % this.columnWidth ? 0 : 1;
32509 lastCol = Math.min( this.cols - 1, lastCol );
32511 // set colYs to bottom of the stamp
32512 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32515 for ( var i = firstCol; i <= lastCol; i++ ) {
32516 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32521 _getContainerSize : function()
32523 this.maxY = Math.max.apply( Math, this.colYs );
32528 if ( this.isFitWidth ) {
32529 size.width = this._getContainerFitWidth();
32535 _getContainerFitWidth : function()
32537 var unusedCols = 0;
32538 // count unused columns
32541 if ( this.colYs[i] !== 0 ) {
32546 // fit container to columns that have been used
32547 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32550 needsResizeLayout : function()
32552 var previousWidth = this.containerWidth;
32553 this.getContainerWidth();
32554 return previousWidth !== this.containerWidth;
32569 * @class Roo.bootstrap.MasonryBrick
32570 * @extends Roo.bootstrap.Component
32571 * Bootstrap MasonryBrick class
32574 * Create a new MasonryBrick
32575 * @param {Object} config The config object
32578 Roo.bootstrap.MasonryBrick = function(config){
32580 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32582 Roo.bootstrap.MasonryBrick.register(this);
32588 * When a MasonryBrick is clcik
32589 * @param {Roo.bootstrap.MasonryBrick} this
32590 * @param {Roo.EventObject} e
32596 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32599 * @cfg {String} title
32603 * @cfg {String} html
32607 * @cfg {String} bgimage
32611 * @cfg {String} videourl
32615 * @cfg {String} cls
32619 * @cfg {String} href
32623 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32628 * @cfg {String} placetitle (center|bottom)
32633 * @cfg {Boolean} isFitContainer defalut true
32635 isFitContainer : true,
32638 * @cfg {Boolean} preventDefault defalut false
32640 preventDefault : false,
32643 * @cfg {Boolean} inverse defalut false
32645 maskInverse : false,
32647 getAutoCreate : function()
32649 if(!this.isFitContainer){
32650 return this.getSplitAutoCreate();
32653 var cls = 'masonry-brick masonry-brick-full';
32655 if(this.href.length){
32656 cls += ' masonry-brick-link';
32659 if(this.bgimage.length){
32660 cls += ' masonry-brick-image';
32663 if(this.maskInverse){
32664 cls += ' mask-inverse';
32667 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32668 cls += ' enable-mask';
32672 cls += ' masonry-' + this.size + '-brick';
32675 if(this.placetitle.length){
32677 switch (this.placetitle) {
32679 cls += ' masonry-center-title';
32682 cls += ' masonry-bottom-title';
32689 if(!this.html.length && !this.bgimage.length){
32690 cls += ' masonry-center-title';
32693 if(!this.html.length && this.bgimage.length){
32694 cls += ' masonry-bottom-title';
32699 cls += ' ' + this.cls;
32703 tag: (this.href.length) ? 'a' : 'div',
32708 cls: 'masonry-brick-mask'
32712 cls: 'masonry-brick-paragraph',
32718 if(this.href.length){
32719 cfg.href = this.href;
32722 var cn = cfg.cn[1].cn;
32724 if(this.title.length){
32727 cls: 'masonry-brick-title',
32732 if(this.html.length){
32735 cls: 'masonry-brick-text',
32740 if (!this.title.length && !this.html.length) {
32741 cfg.cn[1].cls += ' hide';
32744 if(this.bgimage.length){
32747 cls: 'masonry-brick-image-view',
32752 if(this.videourl.length){
32753 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32754 // youtube support only?
32757 cls: 'masonry-brick-image-view',
32760 allowfullscreen : true
32768 getSplitAutoCreate : function()
32770 var cls = 'masonry-brick masonry-brick-split';
32772 if(this.href.length){
32773 cls += ' masonry-brick-link';
32776 if(this.bgimage.length){
32777 cls += ' masonry-brick-image';
32781 cls += ' masonry-' + this.size + '-brick';
32784 switch (this.placetitle) {
32786 cls += ' masonry-center-title';
32789 cls += ' masonry-bottom-title';
32792 if(!this.bgimage.length){
32793 cls += ' masonry-center-title';
32796 if(this.bgimage.length){
32797 cls += ' masonry-bottom-title';
32803 cls += ' ' + this.cls;
32807 tag: (this.href.length) ? 'a' : 'div',
32812 cls: 'masonry-brick-split-head',
32816 cls: 'masonry-brick-paragraph',
32823 cls: 'masonry-brick-split-body',
32829 if(this.href.length){
32830 cfg.href = this.href;
32833 if(this.title.length){
32834 cfg.cn[0].cn[0].cn.push({
32836 cls: 'masonry-brick-title',
32841 if(this.html.length){
32842 cfg.cn[1].cn.push({
32844 cls: 'masonry-brick-text',
32849 if(this.bgimage.length){
32850 cfg.cn[0].cn.push({
32852 cls: 'masonry-brick-image-view',
32857 if(this.videourl.length){
32858 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32859 // youtube support only?
32860 cfg.cn[0].cn.cn.push({
32862 cls: 'masonry-brick-image-view',
32865 allowfullscreen : true
32872 initEvents: function()
32874 switch (this.size) {
32907 this.el.on('touchstart', this.onTouchStart, this);
32908 this.el.on('touchmove', this.onTouchMove, this);
32909 this.el.on('touchend', this.onTouchEnd, this);
32910 this.el.on('contextmenu', this.onContextMenu, this);
32912 this.el.on('mouseenter' ,this.enter, this);
32913 this.el.on('mouseleave', this.leave, this);
32914 this.el.on('click', this.onClick, this);
32917 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32918 this.parent().bricks.push(this);
32923 onClick: function(e, el)
32925 var time = this.endTimer - this.startTimer;
32926 // Roo.log(e.preventDefault());
32929 e.preventDefault();
32934 if(!this.preventDefault){
32938 e.preventDefault();
32940 if (this.activeClass != '') {
32941 this.selectBrick();
32944 this.fireEvent('click', this, e);
32947 enter: function(e, el)
32949 e.preventDefault();
32951 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32955 if(this.bgimage.length && this.html.length){
32956 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32960 leave: function(e, el)
32962 e.preventDefault();
32964 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32968 if(this.bgimage.length && this.html.length){
32969 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32973 onTouchStart: function(e, el)
32975 // e.preventDefault();
32977 this.touchmoved = false;
32979 if(!this.isFitContainer){
32983 if(!this.bgimage.length || !this.html.length){
32987 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32989 this.timer = new Date().getTime();
32993 onTouchMove: function(e, el)
32995 this.touchmoved = true;
32998 onContextMenu : function(e,el)
33000 e.preventDefault();
33001 e.stopPropagation();
33005 onTouchEnd: function(e, el)
33007 // e.preventDefault();
33009 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33016 if(!this.bgimage.length || !this.html.length){
33018 if(this.href.length){
33019 window.location.href = this.href;
33025 if(!this.isFitContainer){
33029 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33031 window.location.href = this.href;
33034 //selection on single brick only
33035 selectBrick : function() {
33037 if (!this.parentId) {
33041 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33042 var index = m.selectedBrick.indexOf(this.id);
33045 m.selectedBrick.splice(index,1);
33046 this.el.removeClass(this.activeClass);
33050 for(var i = 0; i < m.selectedBrick.length; i++) {
33051 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33052 b.el.removeClass(b.activeClass);
33055 m.selectedBrick = [];
33057 m.selectedBrick.push(this.id);
33058 this.el.addClass(this.activeClass);
33062 isSelected : function(){
33063 return this.el.hasClass(this.activeClass);
33068 Roo.apply(Roo.bootstrap.MasonryBrick, {
33071 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33073 * register a Masonry Brick
33074 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33077 register : function(brick)
33079 //this.groups[brick.id] = brick;
33080 this.groups.add(brick.id, brick);
33083 * fetch a masonry brick based on the masonry brick ID
33084 * @param {string} the masonry brick to add
33085 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33088 get: function(brick_id)
33090 // if (typeof(this.groups[brick_id]) == 'undefined') {
33093 // return this.groups[brick_id] ;
33095 if(this.groups.key(brick_id)) {
33096 return this.groups.key(brick_id);
33114 * @class Roo.bootstrap.Brick
33115 * @extends Roo.bootstrap.Component
33116 * Bootstrap Brick class
33119 * Create a new Brick
33120 * @param {Object} config The config object
33123 Roo.bootstrap.Brick = function(config){
33124 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33130 * When a Brick is click
33131 * @param {Roo.bootstrap.Brick} this
33132 * @param {Roo.EventObject} e
33138 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33141 * @cfg {String} title
33145 * @cfg {String} html
33149 * @cfg {String} bgimage
33153 * @cfg {String} cls
33157 * @cfg {String} href
33161 * @cfg {String} video
33165 * @cfg {Boolean} square
33169 getAutoCreate : function()
33171 var cls = 'roo-brick';
33173 if(this.href.length){
33174 cls += ' roo-brick-link';
33177 if(this.bgimage.length){
33178 cls += ' roo-brick-image';
33181 if(!this.html.length && !this.bgimage.length){
33182 cls += ' roo-brick-center-title';
33185 if(!this.html.length && this.bgimage.length){
33186 cls += ' roo-brick-bottom-title';
33190 cls += ' ' + this.cls;
33194 tag: (this.href.length) ? 'a' : 'div',
33199 cls: 'roo-brick-paragraph',
33205 if(this.href.length){
33206 cfg.href = this.href;
33209 var cn = cfg.cn[0].cn;
33211 if(this.title.length){
33214 cls: 'roo-brick-title',
33219 if(this.html.length){
33222 cls: 'roo-brick-text',
33229 if(this.bgimage.length){
33232 cls: 'roo-brick-image-view',
33240 initEvents: function()
33242 if(this.title.length || this.html.length){
33243 this.el.on('mouseenter' ,this.enter, this);
33244 this.el.on('mouseleave', this.leave, this);
33247 Roo.EventManager.onWindowResize(this.resize, this);
33249 if(this.bgimage.length){
33250 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33251 this.imageEl.on('load', this.onImageLoad, this);
33258 onImageLoad : function()
33263 resize : function()
33265 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33267 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33269 if(this.bgimage.length){
33270 var image = this.el.select('.roo-brick-image-view', true).first();
33272 image.setWidth(paragraph.getWidth());
33275 image.setHeight(paragraph.getWidth());
33278 this.el.setHeight(image.getHeight());
33279 paragraph.setHeight(image.getHeight());
33285 enter: function(e, el)
33287 e.preventDefault();
33289 if(this.bgimage.length){
33290 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33291 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33295 leave: function(e, el)
33297 e.preventDefault();
33299 if(this.bgimage.length){
33300 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33301 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33316 * @class Roo.bootstrap.NumberField
33317 * @extends Roo.bootstrap.Input
33318 * Bootstrap NumberField class
33324 * Create a new NumberField
33325 * @param {Object} config The config object
33328 Roo.bootstrap.NumberField = function(config){
33329 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33332 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33335 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33337 allowDecimals : true,
33339 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33341 decimalSeparator : ".",
33343 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33345 decimalPrecision : 2,
33347 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33349 allowNegative : true,
33352 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33356 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33358 minValue : Number.NEGATIVE_INFINITY,
33360 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33362 maxValue : Number.MAX_VALUE,
33364 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33366 minText : "The minimum value for this field is {0}",
33368 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33370 maxText : "The maximum value for this field is {0}",
33372 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33373 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33375 nanText : "{0} is not a valid number",
33377 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33379 thousandsDelimiter : false,
33381 * @cfg {String} valueAlign alignment of value
33383 valueAlign : "left",
33385 getAutoCreate : function()
33387 var hiddenInput = {
33391 cls: 'hidden-number-input'
33395 hiddenInput.name = this.name;
33400 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33402 this.name = hiddenInput.name;
33404 if(cfg.cn.length > 0) {
33405 cfg.cn.push(hiddenInput);
33412 initEvents : function()
33414 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33416 var allowed = "0123456789";
33418 if(this.allowDecimals){
33419 allowed += this.decimalSeparator;
33422 if(this.allowNegative){
33426 if(this.thousandsDelimiter) {
33430 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33432 var keyPress = function(e){
33434 var k = e.getKey();
33436 var c = e.getCharCode();
33439 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33440 allowed.indexOf(String.fromCharCode(c)) === -1
33446 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33450 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33455 this.el.on("keypress", keyPress, this);
33458 validateValue : function(value)
33461 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33465 var num = this.parseValue(value);
33468 this.markInvalid(String.format(this.nanText, value));
33472 if(num < this.minValue){
33473 this.markInvalid(String.format(this.minText, this.minValue));
33477 if(num > this.maxValue){
33478 this.markInvalid(String.format(this.maxText, this.maxValue));
33485 getValue : function()
33487 var v = this.hiddenEl().getValue();
33489 return this.fixPrecision(this.parseValue(v));
33492 parseValue : function(value)
33494 if(this.thousandsDelimiter) {
33496 r = new RegExp(",", "g");
33497 value = value.replace(r, "");
33500 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33501 return isNaN(value) ? '' : value;
33504 fixPrecision : function(value)
33506 if(this.thousandsDelimiter) {
33508 r = new RegExp(",", "g");
33509 value = value.replace(r, "");
33512 var nan = isNaN(value);
33514 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33515 return nan ? '' : value;
33517 return parseFloat(value).toFixed(this.decimalPrecision);
33520 setValue : function(v)
33522 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33528 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33530 this.inputEl().dom.value = (v == '') ? '' :
33531 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33533 if(!this.allowZero && v === '0') {
33534 this.hiddenEl().dom.value = '';
33535 this.inputEl().dom.value = '';
33542 decimalPrecisionFcn : function(v)
33544 return Math.floor(v);
33547 beforeBlur : function()
33549 var v = this.parseValue(this.getRawValue());
33551 if(v || v === 0 || v === ''){
33556 hiddenEl : function()
33558 return this.el.select('input.hidden-number-input',true).first();
33570 * @class Roo.bootstrap.DocumentSlider
33571 * @extends Roo.bootstrap.Component
33572 * Bootstrap DocumentSlider class
33575 * Create a new DocumentViewer
33576 * @param {Object} config The config object
33579 Roo.bootstrap.DocumentSlider = function(config){
33580 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33587 * Fire after initEvent
33588 * @param {Roo.bootstrap.DocumentSlider} this
33593 * Fire after update
33594 * @param {Roo.bootstrap.DocumentSlider} this
33600 * @param {Roo.bootstrap.DocumentSlider} this
33606 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33612 getAutoCreate : function()
33616 cls : 'roo-document-slider',
33620 cls : 'roo-document-slider-header',
33624 cls : 'roo-document-slider-header-title'
33630 cls : 'roo-document-slider-body',
33634 cls : 'roo-document-slider-prev',
33638 cls : 'fa fa-chevron-left'
33644 cls : 'roo-document-slider-thumb',
33648 cls : 'roo-document-slider-image'
33654 cls : 'roo-document-slider-next',
33658 cls : 'fa fa-chevron-right'
33670 initEvents : function()
33672 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33673 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33675 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33676 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33678 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33679 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33681 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33682 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33684 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33685 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33687 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33688 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33690 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33691 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33693 this.thumbEl.on('click', this.onClick, this);
33695 this.prevIndicator.on('click', this.prev, this);
33697 this.nextIndicator.on('click', this.next, this);
33701 initial : function()
33703 if(this.files.length){
33704 this.indicator = 1;
33708 this.fireEvent('initial', this);
33711 update : function()
33713 this.imageEl.attr('src', this.files[this.indicator - 1]);
33715 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33717 this.prevIndicator.show();
33719 if(this.indicator == 1){
33720 this.prevIndicator.hide();
33723 this.nextIndicator.show();
33725 if(this.indicator == this.files.length){
33726 this.nextIndicator.hide();
33729 this.thumbEl.scrollTo('top');
33731 this.fireEvent('update', this);
33734 onClick : function(e)
33736 e.preventDefault();
33738 this.fireEvent('click', this);
33743 e.preventDefault();
33745 this.indicator = Math.max(1, this.indicator - 1);
33752 e.preventDefault();
33754 this.indicator = Math.min(this.files.length, this.indicator + 1);
33768 * @class Roo.bootstrap.RadioSet
33769 * @extends Roo.bootstrap.Input
33770 * Bootstrap RadioSet class
33771 * @cfg {String} indicatorpos (left|right) default left
33772 * @cfg {Boolean} inline (true|false) inline the element (default true)
33773 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33775 * Create a new RadioSet
33776 * @param {Object} config The config object
33779 Roo.bootstrap.RadioSet = function(config){
33781 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33785 Roo.bootstrap.RadioSet.register(this);
33790 * Fires when the element is checked or unchecked.
33791 * @param {Roo.bootstrap.RadioSet} this This radio
33792 * @param {Roo.bootstrap.Radio} item The checked item
33797 * Fires when the element is click.
33798 * @param {Roo.bootstrap.RadioSet} this This radio set
33799 * @param {Roo.bootstrap.Radio} item The checked item
33800 * @param {Roo.EventObject} e The event object
33807 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33815 indicatorpos : 'left',
33817 getAutoCreate : function()
33821 cls : 'roo-radio-set-label',
33825 html : this.fieldLabel
33830 if(this.indicatorpos == 'left'){
33833 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33834 tooltip : 'This field is required'
33839 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33840 tooltip : 'This field is required'
33846 cls : 'roo-radio-set-items'
33849 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33851 if (align === 'left' && this.fieldLabel.length) {
33854 cls : "roo-radio-set-right",
33860 if(this.labelWidth > 12){
33861 label.style = "width: " + this.labelWidth + 'px';
33864 if(this.labelWidth < 13 && this.labelmd == 0){
33865 this.labelmd = this.labelWidth;
33868 if(this.labellg > 0){
33869 label.cls += ' col-lg-' + this.labellg;
33870 items.cls += ' col-lg-' + (12 - this.labellg);
33873 if(this.labelmd > 0){
33874 label.cls += ' col-md-' + this.labelmd;
33875 items.cls += ' col-md-' + (12 - this.labelmd);
33878 if(this.labelsm > 0){
33879 label.cls += ' col-sm-' + this.labelsm;
33880 items.cls += ' col-sm-' + (12 - this.labelsm);
33883 if(this.labelxs > 0){
33884 label.cls += ' col-xs-' + this.labelxs;
33885 items.cls += ' col-xs-' + (12 - this.labelxs);
33891 cls : 'roo-radio-set',
33895 cls : 'roo-radio-set-input',
33898 value : this.value ? this.value : ''
33905 if(this.weight.length){
33906 cfg.cls += ' roo-radio-' + this.weight;
33910 cfg.cls += ' roo-radio-set-inline';
33914 ['xs','sm','md','lg'].map(function(size){
33915 if (settings[size]) {
33916 cfg.cls += ' col-' + size + '-' + settings[size];
33924 initEvents : function()
33926 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33927 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33929 if(!this.fieldLabel.length){
33930 this.labelEl.hide();
33933 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33934 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33936 this.indicator = this.indicatorEl();
33938 if(this.indicator){
33939 this.indicator.addClass('invisible');
33942 this.originalValue = this.getValue();
33946 inputEl: function ()
33948 return this.el.select('.roo-radio-set-input', true).first();
33951 getChildContainer : function()
33953 return this.itemsEl;
33956 register : function(item)
33958 this.radioes.push(item);
33962 validate : function()
33964 if(this.getVisibilityEl().hasClass('hidden')){
33970 Roo.each(this.radioes, function(i){
33979 if(this.allowBlank) {
33983 if(this.disabled || valid){
33988 this.markInvalid();
33993 markValid : function()
33995 if(this.labelEl.isVisible(true)){
33996 this.indicatorEl().removeClass('visible');
33997 this.indicatorEl().addClass('invisible');
34000 this.el.removeClass([this.invalidClass, this.validClass]);
34001 this.el.addClass(this.validClass);
34003 this.fireEvent('valid', this);
34006 markInvalid : function(msg)
34008 if(this.allowBlank || this.disabled){
34012 if(this.labelEl.isVisible(true)){
34013 this.indicatorEl().removeClass('invisible');
34014 this.indicatorEl().addClass('visible');
34017 this.el.removeClass([this.invalidClass, this.validClass]);
34018 this.el.addClass(this.invalidClass);
34020 this.fireEvent('invalid', this, msg);
34024 setValue : function(v, suppressEvent)
34026 if(this.value === v){
34033 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34036 Roo.each(this.radioes, function(i){
34038 i.el.removeClass('checked');
34041 Roo.each(this.radioes, function(i){
34043 if(i.value === v || i.value.toString() === v.toString()){
34045 i.el.addClass('checked');
34047 if(suppressEvent !== true){
34048 this.fireEvent('check', this, i);
34059 clearInvalid : function(){
34061 if(!this.el || this.preventMark){
34065 this.el.removeClass([this.invalidClass]);
34067 this.fireEvent('valid', this);
34072 Roo.apply(Roo.bootstrap.RadioSet, {
34076 register : function(set)
34078 this.groups[set.name] = set;
34081 get: function(name)
34083 if (typeof(this.groups[name]) == 'undefined') {
34087 return this.groups[name] ;
34093 * Ext JS Library 1.1.1
34094 * Copyright(c) 2006-2007, Ext JS, LLC.
34096 * Originally Released Under LGPL - original licence link has changed is not relivant.
34099 * <script type="text/javascript">
34104 * @class Roo.bootstrap.SplitBar
34105 * @extends Roo.util.Observable
34106 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34110 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34111 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34112 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34113 split.minSize = 100;
34114 split.maxSize = 600;
34115 split.animate = true;
34116 split.on('moved', splitterMoved);
34119 * Create a new SplitBar
34120 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34121 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34122 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34123 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34124 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34125 position of the SplitBar).
34127 Roo.bootstrap.SplitBar = function(cfg){
34132 // dragElement : elm
34133 // resizingElement: el,
34135 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34136 // placement : Roo.bootstrap.SplitBar.LEFT ,
34137 // existingProxy ???
34140 this.el = Roo.get(cfg.dragElement, true);
34141 this.el.dom.unselectable = "on";
34143 this.resizingEl = Roo.get(cfg.resizingElement, true);
34147 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34148 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34151 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34154 * The minimum size of the resizing element. (Defaults to 0)
34160 * The maximum size of the resizing element. (Defaults to 2000)
34163 this.maxSize = 2000;
34166 * Whether to animate the transition to the new size
34169 this.animate = false;
34172 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34175 this.useShim = false;
34180 if(!cfg.existingProxy){
34182 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34184 this.proxy = Roo.get(cfg.existingProxy).dom;
34187 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34190 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34193 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34196 this.dragSpecs = {};
34199 * @private The adapter to use to positon and resize elements
34201 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34202 this.adapter.init(this);
34204 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34206 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34207 this.el.addClass("roo-splitbar-h");
34210 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34211 this.el.addClass("roo-splitbar-v");
34217 * Fires when the splitter is moved (alias for {@link #event-moved})
34218 * @param {Roo.bootstrap.SplitBar} this
34219 * @param {Number} newSize the new width or height
34224 * Fires when the splitter is moved
34225 * @param {Roo.bootstrap.SplitBar} this
34226 * @param {Number} newSize the new width or height
34230 * @event beforeresize
34231 * Fires before the splitter is dragged
34232 * @param {Roo.bootstrap.SplitBar} this
34234 "beforeresize" : true,
34236 "beforeapply" : true
34239 Roo.util.Observable.call(this);
34242 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34243 onStartProxyDrag : function(x, y){
34244 this.fireEvent("beforeresize", this);
34246 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34248 o.enableDisplayMode("block");
34249 // all splitbars share the same overlay
34250 Roo.bootstrap.SplitBar.prototype.overlay = o;
34252 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34253 this.overlay.show();
34254 Roo.get(this.proxy).setDisplayed("block");
34255 var size = this.adapter.getElementSize(this);
34256 this.activeMinSize = this.getMinimumSize();;
34257 this.activeMaxSize = this.getMaximumSize();;
34258 var c1 = size - this.activeMinSize;
34259 var c2 = Math.max(this.activeMaxSize - size, 0);
34260 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34261 this.dd.resetConstraints();
34262 this.dd.setXConstraint(
34263 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34264 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34266 this.dd.setYConstraint(0, 0);
34268 this.dd.resetConstraints();
34269 this.dd.setXConstraint(0, 0);
34270 this.dd.setYConstraint(
34271 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34272 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34275 this.dragSpecs.startSize = size;
34276 this.dragSpecs.startPoint = [x, y];
34277 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34281 * @private Called after the drag operation by the DDProxy
34283 onEndProxyDrag : function(e){
34284 Roo.get(this.proxy).setDisplayed(false);
34285 var endPoint = Roo.lib.Event.getXY(e);
34287 this.overlay.hide();
34290 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34291 newSize = this.dragSpecs.startSize +
34292 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34293 endPoint[0] - this.dragSpecs.startPoint[0] :
34294 this.dragSpecs.startPoint[0] - endPoint[0]
34297 newSize = this.dragSpecs.startSize +
34298 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34299 endPoint[1] - this.dragSpecs.startPoint[1] :
34300 this.dragSpecs.startPoint[1] - endPoint[1]
34303 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34304 if(newSize != this.dragSpecs.startSize){
34305 if(this.fireEvent('beforeapply', this, newSize) !== false){
34306 this.adapter.setElementSize(this, newSize);
34307 this.fireEvent("moved", this, newSize);
34308 this.fireEvent("resize", this, newSize);
34314 * Get the adapter this SplitBar uses
34315 * @return The adapter object
34317 getAdapter : function(){
34318 return this.adapter;
34322 * Set the adapter this SplitBar uses
34323 * @param {Object} adapter A SplitBar adapter object
34325 setAdapter : function(adapter){
34326 this.adapter = adapter;
34327 this.adapter.init(this);
34331 * Gets the minimum size for the resizing element
34332 * @return {Number} The minimum size
34334 getMinimumSize : function(){
34335 return this.minSize;
34339 * Sets the minimum size for the resizing element
34340 * @param {Number} minSize The minimum size
34342 setMinimumSize : function(minSize){
34343 this.minSize = minSize;
34347 * Gets the maximum size for the resizing element
34348 * @return {Number} The maximum size
34350 getMaximumSize : function(){
34351 return this.maxSize;
34355 * Sets the maximum size for the resizing element
34356 * @param {Number} maxSize The maximum size
34358 setMaximumSize : function(maxSize){
34359 this.maxSize = maxSize;
34363 * Sets the initialize size for the resizing element
34364 * @param {Number} size The initial size
34366 setCurrentSize : function(size){
34367 var oldAnimate = this.animate;
34368 this.animate = false;
34369 this.adapter.setElementSize(this, size);
34370 this.animate = oldAnimate;
34374 * Destroy this splitbar.
34375 * @param {Boolean} removeEl True to remove the element
34377 destroy : function(removeEl){
34379 this.shim.remove();
34382 this.proxy.parentNode.removeChild(this.proxy);
34390 * @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.
34392 Roo.bootstrap.SplitBar.createProxy = function(dir){
34393 var proxy = new Roo.Element(document.createElement("div"));
34394 proxy.unselectable();
34395 var cls = 'roo-splitbar-proxy';
34396 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34397 document.body.appendChild(proxy.dom);
34402 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34403 * Default Adapter. It assumes the splitter and resizing element are not positioned
34404 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34406 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34409 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34410 // do nothing for now
34411 init : function(s){
34415 * Called before drag operations to get the current size of the resizing element.
34416 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34418 getElementSize : function(s){
34419 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34420 return s.resizingEl.getWidth();
34422 return s.resizingEl.getHeight();
34427 * Called after drag operations to set the size of the resizing element.
34428 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34429 * @param {Number} newSize The new size to set
34430 * @param {Function} onComplete A function to be invoked when resizing is complete
34432 setElementSize : function(s, newSize, onComplete){
34433 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34435 s.resizingEl.setWidth(newSize);
34437 onComplete(s, newSize);
34440 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34445 s.resizingEl.setHeight(newSize);
34447 onComplete(s, newSize);
34450 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34457 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34458 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34459 * Adapter that moves the splitter element to align with the resized sizing element.
34460 * Used with an absolute positioned SplitBar.
34461 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34462 * document.body, make sure you assign an id to the body element.
34464 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34465 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34466 this.container = Roo.get(container);
34469 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34470 init : function(s){
34471 this.basic.init(s);
34474 getElementSize : function(s){
34475 return this.basic.getElementSize(s);
34478 setElementSize : function(s, newSize, onComplete){
34479 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34482 moveSplitter : function(s){
34483 var yes = Roo.bootstrap.SplitBar;
34484 switch(s.placement){
34486 s.el.setX(s.resizingEl.getRight());
34489 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34492 s.el.setY(s.resizingEl.getBottom());
34495 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34502 * Orientation constant - Create a vertical SplitBar
34506 Roo.bootstrap.SplitBar.VERTICAL = 1;
34509 * Orientation constant - Create a horizontal SplitBar
34513 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34516 * Placement constant - The resizing element is to the left of the splitter element
34520 Roo.bootstrap.SplitBar.LEFT = 1;
34523 * Placement constant - The resizing element is to the right of the splitter element
34527 Roo.bootstrap.SplitBar.RIGHT = 2;
34530 * Placement constant - The resizing element is positioned above the splitter element
34534 Roo.bootstrap.SplitBar.TOP = 3;
34537 * Placement constant - The resizing element is positioned under splitter element
34541 Roo.bootstrap.SplitBar.BOTTOM = 4;
34542 Roo.namespace("Roo.bootstrap.layout");/*
34544 * Ext JS Library 1.1.1
34545 * Copyright(c) 2006-2007, Ext JS, LLC.
34547 * Originally Released Under LGPL - original licence link has changed is not relivant.
34550 * <script type="text/javascript">
34554 * @class Roo.bootstrap.layout.Manager
34555 * @extends Roo.bootstrap.Component
34556 * Base class for layout managers.
34558 Roo.bootstrap.layout.Manager = function(config)
34560 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34566 /** false to disable window resize monitoring @type Boolean */
34567 this.monitorWindowResize = true;
34572 * Fires when a layout is performed.
34573 * @param {Roo.LayoutManager} this
34577 * @event regionresized
34578 * Fires when the user resizes a region.
34579 * @param {Roo.LayoutRegion} region The resized region
34580 * @param {Number} newSize The new size (width for east/west, height for north/south)
34582 "regionresized" : true,
34584 * @event regioncollapsed
34585 * Fires when a region is collapsed.
34586 * @param {Roo.LayoutRegion} region The collapsed region
34588 "regioncollapsed" : true,
34590 * @event regionexpanded
34591 * Fires when a region is expanded.
34592 * @param {Roo.LayoutRegion} region The expanded region
34594 "regionexpanded" : true
34596 this.updating = false;
34599 this.el = Roo.get(config.el);
34605 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34610 monitorWindowResize : true,
34616 onRender : function(ct, position)
34619 this.el = Roo.get(ct);
34622 //this.fireEvent('render',this);
34626 initEvents: function()
34630 // ie scrollbar fix
34631 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34632 document.body.scroll = "no";
34633 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34634 this.el.position('relative');
34636 this.id = this.el.id;
34637 this.el.addClass("roo-layout-container");
34638 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34639 if(this.el.dom != document.body ) {
34640 this.el.on('resize', this.layout,this);
34641 this.el.on('show', this.layout,this);
34647 * Returns true if this layout is currently being updated
34648 * @return {Boolean}
34650 isUpdating : function(){
34651 return this.updating;
34655 * Suspend the LayoutManager from doing auto-layouts while
34656 * making multiple add or remove calls
34658 beginUpdate : function(){
34659 this.updating = true;
34663 * Restore auto-layouts and optionally disable the manager from performing a layout
34664 * @param {Boolean} noLayout true to disable a layout update
34666 endUpdate : function(noLayout){
34667 this.updating = false;
34673 layout: function(){
34677 onRegionResized : function(region, newSize){
34678 this.fireEvent("regionresized", region, newSize);
34682 onRegionCollapsed : function(region){
34683 this.fireEvent("regioncollapsed", region);
34686 onRegionExpanded : function(region){
34687 this.fireEvent("regionexpanded", region);
34691 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34692 * performs box-model adjustments.
34693 * @return {Object} The size as an object {width: (the width), height: (the height)}
34695 getViewSize : function()
34698 if(this.el.dom != document.body){
34699 size = this.el.getSize();
34701 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34703 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34704 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34709 * Returns the Element this layout is bound to.
34710 * @return {Roo.Element}
34712 getEl : function(){
34717 * Returns the specified region.
34718 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34719 * @return {Roo.LayoutRegion}
34721 getRegion : function(target){
34722 return this.regions[target.toLowerCase()];
34725 onWindowResize : function(){
34726 if(this.monitorWindowResize){
34733 * Ext JS Library 1.1.1
34734 * Copyright(c) 2006-2007, Ext JS, LLC.
34736 * Originally Released Under LGPL - original licence link has changed is not relivant.
34739 * <script type="text/javascript">
34742 * @class Roo.bootstrap.layout.Border
34743 * @extends Roo.bootstrap.layout.Manager
34744 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34745 * please see: examples/bootstrap/nested.html<br><br>
34747 <b>The container the layout is rendered into can be either the body element or any other element.
34748 If it is not the body element, the container needs to either be an absolute positioned element,
34749 or you will need to add "position:relative" to the css of the container. You will also need to specify
34750 the container size if it is not the body element.</b>
34753 * Create a new Border
34754 * @param {Object} config Configuration options
34756 Roo.bootstrap.layout.Border = function(config){
34757 config = config || {};
34758 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34762 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34763 if(config[region]){
34764 config[region].region = region;
34765 this.addRegion(config[region]);
34771 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34773 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34775 * Creates and adds a new region if it doesn't already exist.
34776 * @param {String} target The target region key (north, south, east, west or center).
34777 * @param {Object} config The regions config object
34778 * @return {BorderLayoutRegion} The new region
34780 addRegion : function(config)
34782 if(!this.regions[config.region]){
34783 var r = this.factory(config);
34784 this.bindRegion(r);
34786 return this.regions[config.region];
34790 bindRegion : function(r){
34791 this.regions[r.config.region] = r;
34793 r.on("visibilitychange", this.layout, this);
34794 r.on("paneladded", this.layout, this);
34795 r.on("panelremoved", this.layout, this);
34796 r.on("invalidated", this.layout, this);
34797 r.on("resized", this.onRegionResized, this);
34798 r.on("collapsed", this.onRegionCollapsed, this);
34799 r.on("expanded", this.onRegionExpanded, this);
34803 * Performs a layout update.
34805 layout : function()
34807 if(this.updating) {
34811 // render all the rebions if they have not been done alreayd?
34812 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34813 if(this.regions[region] && !this.regions[region].bodyEl){
34814 this.regions[region].onRender(this.el)
34818 var size = this.getViewSize();
34819 var w = size.width;
34820 var h = size.height;
34825 //var x = 0, y = 0;
34827 var rs = this.regions;
34828 var north = rs["north"];
34829 var south = rs["south"];
34830 var west = rs["west"];
34831 var east = rs["east"];
34832 var center = rs["center"];
34833 //if(this.hideOnLayout){ // not supported anymore
34834 //c.el.setStyle("display", "none");
34836 if(north && north.isVisible()){
34837 var b = north.getBox();
34838 var m = north.getMargins();
34839 b.width = w - (m.left+m.right);
34842 centerY = b.height + b.y + m.bottom;
34843 centerH -= centerY;
34844 north.updateBox(this.safeBox(b));
34846 if(south && south.isVisible()){
34847 var b = south.getBox();
34848 var m = south.getMargins();
34849 b.width = w - (m.left+m.right);
34851 var totalHeight = (b.height + m.top + m.bottom);
34852 b.y = h - totalHeight + m.top;
34853 centerH -= totalHeight;
34854 south.updateBox(this.safeBox(b));
34856 if(west && west.isVisible()){
34857 var b = west.getBox();
34858 var m = west.getMargins();
34859 b.height = centerH - (m.top+m.bottom);
34861 b.y = centerY + m.top;
34862 var totalWidth = (b.width + m.left + m.right);
34863 centerX += totalWidth;
34864 centerW -= totalWidth;
34865 west.updateBox(this.safeBox(b));
34867 if(east && east.isVisible()){
34868 var b = east.getBox();
34869 var m = east.getMargins();
34870 b.height = centerH - (m.top+m.bottom);
34871 var totalWidth = (b.width + m.left + m.right);
34872 b.x = w - totalWidth + m.left;
34873 b.y = centerY + m.top;
34874 centerW -= totalWidth;
34875 east.updateBox(this.safeBox(b));
34878 var m = center.getMargins();
34880 x: centerX + m.left,
34881 y: centerY + m.top,
34882 width: centerW - (m.left+m.right),
34883 height: centerH - (m.top+m.bottom)
34885 //if(this.hideOnLayout){
34886 //center.el.setStyle("display", "block");
34888 center.updateBox(this.safeBox(centerBox));
34891 this.fireEvent("layout", this);
34895 safeBox : function(box){
34896 box.width = Math.max(0, box.width);
34897 box.height = Math.max(0, box.height);
34902 * Adds a ContentPanel (or subclass) to this layout.
34903 * @param {String} target The target region key (north, south, east, west or center).
34904 * @param {Roo.ContentPanel} panel The panel to add
34905 * @return {Roo.ContentPanel} The added panel
34907 add : function(target, panel){
34909 target = target.toLowerCase();
34910 return this.regions[target].add(panel);
34914 * Remove a ContentPanel (or subclass) to this layout.
34915 * @param {String} target The target region key (north, south, east, west or center).
34916 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34917 * @return {Roo.ContentPanel} The removed panel
34919 remove : function(target, panel){
34920 target = target.toLowerCase();
34921 return this.regions[target].remove(panel);
34925 * Searches all regions for a panel with the specified id
34926 * @param {String} panelId
34927 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34929 findPanel : function(panelId){
34930 var rs = this.regions;
34931 for(var target in rs){
34932 if(typeof rs[target] != "function"){
34933 var p = rs[target].getPanel(panelId);
34943 * Searches all regions for a panel with the specified id and activates (shows) it.
34944 * @param {String/ContentPanel} panelId The panels id or the panel itself
34945 * @return {Roo.ContentPanel} The shown panel or null
34947 showPanel : function(panelId) {
34948 var rs = this.regions;
34949 for(var target in rs){
34950 var r = rs[target];
34951 if(typeof r != "function"){
34952 if(r.hasPanel(panelId)){
34953 return r.showPanel(panelId);
34961 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34962 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34965 restoreState : function(provider){
34967 provider = Roo.state.Manager;
34969 var sm = new Roo.LayoutStateManager();
34970 sm.init(this, provider);
34976 * Adds a xtype elements to the layout.
34980 xtype : 'ContentPanel',
34987 xtype : 'NestedLayoutPanel',
34993 items : [ ... list of content panels or nested layout panels.. ]
34997 * @param {Object} cfg Xtype definition of item to add.
34999 addxtype : function(cfg)
35001 // basically accepts a pannel...
35002 // can accept a layout region..!?!?
35003 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35006 // theory? children can only be panels??
35008 //if (!cfg.xtype.match(/Panel$/)) {
35013 if (typeof(cfg.region) == 'undefined') {
35014 Roo.log("Failed to add Panel, region was not set");
35018 var region = cfg.region;
35024 xitems = cfg.items;
35031 case 'Content': // ContentPanel (el, cfg)
35032 case 'Scroll': // ContentPanel (el, cfg)
35034 cfg.autoCreate = true;
35035 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35037 // var el = this.el.createChild();
35038 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35041 this.add(region, ret);
35045 case 'TreePanel': // our new panel!
35046 cfg.el = this.el.createChild();
35047 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35048 this.add(region, ret);
35053 // create a new Layout (which is a Border Layout...
35055 var clayout = cfg.layout;
35056 clayout.el = this.el.createChild();
35057 clayout.items = clayout.items || [];
35061 // replace this exitems with the clayout ones..
35062 xitems = clayout.items;
35064 // force background off if it's in center...
35065 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35066 cfg.background = false;
35068 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35071 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35072 //console.log('adding nested layout panel ' + cfg.toSource());
35073 this.add(region, ret);
35074 nb = {}; /// find first...
35079 // needs grid and region
35081 //var el = this.getRegion(region).el.createChild();
35083 *var el = this.el.createChild();
35084 // create the grid first...
35085 cfg.grid.container = el;
35086 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35089 if (region == 'center' && this.active ) {
35090 cfg.background = false;
35093 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35095 this.add(region, ret);
35097 if (cfg.background) {
35098 // render grid on panel activation (if panel background)
35099 ret.on('activate', function(gp) {
35100 if (!gp.grid.rendered) {
35101 // gp.grid.render(el);
35105 // cfg.grid.render(el);
35111 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35112 // it was the old xcomponent building that caused this before.
35113 // espeically if border is the top element in the tree.
35123 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35125 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35126 this.add(region, ret);
35130 throw "Can not add '" + cfg.xtype + "' to Border";
35136 this.beginUpdate();
35140 Roo.each(xitems, function(i) {
35141 region = nb && i.region ? i.region : false;
35143 var add = ret.addxtype(i);
35146 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35147 if (!i.background) {
35148 abn[region] = nb[region] ;
35155 // make the last non-background panel active..
35156 //if (nb) { Roo.log(abn); }
35159 for(var r in abn) {
35160 region = this.getRegion(r);
35162 // tried using nb[r], but it does not work..
35164 region.showPanel(abn[r]);
35175 factory : function(cfg)
35178 var validRegions = Roo.bootstrap.layout.Border.regions;
35180 var target = cfg.region;
35183 var r = Roo.bootstrap.layout;
35187 return new r.North(cfg);
35189 return new r.South(cfg);
35191 return new r.East(cfg);
35193 return new r.West(cfg);
35195 return new r.Center(cfg);
35197 throw 'Layout region "'+target+'" not supported.';
35204 * Ext JS Library 1.1.1
35205 * Copyright(c) 2006-2007, Ext JS, LLC.
35207 * Originally Released Under LGPL - original licence link has changed is not relivant.
35210 * <script type="text/javascript">
35214 * @class Roo.bootstrap.layout.Basic
35215 * @extends Roo.util.Observable
35216 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35217 * and does not have a titlebar, tabs or any other features. All it does is size and position
35218 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35219 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35220 * @cfg {string} region the region that it inhabits..
35221 * @cfg {bool} skipConfig skip config?
35225 Roo.bootstrap.layout.Basic = function(config){
35227 this.mgr = config.mgr;
35229 this.position = config.region;
35231 var skipConfig = config.skipConfig;
35235 * @scope Roo.BasicLayoutRegion
35239 * @event beforeremove
35240 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35241 * @param {Roo.LayoutRegion} this
35242 * @param {Roo.ContentPanel} panel The panel
35243 * @param {Object} e The cancel event object
35245 "beforeremove" : true,
35247 * @event invalidated
35248 * Fires when the layout for this region is changed.
35249 * @param {Roo.LayoutRegion} this
35251 "invalidated" : true,
35253 * @event visibilitychange
35254 * Fires when this region is shown or hidden
35255 * @param {Roo.LayoutRegion} this
35256 * @param {Boolean} visibility true or false
35258 "visibilitychange" : true,
35260 * @event paneladded
35261 * Fires when a panel is added.
35262 * @param {Roo.LayoutRegion} this
35263 * @param {Roo.ContentPanel} panel The panel
35265 "paneladded" : true,
35267 * @event panelremoved
35268 * Fires when a panel is removed.
35269 * @param {Roo.LayoutRegion} this
35270 * @param {Roo.ContentPanel} panel The panel
35272 "panelremoved" : true,
35274 * @event beforecollapse
35275 * Fires when this region before collapse.
35276 * @param {Roo.LayoutRegion} this
35278 "beforecollapse" : true,
35281 * Fires when this region is collapsed.
35282 * @param {Roo.LayoutRegion} this
35284 "collapsed" : true,
35287 * Fires when this region is expanded.
35288 * @param {Roo.LayoutRegion} this
35293 * Fires when this region is slid into view.
35294 * @param {Roo.LayoutRegion} this
35296 "slideshow" : true,
35299 * Fires when this region slides out of view.
35300 * @param {Roo.LayoutRegion} this
35302 "slidehide" : true,
35304 * @event panelactivated
35305 * Fires when a panel is activated.
35306 * @param {Roo.LayoutRegion} this
35307 * @param {Roo.ContentPanel} panel The activated panel
35309 "panelactivated" : true,
35312 * Fires when the user resizes this region.
35313 * @param {Roo.LayoutRegion} this
35314 * @param {Number} newSize The new size (width for east/west, height for north/south)
35318 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35319 this.panels = new Roo.util.MixedCollection();
35320 this.panels.getKey = this.getPanelId.createDelegate(this);
35322 this.activePanel = null;
35323 // ensure listeners are added...
35325 if (config.listeners || config.events) {
35326 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35327 listeners : config.listeners || {},
35328 events : config.events || {}
35332 if(skipConfig !== true){
35333 this.applyConfig(config);
35337 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35339 getPanelId : function(p){
35343 applyConfig : function(config){
35344 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35345 this.config = config;
35350 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35351 * the width, for horizontal (north, south) the height.
35352 * @param {Number} newSize The new width or height
35354 resizeTo : function(newSize){
35355 var el = this.el ? this.el :
35356 (this.activePanel ? this.activePanel.getEl() : null);
35358 switch(this.position){
35361 el.setWidth(newSize);
35362 this.fireEvent("resized", this, newSize);
35366 el.setHeight(newSize);
35367 this.fireEvent("resized", this, newSize);
35373 getBox : function(){
35374 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35377 getMargins : function(){
35378 return this.margins;
35381 updateBox : function(box){
35383 var el = this.activePanel.getEl();
35384 el.dom.style.left = box.x + "px";
35385 el.dom.style.top = box.y + "px";
35386 this.activePanel.setSize(box.width, box.height);
35390 * Returns the container element for this region.
35391 * @return {Roo.Element}
35393 getEl : function(){
35394 return this.activePanel;
35398 * Returns true if this region is currently visible.
35399 * @return {Boolean}
35401 isVisible : function(){
35402 return this.activePanel ? true : false;
35405 setActivePanel : function(panel){
35406 panel = this.getPanel(panel);
35407 if(this.activePanel && this.activePanel != panel){
35408 this.activePanel.setActiveState(false);
35409 this.activePanel.getEl().setLeftTop(-10000,-10000);
35411 this.activePanel = panel;
35412 panel.setActiveState(true);
35414 panel.setSize(this.box.width, this.box.height);
35416 this.fireEvent("panelactivated", this, panel);
35417 this.fireEvent("invalidated");
35421 * Show the specified panel.
35422 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35423 * @return {Roo.ContentPanel} The shown panel or null
35425 showPanel : function(panel){
35426 panel = this.getPanel(panel);
35428 this.setActivePanel(panel);
35434 * Get the active panel for this region.
35435 * @return {Roo.ContentPanel} The active panel or null
35437 getActivePanel : function(){
35438 return this.activePanel;
35442 * Add the passed ContentPanel(s)
35443 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35444 * @return {Roo.ContentPanel} The panel added (if only one was added)
35446 add : function(panel){
35447 if(arguments.length > 1){
35448 for(var i = 0, len = arguments.length; i < len; i++) {
35449 this.add(arguments[i]);
35453 if(this.hasPanel(panel)){
35454 this.showPanel(panel);
35457 var el = panel.getEl();
35458 if(el.dom.parentNode != this.mgr.el.dom){
35459 this.mgr.el.dom.appendChild(el.dom);
35461 if(panel.setRegion){
35462 panel.setRegion(this);
35464 this.panels.add(panel);
35465 el.setStyle("position", "absolute");
35466 if(!panel.background){
35467 this.setActivePanel(panel);
35468 if(this.config.initialSize && this.panels.getCount()==1){
35469 this.resizeTo(this.config.initialSize);
35472 this.fireEvent("paneladded", this, panel);
35477 * Returns true if the panel is in this region.
35478 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35479 * @return {Boolean}
35481 hasPanel : function(panel){
35482 if(typeof panel == "object"){ // must be panel obj
35483 panel = panel.getId();
35485 return this.getPanel(panel) ? true : false;
35489 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35490 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35491 * @param {Boolean} preservePanel Overrides the config preservePanel option
35492 * @return {Roo.ContentPanel} The panel that was removed
35494 remove : function(panel, preservePanel){
35495 panel = this.getPanel(panel);
35500 this.fireEvent("beforeremove", this, panel, e);
35501 if(e.cancel === true){
35504 var panelId = panel.getId();
35505 this.panels.removeKey(panelId);
35510 * Returns the panel specified or null if it's not in this region.
35511 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35512 * @return {Roo.ContentPanel}
35514 getPanel : function(id){
35515 if(typeof id == "object"){ // must be panel obj
35518 return this.panels.get(id);
35522 * Returns this regions position (north/south/east/west/center).
35525 getPosition: function(){
35526 return this.position;
35530 * Ext JS Library 1.1.1
35531 * Copyright(c) 2006-2007, Ext JS, LLC.
35533 * Originally Released Under LGPL - original licence link has changed is not relivant.
35536 * <script type="text/javascript">
35540 * @class Roo.bootstrap.layout.Region
35541 * @extends Roo.bootstrap.layout.Basic
35542 * This class represents a region in a layout manager.
35544 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35545 * @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})
35546 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35547 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35548 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35549 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35550 * @cfg {String} title The title for the region (overrides panel titles)
35551 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35552 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35553 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35554 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35555 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35556 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35557 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35558 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35559 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35560 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35562 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35563 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35564 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35565 * @cfg {Number} width For East/West panels
35566 * @cfg {Number} height For North/South panels
35567 * @cfg {Boolean} split To show the splitter
35568 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35570 * @cfg {string} cls Extra CSS classes to add to region
35572 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35573 * @cfg {string} region the region that it inhabits..
35576 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35577 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35579 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35580 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35581 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35583 Roo.bootstrap.layout.Region = function(config)
35585 this.applyConfig(config);
35587 var mgr = config.mgr;
35588 var pos = config.region;
35589 config.skipConfig = true;
35590 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35593 this.onRender(mgr.el);
35596 this.visible = true;
35597 this.collapsed = false;
35598 this.unrendered_panels = [];
35601 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35603 position: '', // set by wrapper (eg. north/south etc..)
35604 unrendered_panels : null, // unrendered panels.
35605 createBody : function(){
35606 /** This region's body element
35607 * @type Roo.Element */
35608 this.bodyEl = this.el.createChild({
35610 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35614 onRender: function(ctr, pos)
35616 var dh = Roo.DomHelper;
35617 /** This region's container element
35618 * @type Roo.Element */
35619 this.el = dh.append(ctr.dom, {
35621 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35623 /** This region's title element
35624 * @type Roo.Element */
35626 this.titleEl = dh.append(this.el.dom,
35629 unselectable: "on",
35630 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35632 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35633 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35636 this.titleEl.enableDisplayMode();
35637 /** This region's title text element
35638 * @type HTMLElement */
35639 this.titleTextEl = this.titleEl.dom.firstChild;
35640 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35642 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35643 this.closeBtn.enableDisplayMode();
35644 this.closeBtn.on("click", this.closeClicked, this);
35645 this.closeBtn.hide();
35647 this.createBody(this.config);
35648 if(this.config.hideWhenEmpty){
35650 this.on("paneladded", this.validateVisibility, this);
35651 this.on("panelremoved", this.validateVisibility, this);
35653 if(this.autoScroll){
35654 this.bodyEl.setStyle("overflow", "auto");
35656 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35658 //if(c.titlebar !== false){
35659 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35660 this.titleEl.hide();
35662 this.titleEl.show();
35663 if(this.config.title){
35664 this.titleTextEl.innerHTML = this.config.title;
35668 if(this.config.collapsed){
35669 this.collapse(true);
35671 if(this.config.hidden){
35675 if (this.unrendered_panels && this.unrendered_panels.length) {
35676 for (var i =0;i< this.unrendered_panels.length; i++) {
35677 this.add(this.unrendered_panels[i]);
35679 this.unrendered_panels = null;
35685 applyConfig : function(c)
35688 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35689 var dh = Roo.DomHelper;
35690 if(c.titlebar !== false){
35691 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35692 this.collapseBtn.on("click", this.collapse, this);
35693 this.collapseBtn.enableDisplayMode();
35695 if(c.showPin === true || this.showPin){
35696 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35697 this.stickBtn.enableDisplayMode();
35698 this.stickBtn.on("click", this.expand, this);
35699 this.stickBtn.hide();
35704 /** This region's collapsed element
35705 * @type Roo.Element */
35708 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35709 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35712 if(c.floatable !== false){
35713 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35714 this.collapsedEl.on("click", this.collapseClick, this);
35717 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35718 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35719 id: "message", unselectable: "on", style:{"float":"left"}});
35720 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35722 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35723 this.expandBtn.on("click", this.expand, this);
35727 if(this.collapseBtn){
35728 this.collapseBtn.setVisible(c.collapsible == true);
35731 this.cmargins = c.cmargins || this.cmargins ||
35732 (this.position == "west" || this.position == "east" ?
35733 {top: 0, left: 2, right:2, bottom: 0} :
35734 {top: 2, left: 0, right:0, bottom: 2});
35736 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35739 this.bottomTabs = c.tabPosition != "top";
35741 this.autoScroll = c.autoScroll || false;
35746 this.duration = c.duration || .30;
35747 this.slideDuration = c.slideDuration || .45;
35752 * Returns true if this region is currently visible.
35753 * @return {Boolean}
35755 isVisible : function(){
35756 return this.visible;
35760 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35761 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35763 //setCollapsedTitle : function(title){
35764 // title = title || " ";
35765 // if(this.collapsedTitleTextEl){
35766 // this.collapsedTitleTextEl.innerHTML = title;
35770 getBox : function(){
35772 // if(!this.collapsed){
35773 b = this.el.getBox(false, true);
35775 // b = this.collapsedEl.getBox(false, true);
35780 getMargins : function(){
35781 return this.margins;
35782 //return this.collapsed ? this.cmargins : this.margins;
35785 highlight : function(){
35786 this.el.addClass("x-layout-panel-dragover");
35789 unhighlight : function(){
35790 this.el.removeClass("x-layout-panel-dragover");
35793 updateBox : function(box)
35795 if (!this.bodyEl) {
35796 return; // not rendered yet..
35800 if(!this.collapsed){
35801 this.el.dom.style.left = box.x + "px";
35802 this.el.dom.style.top = box.y + "px";
35803 this.updateBody(box.width, box.height);
35805 this.collapsedEl.dom.style.left = box.x + "px";
35806 this.collapsedEl.dom.style.top = box.y + "px";
35807 this.collapsedEl.setSize(box.width, box.height);
35810 this.tabs.autoSizeTabs();
35814 updateBody : function(w, h)
35817 this.el.setWidth(w);
35818 w -= this.el.getBorderWidth("rl");
35819 if(this.config.adjustments){
35820 w += this.config.adjustments[0];
35823 if(h !== null && h > 0){
35824 this.el.setHeight(h);
35825 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35826 h -= this.el.getBorderWidth("tb");
35827 if(this.config.adjustments){
35828 h += this.config.adjustments[1];
35830 this.bodyEl.setHeight(h);
35832 h = this.tabs.syncHeight(h);
35835 if(this.panelSize){
35836 w = w !== null ? w : this.panelSize.width;
35837 h = h !== null ? h : this.panelSize.height;
35839 if(this.activePanel){
35840 var el = this.activePanel.getEl();
35841 w = w !== null ? w : el.getWidth();
35842 h = h !== null ? h : el.getHeight();
35843 this.panelSize = {width: w, height: h};
35844 this.activePanel.setSize(w, h);
35846 if(Roo.isIE && this.tabs){
35847 this.tabs.el.repaint();
35852 * Returns the container element for this region.
35853 * @return {Roo.Element}
35855 getEl : function(){
35860 * Hides this region.
35863 //if(!this.collapsed){
35864 this.el.dom.style.left = "-2000px";
35867 // this.collapsedEl.dom.style.left = "-2000px";
35868 // this.collapsedEl.hide();
35870 this.visible = false;
35871 this.fireEvent("visibilitychange", this, false);
35875 * Shows this region if it was previously hidden.
35878 //if(!this.collapsed){
35881 // this.collapsedEl.show();
35883 this.visible = true;
35884 this.fireEvent("visibilitychange", this, true);
35887 closeClicked : function(){
35888 if(this.activePanel){
35889 this.remove(this.activePanel);
35893 collapseClick : function(e){
35895 e.stopPropagation();
35898 e.stopPropagation();
35904 * Collapses this region.
35905 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35908 collapse : function(skipAnim, skipCheck = false){
35909 if(this.collapsed) {
35913 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35915 this.collapsed = true;
35917 this.split.el.hide();
35919 if(this.config.animate && skipAnim !== true){
35920 this.fireEvent("invalidated", this);
35921 this.animateCollapse();
35923 this.el.setLocation(-20000,-20000);
35925 this.collapsedEl.show();
35926 this.fireEvent("collapsed", this);
35927 this.fireEvent("invalidated", this);
35933 animateCollapse : function(){
35938 * Expands this region if it was previously collapsed.
35939 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35940 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35943 expand : function(e, skipAnim){
35945 e.stopPropagation();
35947 if(!this.collapsed || this.el.hasActiveFx()) {
35951 this.afterSlideIn();
35954 this.collapsed = false;
35955 if(this.config.animate && skipAnim !== true){
35956 this.animateExpand();
35960 this.split.el.show();
35962 this.collapsedEl.setLocation(-2000,-2000);
35963 this.collapsedEl.hide();
35964 this.fireEvent("invalidated", this);
35965 this.fireEvent("expanded", this);
35969 animateExpand : function(){
35973 initTabs : function()
35975 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35977 var ts = new Roo.bootstrap.panel.Tabs({
35978 el: this.bodyEl.dom,
35979 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35980 disableTooltips: this.config.disableTabTips,
35981 toolbar : this.config.toolbar
35984 if(this.config.hideTabs){
35985 ts.stripWrap.setDisplayed(false);
35988 ts.resizeTabs = this.config.resizeTabs === true;
35989 ts.minTabWidth = this.config.minTabWidth || 40;
35990 ts.maxTabWidth = this.config.maxTabWidth || 250;
35991 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35992 ts.monitorResize = false;
35993 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35994 ts.bodyEl.addClass('roo-layout-tabs-body');
35995 this.panels.each(this.initPanelAsTab, this);
35998 initPanelAsTab : function(panel){
35999 var ti = this.tabs.addTab(
36003 this.config.closeOnTab && panel.isClosable(),
36006 if(panel.tabTip !== undefined){
36007 ti.setTooltip(panel.tabTip);
36009 ti.on("activate", function(){
36010 this.setActivePanel(panel);
36013 if(this.config.closeOnTab){
36014 ti.on("beforeclose", function(t, e){
36016 this.remove(panel);
36020 panel.tabItem = ti;
36025 updatePanelTitle : function(panel, title)
36027 if(this.activePanel == panel){
36028 this.updateTitle(title);
36031 var ti = this.tabs.getTab(panel.getEl().id);
36033 if(panel.tabTip !== undefined){
36034 ti.setTooltip(panel.tabTip);
36039 updateTitle : function(title){
36040 if(this.titleTextEl && !this.config.title){
36041 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36045 setActivePanel : function(panel)
36047 panel = this.getPanel(panel);
36048 if(this.activePanel && this.activePanel != panel){
36049 if(this.activePanel.setActiveState(false) === false){
36053 this.activePanel = panel;
36054 panel.setActiveState(true);
36055 if(this.panelSize){
36056 panel.setSize(this.panelSize.width, this.panelSize.height);
36059 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36061 this.updateTitle(panel.getTitle());
36063 this.fireEvent("invalidated", this);
36065 this.fireEvent("panelactivated", this, panel);
36069 * Shows the specified panel.
36070 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36071 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36073 showPanel : function(panel)
36075 panel = this.getPanel(panel);
36078 var tab = this.tabs.getTab(panel.getEl().id);
36079 if(tab.isHidden()){
36080 this.tabs.unhideTab(tab.id);
36084 this.setActivePanel(panel);
36091 * Get the active panel for this region.
36092 * @return {Roo.ContentPanel} The active panel or null
36094 getActivePanel : function(){
36095 return this.activePanel;
36098 validateVisibility : function(){
36099 if(this.panels.getCount() < 1){
36100 this.updateTitle(" ");
36101 this.closeBtn.hide();
36104 if(!this.isVisible()){
36111 * Adds the passed ContentPanel(s) to this region.
36112 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36113 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36115 add : function(panel)
36117 if(arguments.length > 1){
36118 for(var i = 0, len = arguments.length; i < len; i++) {
36119 this.add(arguments[i]);
36124 // if we have not been rendered yet, then we can not really do much of this..
36125 if (!this.bodyEl) {
36126 this.unrendered_panels.push(panel);
36133 if(this.hasPanel(panel)){
36134 this.showPanel(panel);
36137 panel.setRegion(this);
36138 this.panels.add(panel);
36139 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36140 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36141 // and hide them... ???
36142 this.bodyEl.dom.appendChild(panel.getEl().dom);
36143 if(panel.background !== true){
36144 this.setActivePanel(panel);
36146 this.fireEvent("paneladded", this, panel);
36153 this.initPanelAsTab(panel);
36157 if(panel.background !== true){
36158 this.tabs.activate(panel.getEl().id);
36160 this.fireEvent("paneladded", this, panel);
36165 * Hides the tab for the specified panel.
36166 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36168 hidePanel : function(panel){
36169 if(this.tabs && (panel = this.getPanel(panel))){
36170 this.tabs.hideTab(panel.getEl().id);
36175 * Unhides the tab for a previously hidden panel.
36176 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36178 unhidePanel : function(panel){
36179 if(this.tabs && (panel = this.getPanel(panel))){
36180 this.tabs.unhideTab(panel.getEl().id);
36184 clearPanels : function(){
36185 while(this.panels.getCount() > 0){
36186 this.remove(this.panels.first());
36191 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36192 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36193 * @param {Boolean} preservePanel Overrides the config preservePanel option
36194 * @return {Roo.ContentPanel} The panel that was removed
36196 remove : function(panel, preservePanel)
36198 panel = this.getPanel(panel);
36203 this.fireEvent("beforeremove", this, panel, e);
36204 if(e.cancel === true){
36207 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36208 var panelId = panel.getId();
36209 this.panels.removeKey(panelId);
36211 document.body.appendChild(panel.getEl().dom);
36214 this.tabs.removeTab(panel.getEl().id);
36215 }else if (!preservePanel){
36216 this.bodyEl.dom.removeChild(panel.getEl().dom);
36218 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36219 var p = this.panels.first();
36220 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36221 tempEl.appendChild(p.getEl().dom);
36222 this.bodyEl.update("");
36223 this.bodyEl.dom.appendChild(p.getEl().dom);
36225 this.updateTitle(p.getTitle());
36227 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36228 this.setActivePanel(p);
36230 panel.setRegion(null);
36231 if(this.activePanel == panel){
36232 this.activePanel = null;
36234 if(this.config.autoDestroy !== false && preservePanel !== true){
36235 try{panel.destroy();}catch(e){}
36237 this.fireEvent("panelremoved", this, panel);
36242 * Returns the TabPanel component used by this region
36243 * @return {Roo.TabPanel}
36245 getTabs : function(){
36249 createTool : function(parentEl, className){
36250 var btn = Roo.DomHelper.append(parentEl, {
36252 cls: "x-layout-tools-button",
36255 cls: "roo-layout-tools-button-inner " + className,
36259 btn.addClassOnOver("roo-layout-tools-button-over");
36264 * Ext JS Library 1.1.1
36265 * Copyright(c) 2006-2007, Ext JS, LLC.
36267 * Originally Released Under LGPL - original licence link has changed is not relivant.
36270 * <script type="text/javascript">
36276 * @class Roo.SplitLayoutRegion
36277 * @extends Roo.LayoutRegion
36278 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36280 Roo.bootstrap.layout.Split = function(config){
36281 this.cursor = config.cursor;
36282 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36285 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36287 splitTip : "Drag to resize.",
36288 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36289 useSplitTips : false,
36291 applyConfig : function(config){
36292 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36295 onRender : function(ctr,pos) {
36297 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36298 if(!this.config.split){
36303 var splitEl = Roo.DomHelper.append(ctr.dom, {
36305 id: this.el.id + "-split",
36306 cls: "roo-layout-split roo-layout-split-"+this.position,
36309 /** The SplitBar for this region
36310 * @type Roo.SplitBar */
36311 // does not exist yet...
36312 Roo.log([this.position, this.orientation]);
36314 this.split = new Roo.bootstrap.SplitBar({
36315 dragElement : splitEl,
36316 resizingElement: this.el,
36317 orientation : this.orientation
36320 this.split.on("moved", this.onSplitMove, this);
36321 this.split.useShim = this.config.useShim === true;
36322 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36323 if(this.useSplitTips){
36324 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36326 //if(config.collapsible){
36327 // this.split.el.on("dblclick", this.collapse, this);
36330 if(typeof this.config.minSize != "undefined"){
36331 this.split.minSize = this.config.minSize;
36333 if(typeof this.config.maxSize != "undefined"){
36334 this.split.maxSize = this.config.maxSize;
36336 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36337 this.hideSplitter();
36342 getHMaxSize : function(){
36343 var cmax = this.config.maxSize || 10000;
36344 var center = this.mgr.getRegion("center");
36345 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36348 getVMaxSize : function(){
36349 var cmax = this.config.maxSize || 10000;
36350 var center = this.mgr.getRegion("center");
36351 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36354 onSplitMove : function(split, newSize){
36355 this.fireEvent("resized", this, newSize);
36359 * Returns the {@link Roo.SplitBar} for this region.
36360 * @return {Roo.SplitBar}
36362 getSplitBar : function(){
36367 this.hideSplitter();
36368 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36371 hideSplitter : function(){
36373 this.split.el.setLocation(-2000,-2000);
36374 this.split.el.hide();
36380 this.split.el.show();
36382 Roo.bootstrap.layout.Split.superclass.show.call(this);
36385 beforeSlide: function(){
36386 if(Roo.isGecko){// firefox overflow auto bug workaround
36387 this.bodyEl.clip();
36389 this.tabs.bodyEl.clip();
36391 if(this.activePanel){
36392 this.activePanel.getEl().clip();
36394 if(this.activePanel.beforeSlide){
36395 this.activePanel.beforeSlide();
36401 afterSlide : function(){
36402 if(Roo.isGecko){// firefox overflow auto bug workaround
36403 this.bodyEl.unclip();
36405 this.tabs.bodyEl.unclip();
36407 if(this.activePanel){
36408 this.activePanel.getEl().unclip();
36409 if(this.activePanel.afterSlide){
36410 this.activePanel.afterSlide();
36416 initAutoHide : function(){
36417 if(this.autoHide !== false){
36418 if(!this.autoHideHd){
36419 var st = new Roo.util.DelayedTask(this.slideIn, this);
36420 this.autoHideHd = {
36421 "mouseout": function(e){
36422 if(!e.within(this.el, true)){
36426 "mouseover" : function(e){
36432 this.el.on(this.autoHideHd);
36436 clearAutoHide : function(){
36437 if(this.autoHide !== false){
36438 this.el.un("mouseout", this.autoHideHd.mouseout);
36439 this.el.un("mouseover", this.autoHideHd.mouseover);
36443 clearMonitor : function(){
36444 Roo.get(document).un("click", this.slideInIf, this);
36447 // these names are backwards but not changed for compat
36448 slideOut : function(){
36449 if(this.isSlid || this.el.hasActiveFx()){
36452 this.isSlid = true;
36453 if(this.collapseBtn){
36454 this.collapseBtn.hide();
36456 this.closeBtnState = this.closeBtn.getStyle('display');
36457 this.closeBtn.hide();
36459 this.stickBtn.show();
36462 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36463 this.beforeSlide();
36464 this.el.setStyle("z-index", 10001);
36465 this.el.slideIn(this.getSlideAnchor(), {
36466 callback: function(){
36468 this.initAutoHide();
36469 Roo.get(document).on("click", this.slideInIf, this);
36470 this.fireEvent("slideshow", this);
36477 afterSlideIn : function(){
36478 this.clearAutoHide();
36479 this.isSlid = false;
36480 this.clearMonitor();
36481 this.el.setStyle("z-index", "");
36482 if(this.collapseBtn){
36483 this.collapseBtn.show();
36485 this.closeBtn.setStyle('display', this.closeBtnState);
36487 this.stickBtn.hide();
36489 this.fireEvent("slidehide", this);
36492 slideIn : function(cb){
36493 if(!this.isSlid || this.el.hasActiveFx()){
36497 this.isSlid = false;
36498 this.beforeSlide();
36499 this.el.slideOut(this.getSlideAnchor(), {
36500 callback: function(){
36501 this.el.setLeftTop(-10000, -10000);
36503 this.afterSlideIn();
36511 slideInIf : function(e){
36512 if(!e.within(this.el)){
36517 animateCollapse : function(){
36518 this.beforeSlide();
36519 this.el.setStyle("z-index", 20000);
36520 var anchor = this.getSlideAnchor();
36521 this.el.slideOut(anchor, {
36522 callback : function(){
36523 this.el.setStyle("z-index", "");
36524 this.collapsedEl.slideIn(anchor, {duration:.3});
36526 this.el.setLocation(-10000,-10000);
36528 this.fireEvent("collapsed", this);
36535 animateExpand : function(){
36536 this.beforeSlide();
36537 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36538 this.el.setStyle("z-index", 20000);
36539 this.collapsedEl.hide({
36542 this.el.slideIn(this.getSlideAnchor(), {
36543 callback : function(){
36544 this.el.setStyle("z-index", "");
36547 this.split.el.show();
36549 this.fireEvent("invalidated", this);
36550 this.fireEvent("expanded", this);
36578 getAnchor : function(){
36579 return this.anchors[this.position];
36582 getCollapseAnchor : function(){
36583 return this.canchors[this.position];
36586 getSlideAnchor : function(){
36587 return this.sanchors[this.position];
36590 getAlignAdj : function(){
36591 var cm = this.cmargins;
36592 switch(this.position){
36608 getExpandAdj : function(){
36609 var c = this.collapsedEl, cm = this.cmargins;
36610 switch(this.position){
36612 return [-(cm.right+c.getWidth()+cm.left), 0];
36615 return [cm.right+c.getWidth()+cm.left, 0];
36618 return [0, -(cm.top+cm.bottom+c.getHeight())];
36621 return [0, cm.top+cm.bottom+c.getHeight()];
36627 * Ext JS Library 1.1.1
36628 * Copyright(c) 2006-2007, Ext JS, LLC.
36630 * Originally Released Under LGPL - original licence link has changed is not relivant.
36633 * <script type="text/javascript">
36636 * These classes are private internal classes
36638 Roo.bootstrap.layout.Center = function(config){
36639 config.region = "center";
36640 Roo.bootstrap.layout.Region.call(this, config);
36641 this.visible = true;
36642 this.minWidth = config.minWidth || 20;
36643 this.minHeight = config.minHeight || 20;
36646 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36648 // center panel can't be hidden
36652 // center panel can't be hidden
36655 getMinWidth: function(){
36656 return this.minWidth;
36659 getMinHeight: function(){
36660 return this.minHeight;
36673 Roo.bootstrap.layout.North = function(config)
36675 config.region = 'north';
36676 config.cursor = 'n-resize';
36678 Roo.bootstrap.layout.Split.call(this, config);
36682 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36683 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36684 this.split.el.addClass("roo-layout-split-v");
36686 var size = config.initialSize || config.height;
36687 if(typeof size != "undefined"){
36688 this.el.setHeight(size);
36691 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36693 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36697 getBox : function(){
36698 if(this.collapsed){
36699 return this.collapsedEl.getBox();
36701 var box = this.el.getBox();
36703 box.height += this.split.el.getHeight();
36708 updateBox : function(box){
36709 if(this.split && !this.collapsed){
36710 box.height -= this.split.el.getHeight();
36711 this.split.el.setLeft(box.x);
36712 this.split.el.setTop(box.y+box.height);
36713 this.split.el.setWidth(box.width);
36715 if(this.collapsed){
36716 this.updateBody(box.width, null);
36718 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36726 Roo.bootstrap.layout.South = function(config){
36727 config.region = 'south';
36728 config.cursor = 's-resize';
36729 Roo.bootstrap.layout.Split.call(this, config);
36731 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36732 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36733 this.split.el.addClass("roo-layout-split-v");
36735 var size = config.initialSize || config.height;
36736 if(typeof size != "undefined"){
36737 this.el.setHeight(size);
36741 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36742 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36743 getBox : function(){
36744 if(this.collapsed){
36745 return this.collapsedEl.getBox();
36747 var box = this.el.getBox();
36749 var sh = this.split.el.getHeight();
36756 updateBox : function(box){
36757 if(this.split && !this.collapsed){
36758 var sh = this.split.el.getHeight();
36761 this.split.el.setLeft(box.x);
36762 this.split.el.setTop(box.y-sh);
36763 this.split.el.setWidth(box.width);
36765 if(this.collapsed){
36766 this.updateBody(box.width, null);
36768 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36772 Roo.bootstrap.layout.East = function(config){
36773 config.region = "east";
36774 config.cursor = "e-resize";
36775 Roo.bootstrap.layout.Split.call(this, config);
36777 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36778 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36779 this.split.el.addClass("roo-layout-split-h");
36781 var size = config.initialSize || config.width;
36782 if(typeof size != "undefined"){
36783 this.el.setWidth(size);
36786 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36787 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36788 getBox : function(){
36789 if(this.collapsed){
36790 return this.collapsedEl.getBox();
36792 var box = this.el.getBox();
36794 var sw = this.split.el.getWidth();
36801 updateBox : function(box){
36802 if(this.split && !this.collapsed){
36803 var sw = this.split.el.getWidth();
36805 this.split.el.setLeft(box.x);
36806 this.split.el.setTop(box.y);
36807 this.split.el.setHeight(box.height);
36810 if(this.collapsed){
36811 this.updateBody(null, box.height);
36813 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36817 Roo.bootstrap.layout.West = function(config){
36818 config.region = "west";
36819 config.cursor = "w-resize";
36821 Roo.bootstrap.layout.Split.call(this, config);
36823 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36824 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36825 this.split.el.addClass("roo-layout-split-h");
36829 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36830 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36832 onRender: function(ctr, pos)
36834 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36835 var size = this.config.initialSize || this.config.width;
36836 if(typeof size != "undefined"){
36837 this.el.setWidth(size);
36841 getBox : function(){
36842 if(this.collapsed){
36843 return this.collapsedEl.getBox();
36845 var box = this.el.getBox();
36847 box.width += 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+box.width);
36857 this.split.el.setTop(box.y);
36858 this.split.el.setHeight(box.height);
36860 if(this.collapsed){
36861 this.updateBody(null, box.height);
36863 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36866 Roo.namespace("Roo.bootstrap.panel");/*
36868 * Ext JS Library 1.1.1
36869 * Copyright(c) 2006-2007, Ext JS, LLC.
36871 * Originally Released Under LGPL - original licence link has changed is not relivant.
36874 * <script type="text/javascript">
36877 * @class Roo.ContentPanel
36878 * @extends Roo.util.Observable
36879 * A basic ContentPanel element.
36880 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36881 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36882 * @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
36883 * @cfg {Boolean} closable True if the panel can be closed/removed
36884 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36885 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36886 * @cfg {Toolbar} toolbar A toolbar for this panel
36887 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36888 * @cfg {String} title The title for this panel
36889 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36890 * @cfg {String} url Calls {@link #setUrl} with this value
36891 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36892 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36893 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36894 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36895 * @cfg {Boolean} badges render the badges
36898 * Create a new ContentPanel.
36899 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36900 * @param {String/Object} config A string to set only the title or a config object
36901 * @param {String} content (optional) Set the HTML content for this panel
36902 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36904 Roo.bootstrap.panel.Content = function( config){
36906 this.tpl = config.tpl || false;
36908 var el = config.el;
36909 var content = config.content;
36911 if(config.autoCreate){ // xtype is available if this is called from factory
36914 this.el = Roo.get(el);
36915 if(!this.el && config && config.autoCreate){
36916 if(typeof config.autoCreate == "object"){
36917 if(!config.autoCreate.id){
36918 config.autoCreate.id = config.id||el;
36920 this.el = Roo.DomHelper.append(document.body,
36921 config.autoCreate, true);
36923 var elcfg = { tag: "div",
36924 cls: "roo-layout-inactive-content",
36928 elcfg.html = config.html;
36932 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36935 this.closable = false;
36936 this.loaded = false;
36937 this.active = false;
36940 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36942 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36944 this.wrapEl = this.el; //this.el.wrap();
36946 if (config.toolbar.items) {
36947 ti = config.toolbar.items ;
36948 delete config.toolbar.items ;
36952 this.toolbar.render(this.wrapEl, 'before');
36953 for(var i =0;i < ti.length;i++) {
36954 // Roo.log(['add child', items[i]]);
36955 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36957 this.toolbar.items = nitems;
36958 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36959 delete config.toolbar;
36963 // xtype created footer. - not sure if will work as we normally have to render first..
36964 if (this.footer && !this.footer.el && this.footer.xtype) {
36965 if (!this.wrapEl) {
36966 this.wrapEl = this.el.wrap();
36969 this.footer.container = this.wrapEl.createChild();
36971 this.footer = Roo.factory(this.footer, Roo);
36976 if(typeof config == "string"){
36977 this.title = config;
36979 Roo.apply(this, config);
36983 this.resizeEl = Roo.get(this.resizeEl, true);
36985 this.resizeEl = this.el;
36987 // handle view.xtype
36995 * Fires when this panel is activated.
36996 * @param {Roo.ContentPanel} this
37000 * @event deactivate
37001 * Fires when this panel is activated.
37002 * @param {Roo.ContentPanel} this
37004 "deactivate" : true,
37008 * Fires when this panel is resized if fitToFrame is true.
37009 * @param {Roo.ContentPanel} this
37010 * @param {Number} width The width after any component adjustments
37011 * @param {Number} height The height after any component adjustments
37017 * Fires when this tab is created
37018 * @param {Roo.ContentPanel} this
37029 if(this.autoScroll){
37030 this.resizeEl.setStyle("overflow", "auto");
37032 // fix randome scrolling
37033 //this.el.on('scroll', function() {
37034 // Roo.log('fix random scolling');
37035 // this.scrollTo('top',0);
37038 content = content || this.content;
37040 this.setContent(content);
37042 if(config && config.url){
37043 this.setUrl(this.url, this.params, this.loadOnce);
37048 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37050 if (this.view && typeof(this.view.xtype) != 'undefined') {
37051 this.view.el = this.el.appendChild(document.createElement("div"));
37052 this.view = Roo.factory(this.view);
37053 this.view.render && this.view.render(false, '');
37057 this.fireEvent('render', this);
37060 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37064 setRegion : function(region){
37065 this.region = region;
37066 this.setActiveClass(region && !this.background);
37070 setActiveClass: function(state)
37073 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37074 this.el.setStyle('position','relative');
37076 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37077 this.el.setStyle('position', 'absolute');
37082 * Returns the toolbar for this Panel if one was configured.
37083 * @return {Roo.Toolbar}
37085 getToolbar : function(){
37086 return this.toolbar;
37089 setActiveState : function(active)
37091 this.active = active;
37092 this.setActiveClass(active);
37094 if(this.fireEvent("deactivate", this) === false){
37099 this.fireEvent("activate", this);
37103 * Updates this panel's element
37104 * @param {String} content The new content
37105 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37107 setContent : function(content, loadScripts){
37108 this.el.update(content, loadScripts);
37111 ignoreResize : function(w, h){
37112 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37115 this.lastSize = {width: w, height: h};
37120 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37121 * @return {Roo.UpdateManager} The UpdateManager
37123 getUpdateManager : function(){
37124 return this.el.getUpdateManager();
37127 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37128 * @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:
37131 url: "your-url.php",
37132 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37133 callback: yourFunction,
37134 scope: yourObject, //(optional scope)
37137 text: "Loading...",
37142 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37143 * 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.
37144 * @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}
37145 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37146 * @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.
37147 * @return {Roo.ContentPanel} this
37150 var um = this.el.getUpdateManager();
37151 um.update.apply(um, arguments);
37157 * 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.
37158 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37159 * @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)
37160 * @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)
37161 * @return {Roo.UpdateManager} The UpdateManager
37163 setUrl : function(url, params, loadOnce){
37164 if(this.refreshDelegate){
37165 this.removeListener("activate", this.refreshDelegate);
37167 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37168 this.on("activate", this.refreshDelegate);
37169 return this.el.getUpdateManager();
37172 _handleRefresh : function(url, params, loadOnce){
37173 if(!loadOnce || !this.loaded){
37174 var updater = this.el.getUpdateManager();
37175 updater.update(url, params, this._setLoaded.createDelegate(this));
37179 _setLoaded : function(){
37180 this.loaded = true;
37184 * Returns this panel's id
37187 getId : function(){
37192 * Returns this panel's element - used by regiosn to add.
37193 * @return {Roo.Element}
37195 getEl : function(){
37196 return this.wrapEl || this.el;
37201 adjustForComponents : function(width, height)
37203 //Roo.log('adjustForComponents ');
37204 if(this.resizeEl != this.el){
37205 width -= this.el.getFrameWidth('lr');
37206 height -= this.el.getFrameWidth('tb');
37209 var te = this.toolbar.getEl();
37210 te.setWidth(width);
37211 height -= te.getHeight();
37214 var te = this.footer.getEl();
37215 te.setWidth(width);
37216 height -= te.getHeight();
37220 if(this.adjustments){
37221 width += this.adjustments[0];
37222 height += this.adjustments[1];
37224 return {"width": width, "height": height};
37227 setSize : function(width, height){
37228 if(this.fitToFrame && !this.ignoreResize(width, height)){
37229 if(this.fitContainer && this.resizeEl != this.el){
37230 this.el.setSize(width, height);
37232 var size = this.adjustForComponents(width, height);
37233 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37234 this.fireEvent('resize', this, size.width, size.height);
37239 * Returns this panel's title
37242 getTitle : function(){
37244 if (typeof(this.title) != 'object') {
37249 for (var k in this.title) {
37250 if (!this.title.hasOwnProperty(k)) {
37254 if (k.indexOf('-') >= 0) {
37255 var s = k.split('-');
37256 for (var i = 0; i<s.length; i++) {
37257 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37260 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37267 * Set this panel's title
37268 * @param {String} title
37270 setTitle : function(title){
37271 this.title = title;
37273 this.region.updatePanelTitle(this, title);
37278 * Returns true is this panel was configured to be closable
37279 * @return {Boolean}
37281 isClosable : function(){
37282 return this.closable;
37285 beforeSlide : function(){
37287 this.resizeEl.clip();
37290 afterSlide : function(){
37292 this.resizeEl.unclip();
37296 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37297 * Will fail silently if the {@link #setUrl} method has not been called.
37298 * This does not activate the panel, just updates its content.
37300 refresh : function(){
37301 if(this.refreshDelegate){
37302 this.loaded = false;
37303 this.refreshDelegate();
37308 * Destroys this panel
37310 destroy : function(){
37311 this.el.removeAllListeners();
37312 var tempEl = document.createElement("span");
37313 tempEl.appendChild(this.el.dom);
37314 tempEl.innerHTML = "";
37320 * form - if the content panel contains a form - this is a reference to it.
37321 * @type {Roo.form.Form}
37325 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37326 * This contains a reference to it.
37332 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37342 * @param {Object} cfg Xtype definition of item to add.
37346 getChildContainer: function () {
37347 return this.getEl();
37352 var ret = new Roo.factory(cfg);
37357 if (cfg.xtype.match(/^Form$/)) {
37360 //if (this.footer) {
37361 // el = this.footer.container.insertSibling(false, 'before');
37363 el = this.el.createChild();
37366 this.form = new Roo.form.Form(cfg);
37369 if ( this.form.allItems.length) {
37370 this.form.render(el.dom);
37374 // should only have one of theses..
37375 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37376 // views.. should not be just added - used named prop 'view''
37378 cfg.el = this.el.appendChild(document.createElement("div"));
37381 var ret = new Roo.factory(cfg);
37383 ret.render && ret.render(false, ''); // render blank..
37393 * @class Roo.bootstrap.panel.Grid
37394 * @extends Roo.bootstrap.panel.Content
37396 * Create a new GridPanel.
37397 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37398 * @param {Object} config A the config object
37404 Roo.bootstrap.panel.Grid = function(config)
37408 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37409 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37411 config.el = this.wrapper;
37412 //this.el = this.wrapper;
37414 if (config.container) {
37415 // ctor'ed from a Border/panel.grid
37418 this.wrapper.setStyle("overflow", "hidden");
37419 this.wrapper.addClass('roo-grid-container');
37424 if(config.toolbar){
37425 var tool_el = this.wrapper.createChild();
37426 this.toolbar = Roo.factory(config.toolbar);
37428 if (config.toolbar.items) {
37429 ti = config.toolbar.items ;
37430 delete config.toolbar.items ;
37434 this.toolbar.render(tool_el);
37435 for(var i =0;i < ti.length;i++) {
37436 // Roo.log(['add child', items[i]]);
37437 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37439 this.toolbar.items = nitems;
37441 delete config.toolbar;
37444 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37445 config.grid.scrollBody = true;;
37446 config.grid.monitorWindowResize = false; // turn off autosizing
37447 config.grid.autoHeight = false;
37448 config.grid.autoWidth = false;
37450 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37452 if (config.background) {
37453 // render grid on panel activation (if panel background)
37454 this.on('activate', function(gp) {
37455 if (!gp.grid.rendered) {
37456 gp.grid.render(this.wrapper);
37457 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37462 this.grid.render(this.wrapper);
37463 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37466 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37467 // ??? needed ??? config.el = this.wrapper;
37472 // xtype created footer. - not sure if will work as we normally have to render first..
37473 if (this.footer && !this.footer.el && this.footer.xtype) {
37475 var ctr = this.grid.getView().getFooterPanel(true);
37476 this.footer.dataSource = this.grid.dataSource;
37477 this.footer = Roo.factory(this.footer, Roo);
37478 this.footer.render(ctr);
37488 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37489 getId : function(){
37490 return this.grid.id;
37494 * Returns the grid for this panel
37495 * @return {Roo.bootstrap.Table}
37497 getGrid : function(){
37501 setSize : function(width, height){
37502 if(!this.ignoreResize(width, height)){
37503 var grid = this.grid;
37504 var size = this.adjustForComponents(width, height);
37505 var gridel = grid.getGridEl();
37506 gridel.setSize(size.width, size.height);
37508 var thd = grid.getGridEl().select('thead',true).first();
37509 var tbd = grid.getGridEl().select('tbody', true).first();
37511 tbd.setSize(width, height - thd.getHeight());
37520 beforeSlide : function(){
37521 this.grid.getView().scroller.clip();
37524 afterSlide : function(){
37525 this.grid.getView().scroller.unclip();
37528 destroy : function(){
37529 this.grid.destroy();
37531 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37536 * @class Roo.bootstrap.panel.Nest
37537 * @extends Roo.bootstrap.panel.Content
37539 * Create a new Panel, that can contain a layout.Border.
37542 * @param {Roo.BorderLayout} layout The layout for this panel
37543 * @param {String/Object} config A string to set only the title or a config object
37545 Roo.bootstrap.panel.Nest = function(config)
37547 // construct with only one argument..
37548 /* FIXME - implement nicer consturctors
37549 if (layout.layout) {
37551 layout = config.layout;
37552 delete config.layout;
37554 if (layout.xtype && !layout.getEl) {
37555 // then layout needs constructing..
37556 layout = Roo.factory(layout, Roo);
37560 config.el = config.layout.getEl();
37562 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37564 config.layout.monitorWindowResize = false; // turn off autosizing
37565 this.layout = config.layout;
37566 this.layout.getEl().addClass("roo-layout-nested-layout");
37573 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37575 setSize : function(width, height){
37576 if(!this.ignoreResize(width, height)){
37577 var size = this.adjustForComponents(width, height);
37578 var el = this.layout.getEl();
37579 if (size.height < 1) {
37580 el.setWidth(size.width);
37582 el.setSize(size.width, size.height);
37584 var touch = el.dom.offsetWidth;
37585 this.layout.layout();
37586 // ie requires a double layout on the first pass
37587 if(Roo.isIE && !this.initialized){
37588 this.initialized = true;
37589 this.layout.layout();
37594 // activate all subpanels if not currently active..
37596 setActiveState : function(active){
37597 this.active = active;
37598 this.setActiveClass(active);
37601 this.fireEvent("deactivate", this);
37605 this.fireEvent("activate", this);
37606 // not sure if this should happen before or after..
37607 if (!this.layout) {
37608 return; // should not happen..
37611 for (var r in this.layout.regions) {
37612 reg = this.layout.getRegion(r);
37613 if (reg.getActivePanel()) {
37614 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37615 reg.setActivePanel(reg.getActivePanel());
37618 if (!reg.panels.length) {
37621 reg.showPanel(reg.getPanel(0));
37630 * Returns the nested BorderLayout for this panel
37631 * @return {Roo.BorderLayout}
37633 getLayout : function(){
37634 return this.layout;
37638 * Adds a xtype elements to the layout of the nested panel
37642 xtype : 'ContentPanel',
37649 xtype : 'NestedLayoutPanel',
37655 items : [ ... list of content panels or nested layout panels.. ]
37659 * @param {Object} cfg Xtype definition of item to add.
37661 addxtype : function(cfg) {
37662 return this.layout.addxtype(cfg);
37667 * Ext JS Library 1.1.1
37668 * Copyright(c) 2006-2007, Ext JS, LLC.
37670 * Originally Released Under LGPL - original licence link has changed is not relivant.
37673 * <script type="text/javascript">
37676 * @class Roo.TabPanel
37677 * @extends Roo.util.Observable
37678 * A lightweight tab container.
37682 // basic tabs 1, built from existing content
37683 var tabs = new Roo.TabPanel("tabs1");
37684 tabs.addTab("script", "View Script");
37685 tabs.addTab("markup", "View Markup");
37686 tabs.activate("script");
37688 // more advanced tabs, built from javascript
37689 var jtabs = new Roo.TabPanel("jtabs");
37690 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37692 // set up the UpdateManager
37693 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37694 var updater = tab2.getUpdateManager();
37695 updater.setDefaultUrl("ajax1.htm");
37696 tab2.on('activate', updater.refresh, updater, true);
37698 // Use setUrl for Ajax loading
37699 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37700 tab3.setUrl("ajax2.htm", null, true);
37703 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37706 jtabs.activate("jtabs-1");
37709 * Create a new TabPanel.
37710 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37711 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37713 Roo.bootstrap.panel.Tabs = function(config){
37715 * The container element for this TabPanel.
37716 * @type Roo.Element
37718 this.el = Roo.get(config.el);
37721 if(typeof config == "boolean"){
37722 this.tabPosition = config ? "bottom" : "top";
37724 Roo.apply(this, config);
37728 if(this.tabPosition == "bottom"){
37729 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37730 this.el.addClass("roo-tabs-bottom");
37732 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37733 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37734 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37736 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37738 if(this.tabPosition != "bottom"){
37739 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37740 * @type Roo.Element
37742 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37743 this.el.addClass("roo-tabs-top");
37747 this.bodyEl.setStyle("position", "relative");
37749 this.active = null;
37750 this.activateDelegate = this.activate.createDelegate(this);
37755 * Fires when the active tab changes
37756 * @param {Roo.TabPanel} this
37757 * @param {Roo.TabPanelItem} activePanel The new active tab
37761 * @event beforetabchange
37762 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37763 * @param {Roo.TabPanel} this
37764 * @param {Object} e Set cancel to true on this object to cancel the tab change
37765 * @param {Roo.TabPanelItem} tab The tab being changed to
37767 "beforetabchange" : true
37770 Roo.EventManager.onWindowResize(this.onResize, this);
37771 this.cpad = this.el.getPadding("lr");
37772 this.hiddenCount = 0;
37775 // toolbar on the tabbar support...
37776 if (this.toolbar) {
37777 alert("no toolbar support yet");
37778 this.toolbar = false;
37780 var tcfg = this.toolbar;
37781 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37782 this.toolbar = new Roo.Toolbar(tcfg);
37783 if (Roo.isSafari) {
37784 var tbl = tcfg.container.child('table', true);
37785 tbl.setAttribute('width', '100%');
37793 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37796 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37798 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37800 tabPosition : "top",
37802 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37804 currentTabWidth : 0,
37806 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37810 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37814 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37816 preferredTabWidth : 175,
37818 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37820 resizeTabs : false,
37822 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37824 monitorResize : true,
37826 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37831 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37832 * @param {String} id The id of the div to use <b>or create</b>
37833 * @param {String} text The text for the tab
37834 * @param {String} content (optional) Content to put in the TabPanelItem body
37835 * @param {Boolean} closable (optional) True to create a close icon on the tab
37836 * @return {Roo.TabPanelItem} The created TabPanelItem
37838 addTab : function(id, text, content, closable, tpl)
37840 var item = new Roo.bootstrap.panel.TabItem({
37844 closable : closable,
37847 this.addTabItem(item);
37849 item.setContent(content);
37855 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37856 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37857 * @return {Roo.TabPanelItem}
37859 getTab : function(id){
37860 return this.items[id];
37864 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37865 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37867 hideTab : function(id){
37868 var t = this.items[id];
37871 this.hiddenCount++;
37872 this.autoSizeTabs();
37877 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37878 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37880 unhideTab : function(id){
37881 var t = this.items[id];
37883 t.setHidden(false);
37884 this.hiddenCount--;
37885 this.autoSizeTabs();
37890 * Adds an existing {@link Roo.TabPanelItem}.
37891 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37893 addTabItem : function(item){
37894 this.items[item.id] = item;
37895 this.items.push(item);
37896 // if(this.resizeTabs){
37897 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37898 // this.autoSizeTabs();
37900 // item.autoSize();
37905 * Removes a {@link Roo.TabPanelItem}.
37906 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37908 removeTab : function(id){
37909 var items = this.items;
37910 var tab = items[id];
37911 if(!tab) { return; }
37912 var index = items.indexOf(tab);
37913 if(this.active == tab && items.length > 1){
37914 var newTab = this.getNextAvailable(index);
37919 this.stripEl.dom.removeChild(tab.pnode.dom);
37920 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37921 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37923 items.splice(index, 1);
37924 delete this.items[tab.id];
37925 tab.fireEvent("close", tab);
37926 tab.purgeListeners();
37927 this.autoSizeTabs();
37930 getNextAvailable : function(start){
37931 var items = this.items;
37933 // look for a next tab that will slide over to
37934 // replace the one being removed
37935 while(index < items.length){
37936 var item = items[++index];
37937 if(item && !item.isHidden()){
37941 // if one isn't found select the previous tab (on the left)
37944 var item = items[--index];
37945 if(item && !item.isHidden()){
37953 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37954 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37956 disableTab : function(id){
37957 var tab = this.items[id];
37958 if(tab && this.active != tab){
37964 * Enables a {@link Roo.TabPanelItem} that is disabled.
37965 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37967 enableTab : function(id){
37968 var tab = this.items[id];
37973 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37974 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37975 * @return {Roo.TabPanelItem} The TabPanelItem.
37977 activate : function(id){
37978 var tab = this.items[id];
37982 if(tab == this.active || tab.disabled){
37986 this.fireEvent("beforetabchange", this, e, tab);
37987 if(e.cancel !== true && !tab.disabled){
37989 this.active.hide();
37991 this.active = this.items[id];
37992 this.active.show();
37993 this.fireEvent("tabchange", this, this.active);
37999 * Gets the active {@link Roo.TabPanelItem}.
38000 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38002 getActiveTab : function(){
38003 return this.active;
38007 * Updates the tab body element to fit the height of the container element
38008 * for overflow scrolling
38009 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38011 syncHeight : function(targetHeight){
38012 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38013 var bm = this.bodyEl.getMargins();
38014 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38015 this.bodyEl.setHeight(newHeight);
38019 onResize : function(){
38020 if(this.monitorResize){
38021 this.autoSizeTabs();
38026 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38028 beginUpdate : function(){
38029 this.updating = true;
38033 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38035 endUpdate : function(){
38036 this.updating = false;
38037 this.autoSizeTabs();
38041 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38043 autoSizeTabs : function(){
38044 var count = this.items.length;
38045 var vcount = count - this.hiddenCount;
38046 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38049 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38050 var availWidth = Math.floor(w / vcount);
38051 var b = this.stripBody;
38052 if(b.getWidth() > w){
38053 var tabs = this.items;
38054 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38055 if(availWidth < this.minTabWidth){
38056 /*if(!this.sleft){ // incomplete scrolling code
38057 this.createScrollButtons();
38060 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38063 if(this.currentTabWidth < this.preferredTabWidth){
38064 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38070 * Returns the number of tabs in this TabPanel.
38073 getCount : function(){
38074 return this.items.length;
38078 * Resizes all the tabs to the passed width
38079 * @param {Number} The new width
38081 setTabWidth : function(width){
38082 this.currentTabWidth = width;
38083 for(var i = 0, len = this.items.length; i < len; i++) {
38084 if(!this.items[i].isHidden()) {
38085 this.items[i].setWidth(width);
38091 * Destroys this TabPanel
38092 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38094 destroy : function(removeEl){
38095 Roo.EventManager.removeResizeListener(this.onResize, this);
38096 for(var i = 0, len = this.items.length; i < len; i++){
38097 this.items[i].purgeListeners();
38099 if(removeEl === true){
38100 this.el.update("");
38105 createStrip : function(container)
38107 var strip = document.createElement("nav");
38108 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38109 container.appendChild(strip);
38113 createStripList : function(strip)
38115 // div wrapper for retard IE
38116 // returns the "tr" element.
38117 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38118 //'<div class="x-tabs-strip-wrap">'+
38119 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38120 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38121 return strip.firstChild; //.firstChild.firstChild.firstChild;
38123 createBody : function(container)
38125 var body = document.createElement("div");
38126 Roo.id(body, "tab-body");
38127 //Roo.fly(body).addClass("x-tabs-body");
38128 Roo.fly(body).addClass("tab-content");
38129 container.appendChild(body);
38132 createItemBody :function(bodyEl, id){
38133 var body = Roo.getDom(id);
38135 body = document.createElement("div");
38138 //Roo.fly(body).addClass("x-tabs-item-body");
38139 Roo.fly(body).addClass("tab-pane");
38140 bodyEl.insertBefore(body, bodyEl.firstChild);
38144 createStripElements : function(stripEl, text, closable, tpl)
38146 var td = document.createElement("li"); // was td..
38149 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38152 stripEl.appendChild(td);
38154 td.className = "x-tabs-closable";
38155 if(!this.closeTpl){
38156 this.closeTpl = new Roo.Template(
38157 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38158 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38159 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38162 var el = this.closeTpl.overwrite(td, {"text": text});
38163 var close = el.getElementsByTagName("div")[0];
38164 var inner = el.getElementsByTagName("em")[0];
38165 return {"el": el, "close": close, "inner": inner};
38168 // not sure what this is..
38169 // if(!this.tabTpl){
38170 //this.tabTpl = new Roo.Template(
38171 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38172 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38174 // this.tabTpl = new Roo.Template(
38175 // '<a href="#">' +
38176 // '<span unselectable="on"' +
38177 // (this.disableTooltips ? '' : ' title="{text}"') +
38178 // ' >{text}</span></a>'
38184 var template = tpl || this.tabTpl || false;
38188 template = new Roo.Template(
38190 '<span unselectable="on"' +
38191 (this.disableTooltips ? '' : ' title="{text}"') +
38192 ' >{text}</span></a>'
38196 switch (typeof(template)) {
38200 template = new Roo.Template(template);
38206 var el = template.overwrite(td, {"text": text});
38208 var inner = el.getElementsByTagName("span")[0];
38210 return {"el": el, "inner": inner};
38218 * @class Roo.TabPanelItem
38219 * @extends Roo.util.Observable
38220 * Represents an individual item (tab plus body) in a TabPanel.
38221 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38222 * @param {String} id The id of this TabPanelItem
38223 * @param {String} text The text for the tab of this TabPanelItem
38224 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38226 Roo.bootstrap.panel.TabItem = function(config){
38228 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38229 * @type Roo.TabPanel
38231 this.tabPanel = config.panel;
38233 * The id for this TabPanelItem
38236 this.id = config.id;
38238 this.disabled = false;
38240 this.text = config.text;
38242 this.loaded = false;
38243 this.closable = config.closable;
38246 * The body element for this TabPanelItem.
38247 * @type Roo.Element
38249 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38250 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38251 this.bodyEl.setStyle("display", "block");
38252 this.bodyEl.setStyle("zoom", "1");
38253 //this.hideAction();
38255 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38257 this.el = Roo.get(els.el);
38258 this.inner = Roo.get(els.inner, true);
38259 this.textEl = Roo.get(this.el.dom.firstChild, true);
38260 this.pnode = Roo.get(els.el.parentNode, true);
38261 // this.el.on("mousedown", this.onTabMouseDown, this);
38262 this.el.on("click", this.onTabClick, this);
38264 if(config.closable){
38265 var c = Roo.get(els.close, true);
38266 c.dom.title = this.closeText;
38267 c.addClassOnOver("close-over");
38268 c.on("click", this.closeClick, this);
38274 * Fires when this tab becomes the active tab.
38275 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38276 * @param {Roo.TabPanelItem} this
38280 * @event beforeclose
38281 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38282 * @param {Roo.TabPanelItem} this
38283 * @param {Object} e Set cancel to true on this object to cancel the close.
38285 "beforeclose": true,
38288 * Fires when this tab is closed.
38289 * @param {Roo.TabPanelItem} this
38293 * @event deactivate
38294 * Fires when this tab is no longer the active tab.
38295 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38296 * @param {Roo.TabPanelItem} this
38298 "deactivate" : true
38300 this.hidden = false;
38302 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38305 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38307 purgeListeners : function(){
38308 Roo.util.Observable.prototype.purgeListeners.call(this);
38309 this.el.removeAllListeners();
38312 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38315 this.pnode.addClass("active");
38318 this.tabPanel.stripWrap.repaint();
38320 this.fireEvent("activate", this.tabPanel, this);
38324 * Returns true if this tab is the active tab.
38325 * @return {Boolean}
38327 isActive : function(){
38328 return this.tabPanel.getActiveTab() == this;
38332 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38335 this.pnode.removeClass("active");
38337 this.fireEvent("deactivate", this.tabPanel, this);
38340 hideAction : function(){
38341 this.bodyEl.hide();
38342 this.bodyEl.setStyle("position", "absolute");
38343 this.bodyEl.setLeft("-20000px");
38344 this.bodyEl.setTop("-20000px");
38347 showAction : function(){
38348 this.bodyEl.setStyle("position", "relative");
38349 this.bodyEl.setTop("");
38350 this.bodyEl.setLeft("");
38351 this.bodyEl.show();
38355 * Set the tooltip for the tab.
38356 * @param {String} tooltip The tab's tooltip
38358 setTooltip : function(text){
38359 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38360 this.textEl.dom.qtip = text;
38361 this.textEl.dom.removeAttribute('title');
38363 this.textEl.dom.title = text;
38367 onTabClick : function(e){
38368 e.preventDefault();
38369 this.tabPanel.activate(this.id);
38372 onTabMouseDown : function(e){
38373 e.preventDefault();
38374 this.tabPanel.activate(this.id);
38377 getWidth : function(){
38378 return this.inner.getWidth();
38381 setWidth : function(width){
38382 var iwidth = width - this.pnode.getPadding("lr");
38383 this.inner.setWidth(iwidth);
38384 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38385 this.pnode.setWidth(width);
38389 * Show or hide the tab
38390 * @param {Boolean} hidden True to hide or false to show.
38392 setHidden : function(hidden){
38393 this.hidden = hidden;
38394 this.pnode.setStyle("display", hidden ? "none" : "");
38398 * Returns true if this tab is "hidden"
38399 * @return {Boolean}
38401 isHidden : function(){
38402 return this.hidden;
38406 * Returns the text for this tab
38409 getText : function(){
38413 autoSize : function(){
38414 //this.el.beginMeasure();
38415 this.textEl.setWidth(1);
38417 * #2804 [new] Tabs in Roojs
38418 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38420 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38421 //this.el.endMeasure();
38425 * Sets the text for the tab (Note: this also sets the tooltip text)
38426 * @param {String} text The tab's text and tooltip
38428 setText : function(text){
38430 this.textEl.update(text);
38431 this.setTooltip(text);
38432 //if(!this.tabPanel.resizeTabs){
38433 // this.autoSize();
38437 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38439 activate : function(){
38440 this.tabPanel.activate(this.id);
38444 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38446 disable : function(){
38447 if(this.tabPanel.active != this){
38448 this.disabled = true;
38449 this.pnode.addClass("disabled");
38454 * Enables this TabPanelItem if it was previously disabled.
38456 enable : function(){
38457 this.disabled = false;
38458 this.pnode.removeClass("disabled");
38462 * Sets the content for this TabPanelItem.
38463 * @param {String} content The content
38464 * @param {Boolean} loadScripts true to look for and load scripts
38466 setContent : function(content, loadScripts){
38467 this.bodyEl.update(content, loadScripts);
38471 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38472 * @return {Roo.UpdateManager} The UpdateManager
38474 getUpdateManager : function(){
38475 return this.bodyEl.getUpdateManager();
38479 * Set a URL to be used to load the content for this TabPanelItem.
38480 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38481 * @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)
38482 * @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)
38483 * @return {Roo.UpdateManager} The UpdateManager
38485 setUrl : function(url, params, loadOnce){
38486 if(this.refreshDelegate){
38487 this.un('activate', this.refreshDelegate);
38489 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38490 this.on("activate", this.refreshDelegate);
38491 return this.bodyEl.getUpdateManager();
38495 _handleRefresh : function(url, params, loadOnce){
38496 if(!loadOnce || !this.loaded){
38497 var updater = this.bodyEl.getUpdateManager();
38498 updater.update(url, params, this._setLoaded.createDelegate(this));
38503 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38504 * Will fail silently if the setUrl method has not been called.
38505 * This does not activate the panel, just updates its content.
38507 refresh : function(){
38508 if(this.refreshDelegate){
38509 this.loaded = false;
38510 this.refreshDelegate();
38515 _setLoaded : function(){
38516 this.loaded = true;
38520 closeClick : function(e){
38523 this.fireEvent("beforeclose", this, o);
38524 if(o.cancel !== true){
38525 this.tabPanel.removeTab(this.id);
38529 * The text displayed in the tooltip for the close icon.
38532 closeText : "Close this tab"
38535 * This script refer to:
38536 * Title: International Telephone Input
38537 * Author: Jack O'Connor
38538 * Code version: v12.1.12
38539 * Availability: https://github.com/jackocnr/intl-tel-input.git
38542 Roo.bootstrap.PhoneInputData = function() {
38545 "Afghanistan (افغانستان)",
38550 "Albania (Shqipëri)",
38555 "Algeria (الجزائر)",
38580 "Antigua and Barbuda",
38590 "Armenia (Հայաստան)",
38606 "Austria (Österreich)",
38611 "Azerbaijan (Azərbaycan)",
38621 "Bahrain (البحرين)",
38626 "Bangladesh (বাংলাদেশ)",
38636 "Belarus (Беларусь)",
38641 "Belgium (België)",
38671 "Bosnia and Herzegovina (Босна и Херцеговина)",
38686 "British Indian Ocean Territory",
38691 "British Virgin Islands",
38701 "Bulgaria (България)",
38711 "Burundi (Uburundi)",
38716 "Cambodia (កម្ពុជា)",
38721 "Cameroon (Cameroun)",
38730 ["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"]
38733 "Cape Verde (Kabu Verdi)",
38738 "Caribbean Netherlands",
38749 "Central African Republic (République centrafricaine)",
38769 "Christmas Island",
38775 "Cocos (Keeling) Islands",
38786 "Comoros (جزر القمر)",
38791 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38796 "Congo (Republic) (Congo-Brazzaville)",
38816 "Croatia (Hrvatska)",
38837 "Czech Republic (Česká republika)",
38842 "Denmark (Danmark)",
38857 "Dominican Republic (República Dominicana)",
38861 ["809", "829", "849"]
38879 "Equatorial Guinea (Guinea Ecuatorial)",
38899 "Falkland Islands (Islas Malvinas)",
38904 "Faroe Islands (Føroyar)",
38925 "French Guiana (Guyane française)",
38930 "French Polynesia (Polynésie française)",
38945 "Georgia (საქართველო)",
38950 "Germany (Deutschland)",
38970 "Greenland (Kalaallit Nunaat)",
39007 "Guinea-Bissau (Guiné Bissau)",
39032 "Hungary (Magyarország)",
39037 "Iceland (Ísland)",
39057 "Iraq (العراق)",
39073 "Israel (ישראל)",
39100 "Jordan (الأردن)",
39105 "Kazakhstan (Казахстан)",
39126 "Kuwait (الكويت)",
39131 "Kyrgyzstan (Кыргызстан)",
39141 "Latvia (Latvija)",
39146 "Lebanon (لبنان)",
39161 "Libya (ليبيا)",
39171 "Lithuania (Lietuva)",
39186 "Macedonia (FYROM) (Македонија)",
39191 "Madagascar (Madagasikara)",
39221 "Marshall Islands",
39231 "Mauritania (موريتانيا)",
39236 "Mauritius (Moris)",
39257 "Moldova (Republica Moldova)",
39267 "Mongolia (Монгол)",
39272 "Montenegro (Crna Gora)",
39282 "Morocco (المغرب)",
39288 "Mozambique (Moçambique)",
39293 "Myanmar (Burma) (မြန်မာ)",
39298 "Namibia (Namibië)",
39313 "Netherlands (Nederland)",
39318 "New Caledonia (Nouvelle-Calédonie)",
39353 "North Korea (조선 민주주의 인민 공화국)",
39358 "Northern Mariana Islands",
39374 "Pakistan (پاکستان)",
39384 "Palestine (فلسطين)",
39394 "Papua New Guinea",
39436 "Réunion (La Réunion)",
39442 "Romania (România)",
39458 "Saint Barthélemy",
39469 "Saint Kitts and Nevis",
39479 "Saint Martin (Saint-Martin (partie française))",
39485 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39490 "Saint Vincent and the Grenadines",
39505 "São Tomé and Príncipe (São Tomé e Príncipe)",
39510 "Saudi Arabia (المملكة العربية السعودية)",
39515 "Senegal (Sénégal)",
39545 "Slovakia (Slovensko)",
39550 "Slovenia (Slovenija)",
39560 "Somalia (Soomaaliya)",
39570 "South Korea (대한민국)",
39575 "South Sudan (جنوب السودان)",
39585 "Sri Lanka (ශ්රී ලංකාව)",
39590 "Sudan (السودان)",
39600 "Svalbard and Jan Mayen",
39611 "Sweden (Sverige)",
39616 "Switzerland (Schweiz)",
39621 "Syria (سوريا)",
39666 "Trinidad and Tobago",
39671 "Tunisia (تونس)",
39676 "Turkey (Türkiye)",
39686 "Turks and Caicos Islands",
39696 "U.S. Virgin Islands",
39706 "Ukraine (Україна)",
39711 "United Arab Emirates (الإمارات العربية المتحدة)",
39733 "Uzbekistan (Oʻzbekiston)",
39743 "Vatican City (Città del Vaticano)",
39754 "Vietnam (Việt Nam)",
39759 "Wallis and Futuna (Wallis-et-Futuna)",
39764 "Western Sahara (الصحراء الغربية)",
39770 "Yemen (اليمن)",
39794 * This script refer to:
39795 * Title: International Telephone Input
39796 * Author: Jack O'Connor
39797 * Code version: v12.1.12
39798 * Availability: https://github.com/jackocnr/intl-tel-input.git
39802 * @class Roo.bootstrap.PhoneInput
39803 * @extends Roo.bootstrap.TriggerField
39804 * An input with International dial-code selection
39806 * @cfg {String} defaultDialCode default '+852'
39807 * @cfg {Array} preferedCountries default []
39810 * Create a new PhoneInput.
39811 * @param {Object} config Configuration options
39814 Roo.bootstrap.PhoneInput = function(config) {
39815 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39818 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39820 listWidth: undefined,
39822 selectedClass: 'active',
39824 invalidClass : "has-warning",
39826 validClass: 'has-success',
39828 allowed: '0123456789',
39831 * @cfg {String} defaultDialCode The default dial code when initializing the input
39833 defaultDialCode: '+852',
39836 * @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
39838 preferedCountries: false,
39840 getAutoCreate : function()
39842 var data = Roo.bootstrap.PhoneInputData();
39843 var align = this.labelAlign || this.parentLabelAlign();
39846 this.allCountries = [];
39847 this.dialCodeMapping = [];
39849 for (var i = 0; i < data.length; i++) {
39851 this.allCountries[i] = {
39855 priority: c[3] || 0,
39856 areaCodes: c[4] || null
39858 this.dialCodeMapping[c[2]] = {
39861 priority: c[3] || 0,
39862 areaCodes: c[4] || null
39874 cls : 'form-control tel-input',
39875 autocomplete: 'new-password'
39878 var hiddenInput = {
39881 cls: 'hidden-tel-input'
39885 hiddenInput.name = this.name;
39888 if (this.disabled) {
39889 input.disabled = true;
39892 var flag_container = {
39909 cls: this.hasFeedback ? 'has-feedback' : '',
39915 cls: 'dial-code-holder',
39922 cls: 'roo-select2-container input-group',
39929 if (this.fieldLabel.length) {
39932 tooltip: 'This field is required'
39938 cls: 'control-label',
39944 html: this.fieldLabel
39947 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39953 if(this.indicatorpos == 'right') {
39954 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39961 if(align == 'left') {
39969 if(this.labelWidth > 12){
39970 label.style = "width: " + this.labelWidth + 'px';
39972 if(this.labelWidth < 13 && this.labelmd == 0){
39973 this.labelmd = this.labelWidth;
39975 if(this.labellg > 0){
39976 label.cls += ' col-lg-' + this.labellg;
39977 input.cls += ' col-lg-' + (12 - this.labellg);
39979 if(this.labelmd > 0){
39980 label.cls += ' col-md-' + this.labelmd;
39981 container.cls += ' col-md-' + (12 - this.labelmd);
39983 if(this.labelsm > 0){
39984 label.cls += ' col-sm-' + this.labelsm;
39985 container.cls += ' col-sm-' + (12 - this.labelsm);
39987 if(this.labelxs > 0){
39988 label.cls += ' col-xs-' + this.labelxs;
39989 container.cls += ' col-xs-' + (12 - this.labelxs);
39999 var settings = this;
40001 ['xs','sm','md','lg'].map(function(size){
40002 if (settings[size]) {
40003 cfg.cls += ' col-' + size + '-' + settings[size];
40007 this.store = new Roo.data.Store({
40008 proxy : new Roo.data.MemoryProxy({}),
40009 reader : new Roo.data.JsonReader({
40020 'name' : 'dialCode',
40024 'name' : 'priority',
40028 'name' : 'areaCodes',
40035 if(!this.preferedCountries) {
40036 this.preferedCountries = [
40043 var p = this.preferedCountries.reverse();
40046 for (var i = 0; i < p.length; i++) {
40047 for (var j = 0; j < this.allCountries.length; j++) {
40048 if(this.allCountries[j].iso2 == p[i]) {
40049 var t = this.allCountries[j];
40050 this.allCountries.splice(j,1);
40051 this.allCountries.unshift(t);
40057 this.store.proxy.data = {
40059 data: this.allCountries
40065 initEvents : function()
40068 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40070 this.indicator = this.indicatorEl();
40071 this.flag = this.flagEl();
40072 this.dialCodeHolder = this.dialCodeHolderEl();
40074 this.trigger = this.el.select('div.flag-box',true).first();
40075 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40080 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40081 _this.list.setWidth(lw);
40084 this.list.on('mouseover', this.onViewOver, this);
40085 this.list.on('mousemove', this.onViewMove, this);
40086 this.inputEl().on("keyup", this.onKeyUp, this);
40088 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40090 this.view = new Roo.View(this.list, this.tpl, {
40091 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40094 this.view.on('click', this.onViewClick, this);
40095 this.setValue(this.defaultDialCode);
40098 onTriggerClick : function(e)
40100 Roo.log('trigger click');
40105 if(this.isExpanded()){
40107 this.hasFocus = false;
40109 this.store.load({});
40110 this.hasFocus = true;
40115 isExpanded : function()
40117 return this.list.isVisible();
40120 collapse : function()
40122 if(!this.isExpanded()){
40126 Roo.get(document).un('mousedown', this.collapseIf, this);
40127 Roo.get(document).un('mousewheel', this.collapseIf, this);
40128 this.fireEvent('collapse', this);
40132 expand : function()
40136 if(this.isExpanded() || !this.hasFocus){
40140 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40141 this.list.setWidth(lw);
40144 this.restrictHeight();
40146 Roo.get(document).on('mousedown', this.collapseIf, this);
40147 Roo.get(document).on('mousewheel', this.collapseIf, this);
40149 this.fireEvent('expand', this);
40152 restrictHeight : function()
40154 this.list.alignTo(this.inputEl(), this.listAlign);
40155 this.list.alignTo(this.inputEl(), this.listAlign);
40158 onViewOver : function(e, t)
40160 if(this.inKeyMode){
40163 var item = this.view.findItemFromChild(t);
40166 var index = this.view.indexOf(item);
40167 this.select(index, false);
40172 onViewClick : function(view, doFocus, el, e)
40174 var index = this.view.getSelectedIndexes()[0];
40176 var r = this.store.getAt(index);
40179 this.onSelect(r, index);
40181 if(doFocus !== false && !this.blockFocus){
40182 this.inputEl().focus();
40186 onViewMove : function(e, t)
40188 this.inKeyMode = false;
40191 select : function(index, scrollIntoView)
40193 this.selectedIndex = index;
40194 this.view.select(index);
40195 if(scrollIntoView !== false){
40196 var el = this.view.getNode(index);
40198 this.list.scrollChildIntoView(el, false);
40203 createList : function()
40205 this.list = Roo.get(document.body).createChild({
40207 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40208 style: 'display:none'
40211 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40214 collapseIf : function(e)
40216 var in_combo = e.within(this.el);
40217 var in_list = e.within(this.list);
40218 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40220 if (in_combo || in_list || is_list) {
40226 onSelect : function(record, index)
40228 if(this.fireEvent('beforeselect', this, record, index) !== false){
40230 this.setFlagClass(record.data.iso2);
40231 this.setDialCode(record.data.dialCode);
40232 this.hasFocus = false;
40234 this.fireEvent('select', this, record, index);
40238 flagEl : function()
40240 var flag = this.el.select('div.flag',true).first();
40247 dialCodeHolderEl : function()
40249 var d = this.el.select('input.dial-code-holder',true).first();
40256 setDialCode : function(v)
40258 this.dialCodeHolder.dom.value = '+'+v;
40261 setFlagClass : function(n)
40263 this.flag.dom.className = 'flag '+n;
40266 getValue : function()
40268 var v = this.inputEl().getValue();
40269 if(this.dialCodeHolder) {
40270 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40275 setValue : function(v)
40277 var d = this.getDialCode(v);
40279 //invalid dial code
40280 if(v.length == 0 || !d || d.length == 0) {
40282 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40283 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40289 this.setFlagClass(this.dialCodeMapping[d].iso2);
40290 this.setDialCode(d);
40291 this.inputEl().dom.value = v.replace('+'+d,'');
40292 this.hiddenEl().dom.value = this.getValue();
40297 getDialCode : function(v)
40301 if (v.length == 0) {
40302 return this.dialCodeHolder.dom.value;
40306 if (v.charAt(0) != "+") {
40309 var numericChars = "";
40310 for (var i = 1; i < v.length; i++) {
40311 var c = v.charAt(i);
40314 if (this.dialCodeMapping[numericChars]) {
40315 dialCode = v.substr(1, i);
40317 if (numericChars.length == 4) {
40327 this.setValue(this.defaultDialCode);
40331 hiddenEl : function()
40333 return this.el.select('input.hidden-tel-input',true).first();
40336 onKeyUp : function(e){
40338 var k = e.getKey();
40339 var c = e.getCharCode();
40342 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40343 this.allowed.indexOf(String.fromCharCode(c)) === -1
40348 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40351 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40355 this.setValue(this.getValue());
40360 * @class Roo.bootstrap.MoneyField
40361 * @extends Roo.bootstrap.ComboBox
40362 * Bootstrap MoneyField class
40365 * Create a new MoneyField.
40366 * @param {Object} config Configuration options
40369 Roo.bootstrap.MoneyField = function(config) {
40371 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40375 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40378 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40380 allowDecimals : true,
40382 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40384 decimalSeparator : ".",
40386 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40388 decimalPrecision : 0,
40390 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40392 allowNegative : true,
40394 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40398 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40400 minValue : Number.NEGATIVE_INFINITY,
40402 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40404 maxValue : Number.MAX_VALUE,
40406 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40408 minText : "The minimum value for this field is {0}",
40410 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40412 maxText : "The maximum value for this field is {0}",
40414 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40415 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40417 nanText : "{0} is not a valid number",
40419 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40423 * @cfg {String} defaults currency of the MoneyField
40424 * value should be in lkey
40426 defaultCurrency : false,
40428 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40430 thousandsDelimiter : false,
40440 getAutoCreate : function()
40442 var align = this.labelAlign || this.parentLabelAlign();
40454 cls : 'form-control roo-money-amount-input',
40455 autocomplete: 'new-password'
40458 var hiddenInput = {
40462 cls: 'hidden-number-input'
40466 hiddenInput.name = this.name;
40469 if (this.disabled) {
40470 input.disabled = true;
40473 var clg = 12 - this.inputlg;
40474 var cmd = 12 - this.inputmd;
40475 var csm = 12 - this.inputsm;
40476 var cxs = 12 - this.inputxs;
40480 cls : 'row roo-money-field',
40484 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40488 cls: 'roo-select2-container input-group',
40492 cls : 'form-control roo-money-currency-input',
40493 autocomplete: 'new-password',
40495 name : this.currencyName
40499 cls : 'input-group-addon',
40513 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40517 cls: this.hasFeedback ? 'has-feedback' : '',
40528 if (this.fieldLabel.length) {
40531 tooltip: 'This field is required'
40537 cls: 'control-label',
40543 html: this.fieldLabel
40546 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40552 if(this.indicatorpos == 'right') {
40553 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40560 if(align == 'left') {
40568 if(this.labelWidth > 12){
40569 label.style = "width: " + this.labelWidth + 'px';
40571 if(this.labelWidth < 13 && this.labelmd == 0){
40572 this.labelmd = this.labelWidth;
40574 if(this.labellg > 0){
40575 label.cls += ' col-lg-' + this.labellg;
40576 input.cls += ' col-lg-' + (12 - this.labellg);
40578 if(this.labelmd > 0){
40579 label.cls += ' col-md-' + this.labelmd;
40580 container.cls += ' col-md-' + (12 - this.labelmd);
40582 if(this.labelsm > 0){
40583 label.cls += ' col-sm-' + this.labelsm;
40584 container.cls += ' col-sm-' + (12 - this.labelsm);
40586 if(this.labelxs > 0){
40587 label.cls += ' col-xs-' + this.labelxs;
40588 container.cls += ' col-xs-' + (12 - this.labelxs);
40599 var settings = this;
40601 ['xs','sm','md','lg'].map(function(size){
40602 if (settings[size]) {
40603 cfg.cls += ' col-' + size + '-' + settings[size];
40610 initEvents : function()
40612 this.indicator = this.indicatorEl();
40614 this.initCurrencyEvent();
40616 this.initNumberEvent();
40619 initCurrencyEvent : function()
40622 throw "can not find store for combo";
40625 this.store = Roo.factory(this.store, Roo.data);
40626 this.store.parent = this;
40630 this.triggerEl = this.el.select('.input-group-addon', true).first();
40632 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40637 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40638 _this.list.setWidth(lw);
40641 this.list.on('mouseover', this.onViewOver, this);
40642 this.list.on('mousemove', this.onViewMove, this);
40643 this.list.on('scroll', this.onViewScroll, this);
40646 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40649 this.view = new Roo.View(this.list, this.tpl, {
40650 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40653 this.view.on('click', this.onViewClick, this);
40655 this.store.on('beforeload', this.onBeforeLoad, this);
40656 this.store.on('load', this.onLoad, this);
40657 this.store.on('loadexception', this.onLoadException, this);
40659 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40660 "up" : function(e){
40661 this.inKeyMode = true;
40665 "down" : function(e){
40666 if(!this.isExpanded()){
40667 this.onTriggerClick();
40669 this.inKeyMode = true;
40674 "enter" : function(e){
40677 if(this.fireEvent("specialkey", this, e)){
40678 this.onViewClick(false);
40684 "esc" : function(e){
40688 "tab" : function(e){
40691 if(this.fireEvent("specialkey", this, e)){
40692 this.onViewClick(false);
40700 doRelay : function(foo, bar, hname){
40701 if(hname == 'down' || this.scope.isExpanded()){
40702 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40710 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40714 initNumberEvent : function(e)
40716 this.inputEl().on("keydown" , this.fireKey, this);
40717 this.inputEl().on("focus", this.onFocus, this);
40718 this.inputEl().on("blur", this.onBlur, this);
40720 this.inputEl().relayEvent('keyup', this);
40722 if(this.indicator){
40723 this.indicator.addClass('invisible');
40726 this.originalValue = this.getValue();
40728 if(this.validationEvent == 'keyup'){
40729 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40730 this.inputEl().on('keyup', this.filterValidation, this);
40732 else if(this.validationEvent !== false){
40733 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40736 if(this.selectOnFocus){
40737 this.on("focus", this.preFocus, this);
40740 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40741 this.inputEl().on("keypress", this.filterKeys, this);
40743 this.inputEl().relayEvent('keypress', this);
40746 var allowed = "0123456789";
40748 if(this.allowDecimals){
40749 allowed += this.decimalSeparator;
40752 if(this.allowNegative){
40756 if(this.thousandsDelimiter) {
40760 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40762 var keyPress = function(e){
40764 var k = e.getKey();
40766 var c = e.getCharCode();
40769 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40770 allowed.indexOf(String.fromCharCode(c)) === -1
40776 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40780 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40785 this.inputEl().on("keypress", keyPress, this);
40789 onTriggerClick : function(e)
40796 this.loadNext = false;
40798 if(this.isExpanded()){
40803 this.hasFocus = true;
40805 if(this.triggerAction == 'all') {
40806 this.doQuery(this.allQuery, true);
40810 this.doQuery(this.getRawValue());
40813 getCurrency : function()
40815 var v = this.currencyEl().getValue();
40820 restrictHeight : function()
40822 this.list.alignTo(this.currencyEl(), this.listAlign);
40823 this.list.alignTo(this.currencyEl(), this.listAlign);
40826 onViewClick : function(view, doFocus, el, e)
40828 var index = this.view.getSelectedIndexes()[0];
40830 var r = this.store.getAt(index);
40833 this.onSelect(r, index);
40837 onSelect : function(record, index){
40839 if(this.fireEvent('beforeselect', this, record, index) !== false){
40841 this.setFromCurrencyData(index > -1 ? record.data : false);
40845 this.fireEvent('select', this, record, index);
40849 setFromCurrencyData : function(o)
40853 this.lastCurrency = o;
40855 if (this.currencyField) {
40856 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40858 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40861 this.lastSelectionText = currency;
40863 //setting default currency
40864 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40865 this.setCurrency(this.defaultCurrency);
40869 this.setCurrency(currency);
40872 setFromData : function(o)
40876 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40878 this.setFromCurrencyData(c);
40883 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40885 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40888 this.setValue(value);
40892 setCurrency : function(v)
40894 this.currencyValue = v;
40897 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40902 setValue : function(v)
40904 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40910 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40912 this.inputEl().dom.value = (v == '') ? '' :
40913 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40915 if(!this.allowZero && v === '0') {
40916 this.hiddenEl().dom.value = '';
40917 this.inputEl().dom.value = '';
40924 getRawValue : function()
40926 var v = this.inputEl().getValue();
40931 getValue : function()
40933 return this.fixPrecision(this.parseValue(this.getRawValue()));
40936 parseValue : function(value)
40938 if(this.thousandsDelimiter) {
40940 r = new RegExp(",", "g");
40941 value = value.replace(r, "");
40944 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40945 return isNaN(value) ? '' : value;
40949 fixPrecision : function(value)
40951 if(this.thousandsDelimiter) {
40953 r = new RegExp(",", "g");
40954 value = value.replace(r, "");
40957 var nan = isNaN(value);
40959 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40960 return nan ? '' : value;
40962 return parseFloat(value).toFixed(this.decimalPrecision);
40965 decimalPrecisionFcn : function(v)
40967 return Math.floor(v);
40970 validateValue : function(value)
40972 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40976 var num = this.parseValue(value);
40979 this.markInvalid(String.format(this.nanText, value));
40983 if(num < this.minValue){
40984 this.markInvalid(String.format(this.minText, this.minValue));
40988 if(num > this.maxValue){
40989 this.markInvalid(String.format(this.maxText, this.maxValue));
40996 validate : function()
40998 if(this.disabled || this.allowBlank){
41003 var currency = this.getCurrency();
41005 if(this.validateValue(this.getRawValue()) && currency.length){
41010 this.markInvalid();
41014 getName: function()
41019 beforeBlur : function()
41025 var v = this.parseValue(this.getRawValue());
41032 onBlur : function()
41036 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41037 //this.el.removeClass(this.focusClass);
41040 this.hasFocus = false;
41042 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41046 var v = this.getValue();
41048 if(String(v) !== String(this.startValue)){
41049 this.fireEvent('change', this, v, this.startValue);
41052 this.fireEvent("blur", this);
41055 inputEl : function()
41057 return this.el.select('.roo-money-amount-input', true).first();
41060 currencyEl : function()
41062 return this.el.select('.roo-money-currency-input', true).first();
41065 hiddenEl : function()
41067 return this.el.select('input.hidden-number-input',true).first();