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',
2643 onRender : function(ct, position)
2645 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2648 var cfg = Roo.apply({}, this.getAutoCreate());
2651 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653 //if (!cfg.name.length) {
2657 cfg.cls += ' ' + this.cls;
2660 cfg.style = this.style;
2662 this.el = Roo.get(document.body).createChild(cfg, position);
2664 //var type = this.el.dom.type;
2667 if(this.tabIndex !== undefined){
2668 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2671 this.dialogEl = this.el.select('.modal-dialog',true).first();
2672 this.bodyEl = this.el.select('.modal-body',true).first();
2673 this.closeEl = this.el.select('.modal-header .close', true).first();
2674 this.headerEl = this.el.select('.modal-header',true).first();
2675 this.titleEl = this.el.select('.modal-title',true).first();
2676 this.footerEl = this.el.select('.modal-footer',true).first();
2678 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680 //this.el.addClass("x-dlg-modal");
2682 if (this.buttons.length) {
2683 Roo.each(this.buttons, function(bb) {
2684 var b = Roo.apply({}, bb);
2685 b.xns = b.xns || Roo.bootstrap;
2686 b.xtype = b.xtype || 'Button';
2687 if (typeof(b.listeners) == 'undefined') {
2688 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2691 var btn = Roo.factory(b);
2693 btn.render(this.el.select('.modal-footer div').first());
2697 // render the children.
2700 if(typeof(this.items) != 'undefined'){
2701 var items = this.items;
2704 for(var i =0;i < items.length;i++) {
2705 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2709 this.items = nitems;
2711 // where are these used - they used to be body/close/footer
2715 //this.el.addClass([this.fieldClass, this.cls]);
2719 getAutoCreate : function()
2723 html : this.html || ''
2728 cls : 'modal-title',
2732 if(this.specificTitle){
2738 if (this.allow_close) {
2750 if(this.size.length){
2751 size = 'modal-' + this.size;
2758 cls: "modal-dialog " + size,
2761 cls : "modal-content",
2764 cls : 'modal-header',
2769 cls : 'modal-footer',
2773 cls: 'btn-' + this.buttonPosition
2790 modal.cls += ' fade';
2796 getChildContainer : function() {
2801 getButtonContainer : function() {
2802 return this.el.select('.modal-footer div',true).first();
2805 initEvents : function()
2807 if (this.allow_close) {
2808 this.closeEl.on('click', this.hide, this);
2810 Roo.EventManager.onWindowResize(this.resize, this, true);
2817 this.maskEl.setSize(
2818 Roo.lib.Dom.getViewWidth(true),
2819 Roo.lib.Dom.getViewHeight(true)
2822 if (this.fitwindow) {
2824 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2825 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2830 if(this.max_width !== 0) {
2832 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2837 this.height <= Roo.lib.Dom.getViewportHeight(true) - 60 ?
2838 this.height : Roo.lib.Dom.getViewportHeight(true) - 60
2843 if(!this.fit_content) {
2844 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2848 this.setSize(w, Math.min(
2850 this.headerEl.getHeight() +
2851 this.footerEl.getHeight() +
2852 this.getChildHeight(this.bodyEl.dom.childNodes),
2853 Roo.lib.Dom.getViewportHeight(true) - 60)
2859 setSize : function(w,h)
2870 if (!this.rendered) {
2874 //this.el.setStyle('display', 'block');
2875 this.el.removeClass('hideing');
2876 this.el.addClass('show');
2878 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2881 this.el.addClass('in');
2884 this.el.addClass('in');
2887 // not sure how we can show data in here..
2889 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2892 Roo.get(document.body).addClass("x-body-masked");
2894 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2895 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2896 this.maskEl.addClass('show');
2900 this.fireEvent('show', this);
2902 // set zindex here - otherwise it appears to be ignored...
2903 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2906 this.items.forEach( function(e) {
2907 e.layout ? e.layout() : false;
2915 if(this.fireEvent("beforehide", this) !== false){
2916 this.maskEl.removeClass('show');
2917 Roo.get(document.body).removeClass("x-body-masked");
2918 this.el.removeClass('in');
2919 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2921 if(this.animate){ // why
2922 this.el.addClass('hideing');
2924 if (!this.el.hasClass('hideing')) {
2925 return; // it's been shown again...
2927 this.el.removeClass('show');
2928 this.el.removeClass('hideing');
2932 this.el.removeClass('show');
2934 this.fireEvent('hide', this);
2937 isVisible : function()
2940 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2944 addButton : function(str, cb)
2948 var b = Roo.apply({}, { html : str } );
2949 b.xns = b.xns || Roo.bootstrap;
2950 b.xtype = b.xtype || 'Button';
2951 if (typeof(b.listeners) == 'undefined') {
2952 b.listeners = { click : cb.createDelegate(this) };
2955 var btn = Roo.factory(b);
2957 btn.render(this.el.select('.modal-footer div').first());
2963 setDefaultButton : function(btn)
2965 //this.el.select('.modal-footer').()
2969 resizeTo: function(w,h)
2973 this.dialogEl.setWidth(w);
2974 if (this.diff === false) {
2975 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2978 this.bodyEl.setHeight(h - this.diff);
2980 this.fireEvent('resize', this);
2983 setContentSize : function(w, h)
2987 onButtonClick: function(btn,e)
2990 this.fireEvent('btnclick', btn.name, e);
2993 * Set the title of the Dialog
2994 * @param {String} str new Title
2996 setTitle: function(str) {
2997 this.titleEl.dom.innerHTML = str;
3000 * Set the body of the Dialog
3001 * @param {String} str new Title
3003 setBody: function(str) {
3004 this.bodyEl.dom.innerHTML = str;
3007 * Set the body of the Dialog using the template
3008 * @param {Obj} data - apply this data to the template and replace the body contents.
3010 applyBody: function(obj)
3013 Roo.log("Error - using apply Body without a template");
3016 this.tmpl.overwrite(this.bodyEl, obj);
3019 getChildHeight : function(child_nodes)
3023 child_nodes.length == 0
3028 var child_height = 0;
3030 for(var i = 0; i < child_nodes.length; i++) {
3032 // for modal with tabs...
3033 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3035 var layout_childs = child_nodes[i].childNodes;
3037 for(var j = 0; j < layout_childs.length; j++) {
3039 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3041 var layout_body_childs = layout_childs[j].childNodes;
3043 for(var k = 0; k < layout_body_childs.length; k++) {
3045 if(layout_body_childs[k].classList.contains('navbar')) {
3046 child_height += layout_body_childs[k].offsetHeight;
3047 // Roo.log('nav height: '+ layout_body_childs[k].offsetHeight);
3051 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3053 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3055 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3057 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3058 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3059 // Roo.log('active panel height: '+this.getChildHeight(layout_body_tab_childs[m].childNodes));
3073 child_height += child_nodes[i].offsetHeight;
3076 return child_height;
3082 Roo.apply(Roo.bootstrap.Modal, {
3084 * Button config that displays a single OK button
3093 * Button config that displays Yes and No buttons
3109 * Button config that displays OK and Cancel buttons
3124 * Button config that displays Yes, No and Cancel buttons
3148 * messagebox - can be used as a replace
3152 * @class Roo.MessageBox
3153 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3157 Roo.Msg.alert('Status', 'Changes saved successfully.');
3159 // Prompt for user data:
3160 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3162 // process text value...
3166 // Show a dialog using config options:
3168 title:'Save Changes?',
3169 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3170 buttons: Roo.Msg.YESNOCANCEL,
3177 Roo.bootstrap.MessageBox = function(){
3178 var dlg, opt, mask, waitTimer;
3179 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3180 var buttons, activeTextEl, bwidth;
3184 var handleButton = function(button){
3186 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3190 var handleHide = function(){
3192 dlg.el.removeClass(opt.cls);
3195 // Roo.TaskMgr.stop(waitTimer);
3196 // waitTimer = null;
3201 var updateButtons = function(b){
3204 buttons["ok"].hide();
3205 buttons["cancel"].hide();
3206 buttons["yes"].hide();
3207 buttons["no"].hide();
3208 //dlg.footer.dom.style.display = 'none';
3211 dlg.footerEl.dom.style.display = '';
3212 for(var k in buttons){
3213 if(typeof buttons[k] != "function"){
3216 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3217 width += buttons[k].el.getWidth()+15;
3227 var handleEsc = function(d, k, e){
3228 if(opt && opt.closable !== false){
3238 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3239 * @return {Roo.BasicDialog} The BasicDialog element
3241 getDialog : function(){
3243 dlg = new Roo.bootstrap.Modal( {
3246 //constraintoviewport:false,
3248 //collapsible : false,
3253 //buttonAlign:"center",
3254 closeClick : function(){
3255 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3258 handleButton("cancel");
3263 dlg.on("hide", handleHide);
3265 //dlg.addKeyListener(27, handleEsc);
3267 this.buttons = buttons;
3268 var bt = this.buttonText;
3269 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3270 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3271 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3272 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3274 bodyEl = dlg.bodyEl.createChild({
3276 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3277 '<textarea class="roo-mb-textarea"></textarea>' +
3278 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3280 msgEl = bodyEl.dom.firstChild;
3281 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3282 textboxEl.enableDisplayMode();
3283 textboxEl.addKeyListener([10,13], function(){
3284 if(dlg.isVisible() && opt && opt.buttons){
3287 }else if(opt.buttons.yes){
3288 handleButton("yes");
3292 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3293 textareaEl.enableDisplayMode();
3294 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3295 progressEl.enableDisplayMode();
3297 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3298 var pf = progressEl.dom.firstChild;
3300 pp = Roo.get(pf.firstChild);
3301 pp.setHeight(pf.offsetHeight);
3309 * Updates the message box body text
3310 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3311 * the XHTML-compliant non-breaking space character '&#160;')
3312 * @return {Roo.MessageBox} This message box
3314 updateText : function(text)
3316 if(!dlg.isVisible() && !opt.width){
3317 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3318 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3320 msgEl.innerHTML = text || ' ';
3322 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3323 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3325 Math.min(opt.width || cw , this.maxWidth),
3326 Math.max(opt.minWidth || this.minWidth, bwidth)
3329 activeTextEl.setWidth(w);
3331 if(dlg.isVisible()){
3332 dlg.fixedcenter = false;
3334 // to big, make it scroll. = But as usual stupid IE does not support
3337 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3338 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3339 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3341 bodyEl.dom.style.height = '';
3342 bodyEl.dom.style.overflowY = '';
3345 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3347 bodyEl.dom.style.overflowX = '';
3350 dlg.setContentSize(w, bodyEl.getHeight());
3351 if(dlg.isVisible()){
3352 dlg.fixedcenter = true;
3358 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3359 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3360 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3361 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3362 * @return {Roo.MessageBox} This message box
3364 updateProgress : function(value, text){
3366 this.updateText(text);
3369 if (pp) { // weird bug on my firefox - for some reason this is not defined
3370 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3371 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3377 * Returns true if the message box is currently displayed
3378 * @return {Boolean} True if the message box is visible, else false
3380 isVisible : function(){
3381 return dlg && dlg.isVisible();
3385 * Hides the message box if it is displayed
3388 if(this.isVisible()){
3394 * Displays a new message box, or reinitializes an existing message box, based on the config options
3395 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3396 * The following config object properties are supported:
3398 Property Type Description
3399 ---------- --------------- ------------------------------------------------------------------------------------
3400 animEl String/Element An id or Element from which the message box should animate as it opens and
3401 closes (defaults to undefined)
3402 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3403 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3404 closable Boolean False to hide the top-right close button (defaults to true). Note that
3405 progress and wait dialogs will ignore this property and always hide the
3406 close button as they can only be closed programmatically.
3407 cls String A custom CSS class to apply to the message box element
3408 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3409 displayed (defaults to 75)
3410 fn Function A callback function to execute after closing the dialog. The arguments to the
3411 function will be btn (the name of the button that was clicked, if applicable,
3412 e.g. "ok"), and text (the value of the active text field, if applicable).
3413 Progress and wait dialogs will ignore this option since they do not respond to
3414 user actions and can only be closed programmatically, so any required function
3415 should be called by the same code after it closes the dialog.
3416 icon String A CSS class that provides a background image to be used as an icon for
3417 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3418 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3419 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3420 modal Boolean False to allow user interaction with the page while the message box is
3421 displayed (defaults to true)
3422 msg String A string that will replace the existing message box body text (defaults
3423 to the XHTML-compliant non-breaking space character ' ')
3424 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3425 progress Boolean True to display a progress bar (defaults to false)
3426 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3427 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3428 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3429 title String The title text
3430 value String The string value to set into the active textbox element if displayed
3431 wait Boolean True to display a progress bar (defaults to false)
3432 width Number The width of the dialog in pixels
3439 msg: 'Please enter your address:',
3441 buttons: Roo.MessageBox.OKCANCEL,
3444 animEl: 'addAddressBtn'
3447 * @param {Object} config Configuration options
3448 * @return {Roo.MessageBox} This message box
3450 show : function(options)
3453 // this causes nightmares if you show one dialog after another
3454 // especially on callbacks..
3456 if(this.isVisible()){
3459 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3460 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3461 Roo.log("New Dialog Message:" + options.msg )
3462 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3463 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3466 var d = this.getDialog();
3468 d.setTitle(opt.title || " ");
3469 d.closeEl.setDisplayed(opt.closable !== false);
3470 activeTextEl = textboxEl;
3471 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3476 textareaEl.setHeight(typeof opt.multiline == "number" ?
3477 opt.multiline : this.defaultTextHeight);
3478 activeTextEl = textareaEl;
3487 progressEl.setDisplayed(opt.progress === true);
3488 this.updateProgress(0);
3489 activeTextEl.dom.value = opt.value || "";
3491 dlg.setDefaultButton(activeTextEl);
3493 var bs = opt.buttons;
3497 }else if(bs && bs.yes){
3498 db = buttons["yes"];
3500 dlg.setDefaultButton(db);
3502 bwidth = updateButtons(opt.buttons);
3503 this.updateText(opt.msg);
3505 d.el.addClass(opt.cls);
3507 d.proxyDrag = opt.proxyDrag === true;
3508 d.modal = opt.modal !== false;
3509 d.mask = opt.modal !== false ? mask : false;
3511 // force it to the end of the z-index stack so it gets a cursor in FF
3512 document.body.appendChild(dlg.el.dom);
3513 d.animateTarget = null;
3514 d.show(options.animEl);
3520 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3521 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3522 * and closing the message box when the process is complete.
3523 * @param {String} title The title bar text
3524 * @param {String} msg The message box body text
3525 * @return {Roo.MessageBox} This message box
3527 progress : function(title, msg){
3534 minWidth: this.minProgressWidth,
3541 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3542 * If a callback function is passed it will be called after the user clicks the button, and the
3543 * id of the button that was clicked will be passed as the only parameter to the callback
3544 * (could also be the top-right close button).
3545 * @param {String} title The title bar text
3546 * @param {String} msg The message box body text
3547 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3548 * @param {Object} scope (optional) The scope of the callback function
3549 * @return {Roo.MessageBox} This message box
3551 alert : function(title, msg, fn, scope)
3566 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3567 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3568 * You are responsible for closing the message box when the process is complete.
3569 * @param {String} msg The message box body text
3570 * @param {String} title (optional) The title bar text
3571 * @return {Roo.MessageBox} This message box
3573 wait : function(msg, title){
3584 waitTimer = Roo.TaskMgr.start({
3586 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3594 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3595 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3596 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3597 * @param {String} title The title bar text
3598 * @param {String} msg The message box body text
3599 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3600 * @param {Object} scope (optional) The scope of the callback function
3601 * @return {Roo.MessageBox} This message box
3603 confirm : function(title, msg, fn, scope){
3607 buttons: this.YESNO,
3616 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3617 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3618 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3619 * (could also be the top-right close button) and the text that was entered will be passed as the two
3620 * parameters to the callback.
3621 * @param {String} title The title bar text
3622 * @param {String} msg The message box body text
3623 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3624 * @param {Object} scope (optional) The scope of the callback function
3625 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3626 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3627 * @return {Roo.MessageBox} This message box
3629 prompt : function(title, msg, fn, scope, multiline){
3633 buttons: this.OKCANCEL,
3638 multiline: multiline,
3645 * Button config that displays a single OK button
3650 * Button config that displays Yes and No buttons
3653 YESNO : {yes:true, no:true},
3655 * Button config that displays OK and Cancel buttons
3658 OKCANCEL : {ok:true, cancel:true},
3660 * Button config that displays Yes, No and Cancel buttons
3663 YESNOCANCEL : {yes:true, no:true, cancel:true},
3666 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3669 defaultTextHeight : 75,
3671 * The maximum width in pixels of the message box (defaults to 600)
3676 * The minimum width in pixels of the message box (defaults to 100)
3681 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3682 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3685 minProgressWidth : 250,
3687 * An object containing the default button text strings that can be overriden for localized language support.
3688 * Supported properties are: ok, cancel, yes and no.
3689 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3702 * Shorthand for {@link Roo.MessageBox}
3704 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3705 Roo.Msg = Roo.Msg || Roo.MessageBox;
3714 * @class Roo.bootstrap.Navbar
3715 * @extends Roo.bootstrap.Component
3716 * Bootstrap Navbar class
3719 * Create a new Navbar
3720 * @param {Object} config The config object
3724 Roo.bootstrap.Navbar = function(config){
3725 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3729 * @event beforetoggle
3730 * Fire before toggle the menu
3731 * @param {Roo.EventObject} e
3733 "beforetoggle" : true
3737 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3746 getAutoCreate : function(){
3749 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3753 initEvents :function ()
3755 //Roo.log(this.el.select('.navbar-toggle',true));
3756 this.el.select('.navbar-toggle',true).on('click', function() {
3757 if(this.fireEvent('beforetoggle', this) !== false){
3758 this.el.select('.navbar-collapse',true).toggleClass('in');
3768 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3770 var size = this.el.getSize();
3771 this.maskEl.setSize(size.width, size.height);
3772 this.maskEl.enableDisplayMode("block");
3781 getChildContainer : function()
3783 if (this.el.select('.collapse').getCount()) {
3784 return this.el.select('.collapse',true).first();
3817 * @class Roo.bootstrap.NavSimplebar
3818 * @extends Roo.bootstrap.Navbar
3819 * Bootstrap Sidebar class
3821 * @cfg {Boolean} inverse is inverted color
3823 * @cfg {String} type (nav | pills | tabs)
3824 * @cfg {Boolean} arrangement stacked | justified
3825 * @cfg {String} align (left | right) alignment
3827 * @cfg {Boolean} main (true|false) main nav bar? default false
3828 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3830 * @cfg {String} tag (header|footer|nav|div) default is nav
3836 * Create a new Sidebar
3837 * @param {Object} config The config object
3841 Roo.bootstrap.NavSimplebar = function(config){
3842 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3845 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3861 getAutoCreate : function(){
3865 tag : this.tag || 'div',
3878 this.type = this.type || 'nav';
3879 if (['tabs','pills'].indexOf(this.type)!==-1) {
3880 cfg.cn[0].cls += ' nav-' + this.type
3884 if (this.type!=='nav') {
3885 Roo.log('nav type must be nav/tabs/pills')
3887 cfg.cn[0].cls += ' navbar-nav'
3893 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3894 cfg.cn[0].cls += ' nav-' + this.arrangement;
3898 if (this.align === 'right') {
3899 cfg.cn[0].cls += ' navbar-right';
3903 cfg.cls += ' navbar-inverse';
3930 * @class Roo.bootstrap.NavHeaderbar
3931 * @extends Roo.bootstrap.NavSimplebar
3932 * Bootstrap Sidebar class
3934 * @cfg {String} brand what is brand
3935 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3936 * @cfg {String} brand_href href of the brand
3937 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3938 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3939 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3940 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3943 * Create a new Sidebar
3944 * @param {Object} config The config object
3948 Roo.bootstrap.NavHeaderbar = function(config){
3949 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3953 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3960 desktopCenter : false,
3963 getAutoCreate : function(){
3966 tag: this.nav || 'nav',
3973 if (this.desktopCenter) {
3974 cn.push({cls : 'container', cn : []});
3981 cls: 'navbar-header',
3986 cls: 'navbar-toggle',
3987 'data-toggle': 'collapse',
3992 html: 'Toggle navigation'
4014 cls: 'collapse navbar-collapse',
4018 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4020 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4021 cfg.cls += ' navbar-' + this.position;
4023 // tag can override this..
4025 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4028 if (this.brand !== '') {
4031 href: this.brand_href ? this.brand_href : '#',
4032 cls: 'navbar-brand',
4040 cfg.cls += ' main-nav';
4048 getHeaderChildContainer : function()
4050 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4051 return this.el.select('.navbar-header',true).first();
4054 return this.getChildContainer();
4058 initEvents : function()
4060 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4062 if (this.autohide) {
4067 Roo.get(document).on('scroll',function(e) {
4068 var ns = Roo.get(document).getScroll().top;
4069 var os = prevScroll;
4073 ft.removeClass('slideDown');
4074 ft.addClass('slideUp');
4077 ft.removeClass('slideUp');
4078 ft.addClass('slideDown');
4099 * @class Roo.bootstrap.NavSidebar
4100 * @extends Roo.bootstrap.Navbar
4101 * Bootstrap Sidebar class
4104 * Create a new Sidebar
4105 * @param {Object} config The config object
4109 Roo.bootstrap.NavSidebar = function(config){
4110 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4113 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4115 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4117 getAutoCreate : function(){
4122 cls: 'sidebar sidebar-nav'
4144 * @class Roo.bootstrap.NavGroup
4145 * @extends Roo.bootstrap.Component
4146 * Bootstrap NavGroup class
4147 * @cfg {String} align (left|right)
4148 * @cfg {Boolean} inverse
4149 * @cfg {String} type (nav|pills|tab) default nav
4150 * @cfg {String} navId - reference Id for navbar.
4154 * Create a new nav group
4155 * @param {Object} config The config object
4158 Roo.bootstrap.NavGroup = function(config){
4159 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4162 Roo.bootstrap.NavGroup.register(this);
4166 * Fires when the active item changes
4167 * @param {Roo.bootstrap.NavGroup} this
4168 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4169 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4176 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4187 getAutoCreate : function()
4189 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4196 if (['tabs','pills'].indexOf(this.type)!==-1) {
4197 cfg.cls += ' nav-' + this.type
4199 if (this.type!=='nav') {
4200 Roo.log('nav type must be nav/tabs/pills')
4202 cfg.cls += ' navbar-nav'
4205 if (this.parent() && this.parent().sidebar) {
4208 cls: 'dashboard-menu sidebar-menu'
4214 if (this.form === true) {
4220 if (this.align === 'right') {
4221 cfg.cls += ' navbar-right';
4223 cfg.cls += ' navbar-left';
4227 if (this.align === 'right') {
4228 cfg.cls += ' navbar-right';
4232 cfg.cls += ' navbar-inverse';
4240 * sets the active Navigation item
4241 * @param {Roo.bootstrap.NavItem} the new current navitem
4243 setActiveItem : function(item)
4246 Roo.each(this.navItems, function(v){
4251 v.setActive(false, true);
4258 item.setActive(true, true);
4259 this.fireEvent('changed', this, item, prev);
4264 * gets the active Navigation item
4265 * @return {Roo.bootstrap.NavItem} the current navitem
4267 getActive : function()
4271 Roo.each(this.navItems, function(v){
4282 indexOfNav : function()
4286 Roo.each(this.navItems, function(v,i){
4297 * adds a Navigation item
4298 * @param {Roo.bootstrap.NavItem} the navitem to add
4300 addItem : function(cfg)
4302 var cn = new Roo.bootstrap.NavItem(cfg);
4304 cn.parentId = this.id;
4305 cn.onRender(this.el, null);
4309 * register a Navigation item
4310 * @param {Roo.bootstrap.NavItem} the navitem to add
4312 register : function(item)
4314 this.navItems.push( item);
4315 item.navId = this.navId;
4320 * clear all the Navigation item
4323 clearAll : function()
4326 this.el.dom.innerHTML = '';
4329 getNavItem: function(tabId)
4332 Roo.each(this.navItems, function(e) {
4333 if (e.tabId == tabId) {
4343 setActiveNext : function()
4345 var i = this.indexOfNav(this.getActive());
4346 if (i > this.navItems.length) {
4349 this.setActiveItem(this.navItems[i+1]);
4351 setActivePrev : function()
4353 var i = this.indexOfNav(this.getActive());
4357 this.setActiveItem(this.navItems[i-1]);
4359 clearWasActive : function(except) {
4360 Roo.each(this.navItems, function(e) {
4361 if (e.tabId != except.tabId && e.was_active) {
4362 e.was_active = false;
4369 getWasActive : function ()
4372 Roo.each(this.navItems, function(e) {
4387 Roo.apply(Roo.bootstrap.NavGroup, {
4391 * register a Navigation Group
4392 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4394 register : function(navgrp)
4396 this.groups[navgrp.navId] = navgrp;
4400 * fetch a Navigation Group based on the navigation ID
4401 * @param {string} the navgroup to add
4402 * @returns {Roo.bootstrap.NavGroup} the navgroup
4404 get: function(navId) {
4405 if (typeof(this.groups[navId]) == 'undefined') {
4407 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4409 return this.groups[navId] ;
4424 * @class Roo.bootstrap.NavItem
4425 * @extends Roo.bootstrap.Component
4426 * Bootstrap Navbar.NavItem class
4427 * @cfg {String} href link to
4428 * @cfg {String} html content of button
4429 * @cfg {String} badge text inside badge
4430 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4431 * @cfg {String} glyphicon name of glyphicon
4432 * @cfg {String} icon name of font awesome icon
4433 * @cfg {Boolean} active Is item active
4434 * @cfg {Boolean} disabled Is item disabled
4436 * @cfg {Boolean} preventDefault (true | false) default false
4437 * @cfg {String} tabId the tab that this item activates.
4438 * @cfg {String} tagtype (a|span) render as a href or span?
4439 * @cfg {Boolean} animateRef (true|false) link to element default false
4442 * Create a new Navbar Item
4443 * @param {Object} config The config object
4445 Roo.bootstrap.NavItem = function(config){
4446 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4451 * The raw click event for the entire grid.
4452 * @param {Roo.EventObject} e
4457 * Fires when the active item active state changes
4458 * @param {Roo.bootstrap.NavItem} this
4459 * @param {boolean} state the new state
4465 * Fires when scroll to element
4466 * @param {Roo.bootstrap.NavItem} this
4467 * @param {Object} options
4468 * @param {Roo.EventObject} e
4476 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4484 preventDefault : false,
4491 getAutoCreate : function(){
4500 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4502 if (this.disabled) {
4503 cfg.cls += ' disabled';
4506 if (this.href || this.html || this.glyphicon || this.icon) {
4510 href : this.href || "#",
4511 html: this.html || ''
4516 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4519 if(this.glyphicon) {
4520 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4525 cfg.cn[0].html += " <span class='caret'></span>";
4529 if (this.badge !== '') {
4531 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4539 initEvents: function()
4541 if (typeof (this.menu) != 'undefined') {
4542 this.menu.parentType = this.xtype;
4543 this.menu.triggerEl = this.el;
4544 this.menu = this.addxtype(Roo.apply({}, this.menu));
4547 this.el.select('a',true).on('click', this.onClick, this);
4549 if(this.tagtype == 'span'){
4550 this.el.select('span',true).on('click', this.onClick, this);
4553 // at this point parent should be available..
4554 this.parent().register(this);
4557 onClick : function(e)
4559 if (e.getTarget('.dropdown-menu-item')) {
4560 // did you click on a menu itemm.... - then don't trigger onclick..
4565 this.preventDefault ||
4568 Roo.log("NavItem - prevent Default?");
4572 if (this.disabled) {
4576 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4577 if (tg && tg.transition) {
4578 Roo.log("waiting for the transitionend");
4584 //Roo.log("fire event clicked");
4585 if(this.fireEvent('click', this, e) === false){
4589 if(this.tagtype == 'span'){
4593 //Roo.log(this.href);
4594 var ael = this.el.select('a',true).first();
4597 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4598 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4599 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4600 return; // ignore... - it's a 'hash' to another page.
4602 Roo.log("NavItem - prevent Default?");
4604 this.scrollToElement(e);
4608 var p = this.parent();
4610 if (['tabs','pills'].indexOf(p.type)!==-1) {
4611 if (typeof(p.setActiveItem) !== 'undefined') {
4612 p.setActiveItem(this);
4616 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4617 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4618 // remove the collapsed menu expand...
4619 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4623 isActive: function () {
4626 setActive : function(state, fire, is_was_active)
4628 if (this.active && !state && this.navId) {
4629 this.was_active = true;
4630 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4632 nv.clearWasActive(this);
4636 this.active = state;
4639 this.el.removeClass('active');
4640 } else if (!this.el.hasClass('active')) {
4641 this.el.addClass('active');
4644 this.fireEvent('changed', this, state);
4647 // show a panel if it's registered and related..
4649 if (!this.navId || !this.tabId || !state || is_was_active) {
4653 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4657 var pan = tg.getPanelByName(this.tabId);
4661 // if we can not flip to new panel - go back to old nav highlight..
4662 if (false == tg.showPanel(pan)) {
4663 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4665 var onav = nv.getWasActive();
4667 onav.setActive(true, false, true);
4676 // this should not be here...
4677 setDisabled : function(state)
4679 this.disabled = state;
4681 this.el.removeClass('disabled');
4682 } else if (!this.el.hasClass('disabled')) {
4683 this.el.addClass('disabled');
4689 * Fetch the element to display the tooltip on.
4690 * @return {Roo.Element} defaults to this.el
4692 tooltipEl : function()
4694 return this.el.select('' + this.tagtype + '', true).first();
4697 scrollToElement : function(e)
4699 var c = document.body;
4702 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4704 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4705 c = document.documentElement;
4708 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4714 var o = target.calcOffsetsTo(c);
4721 this.fireEvent('scrollto', this, options, e);
4723 Roo.get(c).scrollTo('top', options.value, true);
4736 * <span> icon </span>
4737 * <span> text </span>
4738 * <span>badge </span>
4742 * @class Roo.bootstrap.NavSidebarItem
4743 * @extends Roo.bootstrap.NavItem
4744 * Bootstrap Navbar.NavSidebarItem class
4745 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4746 * {Boolean} open is the menu open
4747 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4748 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4749 * {String} buttonSize (sm|md|lg)the extra classes for the button
4750 * {Boolean} showArrow show arrow next to the text (default true)
4752 * Create a new Navbar Button
4753 * @param {Object} config The config object
4755 Roo.bootstrap.NavSidebarItem = function(config){
4756 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4761 * The raw click event for the entire grid.
4762 * @param {Roo.EventObject} e
4767 * Fires when the active item active state changes
4768 * @param {Roo.bootstrap.NavSidebarItem} this
4769 * @param {boolean} state the new state
4777 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4779 badgeWeight : 'default',
4785 buttonWeight : 'default',
4791 getAutoCreate : function(){
4796 href : this.href || '#',
4802 if(this.buttonView){
4805 href : this.href || '#',
4806 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4819 cfg.cls += ' active';
4822 if (this.disabled) {
4823 cfg.cls += ' disabled';
4826 cfg.cls += ' open x-open';
4829 if (this.glyphicon || this.icon) {
4830 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4831 a.cn.push({ tag : 'i', cls : c }) ;
4834 if(!this.buttonView){
4837 html : this.html || ''
4844 if (this.badge !== '') {
4845 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4851 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4854 a.cls += ' dropdown-toggle treeview' ;
4860 initEvents : function()
4862 if (typeof (this.menu) != 'undefined') {
4863 this.menu.parentType = this.xtype;
4864 this.menu.triggerEl = this.el;
4865 this.menu = this.addxtype(Roo.apply({}, this.menu));
4868 this.el.on('click', this.onClick, this);
4870 if(this.badge !== ''){
4871 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4876 onClick : function(e)
4883 if(this.preventDefault){
4887 this.fireEvent('click', this);
4890 disable : function()
4892 this.setDisabled(true);
4897 this.setDisabled(false);
4900 setDisabled : function(state)
4902 if(this.disabled == state){
4906 this.disabled = state;
4909 this.el.addClass('disabled');
4913 this.el.removeClass('disabled');
4918 setActive : function(state)
4920 if(this.active == state){
4924 this.active = state;
4927 this.el.addClass('active');
4931 this.el.removeClass('active');
4936 isActive: function ()
4941 setBadge : function(str)
4947 this.badgeEl.dom.innerHTML = str;
4964 * @class Roo.bootstrap.Row
4965 * @extends Roo.bootstrap.Component
4966 * Bootstrap Row class (contains columns...)
4970 * @param {Object} config The config object
4973 Roo.bootstrap.Row = function(config){
4974 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4977 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4979 getAutoCreate : function(){
4998 * @class Roo.bootstrap.Element
4999 * @extends Roo.bootstrap.Component
5000 * Bootstrap Element class
5001 * @cfg {String} html contents of the element
5002 * @cfg {String} tag tag of the element
5003 * @cfg {String} cls class of the element
5004 * @cfg {Boolean} preventDefault (true|false) default false
5005 * @cfg {Boolean} clickable (true|false) default false
5008 * Create a new Element
5009 * @param {Object} config The config object
5012 Roo.bootstrap.Element = function(config){
5013 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5019 * When a element is chick
5020 * @param {Roo.bootstrap.Element} this
5021 * @param {Roo.EventObject} e
5027 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5032 preventDefault: false,
5035 getAutoCreate : function(){
5039 // cls: this.cls, double assign in parent class Component.js :: onRender
5046 initEvents: function()
5048 Roo.bootstrap.Element.superclass.initEvents.call(this);
5051 this.el.on('click', this.onClick, this);
5056 onClick : function(e)
5058 if(this.preventDefault){
5062 this.fireEvent('click', this, e);
5065 getValue : function()
5067 return this.el.dom.innerHTML;
5070 setValue : function(value)
5072 this.el.dom.innerHTML = value;
5087 * @class Roo.bootstrap.Pagination
5088 * @extends Roo.bootstrap.Component
5089 * Bootstrap Pagination class
5090 * @cfg {String} size xs | sm | md | lg
5091 * @cfg {Boolean} inverse false | true
5094 * Create a new Pagination
5095 * @param {Object} config The config object
5098 Roo.bootstrap.Pagination = function(config){
5099 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5108 getAutoCreate : function(){
5114 cfg.cls += ' inverse';
5120 cfg.cls += " " + this.cls;
5138 * @class Roo.bootstrap.PaginationItem
5139 * @extends Roo.bootstrap.Component
5140 * Bootstrap PaginationItem class
5141 * @cfg {String} html text
5142 * @cfg {String} href the link
5143 * @cfg {Boolean} preventDefault (true | false) default true
5144 * @cfg {Boolean} active (true | false) default false
5145 * @cfg {Boolean} disabled default false
5149 * Create a new PaginationItem
5150 * @param {Object} config The config object
5154 Roo.bootstrap.PaginationItem = function(config){
5155 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5160 * The raw click event for the entire grid.
5161 * @param {Roo.EventObject} e
5167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5171 preventDefault: true,
5176 getAutoCreate : function(){
5182 href : this.href ? this.href : '#',
5183 html : this.html ? this.html : ''
5193 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5197 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5203 initEvents: function() {
5205 this.el.on('click', this.onClick, this);
5208 onClick : function(e)
5210 Roo.log('PaginationItem on click ');
5211 if(this.preventDefault){
5219 this.fireEvent('click', this, e);
5235 * @class Roo.bootstrap.Slider
5236 * @extends Roo.bootstrap.Component
5237 * Bootstrap Slider class
5240 * Create a new Slider
5241 * @param {Object} config The config object
5244 Roo.bootstrap.Slider = function(config){
5245 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5250 getAutoCreate : function(){
5254 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5258 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5270 * Ext JS Library 1.1.1
5271 * Copyright(c) 2006-2007, Ext JS, LLC.
5273 * Originally Released Under LGPL - original licence link has changed is not relivant.
5276 * <script type="text/javascript">
5281 * @class Roo.grid.ColumnModel
5282 * @extends Roo.util.Observable
5283 * This is the default implementation of a ColumnModel used by the Grid. It defines
5284 * the columns in the grid.
5287 var colModel = new Roo.grid.ColumnModel([
5288 {header: "Ticker", width: 60, sortable: true, locked: true},
5289 {header: "Company Name", width: 150, sortable: true},
5290 {header: "Market Cap.", width: 100, sortable: true},
5291 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5292 {header: "Employees", width: 100, sortable: true, resizable: false}
5297 * The config options listed for this class are options which may appear in each
5298 * individual column definition.
5299 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5301 * @param {Object} config An Array of column config objects. See this class's
5302 * config objects for details.
5304 Roo.grid.ColumnModel = function(config){
5306 * The config passed into the constructor
5308 this.config = config;
5311 // if no id, create one
5312 // if the column does not have a dataIndex mapping,
5313 // map it to the order it is in the config
5314 for(var i = 0, len = config.length; i < len; i++){
5316 if(typeof c.dataIndex == "undefined"){
5319 if(typeof c.renderer == "string"){
5320 c.renderer = Roo.util.Format[c.renderer];
5322 if(typeof c.id == "undefined"){
5325 if(c.editor && c.editor.xtype){
5326 c.editor = Roo.factory(c.editor, Roo.grid);
5328 if(c.editor && c.editor.isFormField){
5329 c.editor = new Roo.grid.GridEditor(c.editor);
5331 this.lookup[c.id] = c;
5335 * The width of columns which have no width specified (defaults to 100)
5338 this.defaultWidth = 100;
5341 * Default sortable of columns which have no sortable specified (defaults to false)
5344 this.defaultSortable = false;
5348 * @event widthchange
5349 * Fires when the width of a column changes.
5350 * @param {ColumnModel} this
5351 * @param {Number} columnIndex The column index
5352 * @param {Number} newWidth The new width
5354 "widthchange": true,
5356 * @event headerchange
5357 * Fires when the text of a header changes.
5358 * @param {ColumnModel} this
5359 * @param {Number} columnIndex The column index
5360 * @param {Number} newText The new header text
5362 "headerchange": true,
5364 * @event hiddenchange
5365 * Fires when a column is hidden or "unhidden".
5366 * @param {ColumnModel} this
5367 * @param {Number} columnIndex The column index
5368 * @param {Boolean} hidden true if hidden, false otherwise
5370 "hiddenchange": true,
5372 * @event columnmoved
5373 * Fires when a column is moved.
5374 * @param {ColumnModel} this
5375 * @param {Number} oldIndex
5376 * @param {Number} newIndex
5378 "columnmoved" : true,
5380 * @event columlockchange
5381 * Fires when a column's locked state is changed
5382 * @param {ColumnModel} this
5383 * @param {Number} colIndex
5384 * @param {Boolean} locked true if locked
5386 "columnlockchange" : true
5388 Roo.grid.ColumnModel.superclass.constructor.call(this);
5390 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5392 * @cfg {String} header The header text to display in the Grid view.
5395 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5396 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5397 * specified, the column's index is used as an index into the Record's data Array.
5400 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5401 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5404 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5405 * Defaults to the value of the {@link #defaultSortable} property.
5406 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5409 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5412 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5415 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5418 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5421 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5422 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5423 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5424 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5427 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5430 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5433 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5436 * @cfg {String} cursor (Optional)
5439 * @cfg {String} tooltip (Optional)
5442 * @cfg {Number} xs (Optional)
5445 * @cfg {Number} sm (Optional)
5448 * @cfg {Number} md (Optional)
5451 * @cfg {Number} lg (Optional)
5454 * Returns the id of the column at the specified index.
5455 * @param {Number} index The column index
5456 * @return {String} the id
5458 getColumnId : function(index){
5459 return this.config[index].id;
5463 * Returns the column for a specified id.
5464 * @param {String} id The column id
5465 * @return {Object} the column
5467 getColumnById : function(id){
5468 return this.lookup[id];
5473 * Returns the column for a specified dataIndex.
5474 * @param {String} dataIndex The column dataIndex
5475 * @return {Object|Boolean} the column or false if not found
5477 getColumnByDataIndex: function(dataIndex){
5478 var index = this.findColumnIndex(dataIndex);
5479 return index > -1 ? this.config[index] : false;
5483 * Returns the index for a specified column id.
5484 * @param {String} id The column id
5485 * @return {Number} the index, or -1 if not found
5487 getIndexById : function(id){
5488 for(var i = 0, len = this.config.length; i < len; i++){
5489 if(this.config[i].id == id){
5497 * Returns the index for a specified column dataIndex.
5498 * @param {String} dataIndex The column dataIndex
5499 * @return {Number} the index, or -1 if not found
5502 findColumnIndex : function(dataIndex){
5503 for(var i = 0, len = this.config.length; i < len; i++){
5504 if(this.config[i].dataIndex == dataIndex){
5512 moveColumn : function(oldIndex, newIndex){
5513 var c = this.config[oldIndex];
5514 this.config.splice(oldIndex, 1);
5515 this.config.splice(newIndex, 0, c);
5516 this.dataMap = null;
5517 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5520 isLocked : function(colIndex){
5521 return this.config[colIndex].locked === true;
5524 setLocked : function(colIndex, value, suppressEvent){
5525 if(this.isLocked(colIndex) == value){
5528 this.config[colIndex].locked = value;
5530 this.fireEvent("columnlockchange", this, colIndex, value);
5534 getTotalLockedWidth : function(){
5536 for(var i = 0; i < this.config.length; i++){
5537 if(this.isLocked(i) && !this.isHidden(i)){
5538 this.totalWidth += this.getColumnWidth(i);
5544 getLockedCount : function(){
5545 for(var i = 0, len = this.config.length; i < len; i++){
5546 if(!this.isLocked(i)){
5551 return this.config.length;
5555 * Returns the number of columns.
5558 getColumnCount : function(visibleOnly){
5559 if(visibleOnly === true){
5561 for(var i = 0, len = this.config.length; i < len; i++){
5562 if(!this.isHidden(i)){
5568 return this.config.length;
5572 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5573 * @param {Function} fn
5574 * @param {Object} scope (optional)
5575 * @return {Array} result
5577 getColumnsBy : function(fn, scope){
5579 for(var i = 0, len = this.config.length; i < len; i++){
5580 var c = this.config[i];
5581 if(fn.call(scope||this, c, i) === true){
5589 * Returns true if the specified column is sortable.
5590 * @param {Number} col The column index
5593 isSortable : function(col){
5594 if(typeof this.config[col].sortable == "undefined"){
5595 return this.defaultSortable;
5597 return this.config[col].sortable;
5601 * Returns the rendering (formatting) function defined for the column.
5602 * @param {Number} col The column index.
5603 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5605 getRenderer : function(col){
5606 if(!this.config[col].renderer){
5607 return Roo.grid.ColumnModel.defaultRenderer;
5609 return this.config[col].renderer;
5613 * Sets the rendering (formatting) function for a column.
5614 * @param {Number} col The column index
5615 * @param {Function} fn The function to use to process the cell's raw data
5616 * to return HTML markup for the grid view. The render function is called with
5617 * the following parameters:<ul>
5618 * <li>Data value.</li>
5619 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5620 * <li>css A CSS style string to apply to the table cell.</li>
5621 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5622 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5623 * <li>Row index</li>
5624 * <li>Column index</li>
5625 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5627 setRenderer : function(col, fn){
5628 this.config[col].renderer = fn;
5632 * Returns the width for the specified column.
5633 * @param {Number} col The column index
5636 getColumnWidth : function(col){
5637 return this.config[col].width * 1 || this.defaultWidth;
5641 * Sets the width for a column.
5642 * @param {Number} col The column index
5643 * @param {Number} width The new width
5645 setColumnWidth : function(col, width, suppressEvent){
5646 this.config[col].width = width;
5647 this.totalWidth = null;
5649 this.fireEvent("widthchange", this, col, width);
5654 * Returns the total width of all columns.
5655 * @param {Boolean} includeHidden True to include hidden column widths
5658 getTotalWidth : function(includeHidden){
5659 if(!this.totalWidth){
5660 this.totalWidth = 0;
5661 for(var i = 0, len = this.config.length; i < len; i++){
5662 if(includeHidden || !this.isHidden(i)){
5663 this.totalWidth += this.getColumnWidth(i);
5667 return this.totalWidth;
5671 * Returns the header for the specified column.
5672 * @param {Number} col The column index
5675 getColumnHeader : function(col){
5676 return this.config[col].header;
5680 * Sets the header for a column.
5681 * @param {Number} col The column index
5682 * @param {String} header The new header
5684 setColumnHeader : function(col, header){
5685 this.config[col].header = header;
5686 this.fireEvent("headerchange", this, col, header);
5690 * Returns the tooltip for the specified column.
5691 * @param {Number} col The column index
5694 getColumnTooltip : function(col){
5695 return this.config[col].tooltip;
5698 * Sets the tooltip for a column.
5699 * @param {Number} col The column index
5700 * @param {String} tooltip The new tooltip
5702 setColumnTooltip : function(col, tooltip){
5703 this.config[col].tooltip = tooltip;
5707 * Returns the dataIndex for the specified column.
5708 * @param {Number} col The column index
5711 getDataIndex : function(col){
5712 return this.config[col].dataIndex;
5716 * Sets the dataIndex for a column.
5717 * @param {Number} col The column index
5718 * @param {Number} dataIndex The new dataIndex
5720 setDataIndex : function(col, dataIndex){
5721 this.config[col].dataIndex = dataIndex;
5727 * Returns true if the cell is editable.
5728 * @param {Number} colIndex The column index
5729 * @param {Number} rowIndex The row index - this is nto actually used..?
5732 isCellEditable : function(colIndex, rowIndex){
5733 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5737 * Returns the editor defined for the cell/column.
5738 * return false or null to disable editing.
5739 * @param {Number} colIndex The column index
5740 * @param {Number} rowIndex The row index
5743 getCellEditor : function(colIndex, rowIndex){
5744 return this.config[colIndex].editor;
5748 * Sets if a column is editable.
5749 * @param {Number} col The column index
5750 * @param {Boolean} editable True if the column is editable
5752 setEditable : function(col, editable){
5753 this.config[col].editable = editable;
5758 * Returns true if the column is hidden.
5759 * @param {Number} colIndex The column index
5762 isHidden : function(colIndex){
5763 return this.config[colIndex].hidden;
5768 * Returns true if the column width cannot be changed
5770 isFixed : function(colIndex){
5771 return this.config[colIndex].fixed;
5775 * Returns true if the column can be resized
5778 isResizable : function(colIndex){
5779 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5782 * Sets if a column is hidden.
5783 * @param {Number} colIndex The column index
5784 * @param {Boolean} hidden True if the column is hidden
5786 setHidden : function(colIndex, hidden){
5787 this.config[colIndex].hidden = hidden;
5788 this.totalWidth = null;
5789 this.fireEvent("hiddenchange", this, colIndex, hidden);
5793 * Sets the editor for a column.
5794 * @param {Number} col The column index
5795 * @param {Object} editor The editor object
5797 setEditor : function(col, editor){
5798 this.config[col].editor = editor;
5802 Roo.grid.ColumnModel.defaultRenderer = function(value)
5804 if(typeof value == "object") {
5807 if(typeof value == "string" && value.length < 1){
5811 return String.format("{0}", value);
5814 // Alias for backwards compatibility
5815 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5818 * Ext JS Library 1.1.1
5819 * Copyright(c) 2006-2007, Ext JS, LLC.
5821 * Originally Released Under LGPL - original licence link has changed is not relivant.
5824 * <script type="text/javascript">
5828 * @class Roo.LoadMask
5829 * A simple utility class for generically masking elements while loading data. If the element being masked has
5830 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5831 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5832 * element's UpdateManager load indicator and will be destroyed after the initial load.
5834 * Create a new LoadMask
5835 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5836 * @param {Object} config The config object
5838 Roo.LoadMask = function(el, config){
5839 this.el = Roo.get(el);
5840 Roo.apply(this, config);
5842 this.store.on('beforeload', this.onBeforeLoad, this);
5843 this.store.on('load', this.onLoad, this);
5844 this.store.on('loadexception', this.onLoadException, this);
5845 this.removeMask = false;
5847 var um = this.el.getUpdateManager();
5848 um.showLoadIndicator = false; // disable the default indicator
5849 um.on('beforeupdate', this.onBeforeLoad, this);
5850 um.on('update', this.onLoad, this);
5851 um.on('failure', this.onLoad, this);
5852 this.removeMask = true;
5856 Roo.LoadMask.prototype = {
5858 * @cfg {Boolean} removeMask
5859 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5860 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5864 * The text to display in a centered loading message box (defaults to 'Loading...')
5868 * @cfg {String} msgCls
5869 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5871 msgCls : 'x-mask-loading',
5874 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5880 * Disables the mask to prevent it from being displayed
5882 disable : function(){
5883 this.disabled = true;
5887 * Enables the mask so that it can be displayed
5889 enable : function(){
5890 this.disabled = false;
5893 onLoadException : function()
5897 if (typeof(arguments[3]) != 'undefined') {
5898 Roo.MessageBox.alert("Error loading",arguments[3]);
5902 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5903 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5910 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5915 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5919 onBeforeLoad : function(){
5921 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5926 destroy : function(){
5928 this.store.un('beforeload', this.onBeforeLoad, this);
5929 this.store.un('load', this.onLoad, this);
5930 this.store.un('loadexception', this.onLoadException, this);
5932 var um = this.el.getUpdateManager();
5933 um.un('beforeupdate', this.onBeforeLoad, this);
5934 um.un('update', this.onLoad, this);
5935 um.un('failure', this.onLoad, this);
5946 * @class Roo.bootstrap.Table
5947 * @extends Roo.bootstrap.Component
5948 * Bootstrap Table class
5949 * @cfg {String} cls table class
5950 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5951 * @cfg {String} bgcolor Specifies the background color for a table
5952 * @cfg {Number} border Specifies whether the table cells should have borders or not
5953 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5954 * @cfg {Number} cellspacing Specifies the space between cells
5955 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5956 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5957 * @cfg {String} sortable Specifies that the table should be sortable
5958 * @cfg {String} summary Specifies a summary of the content of a table
5959 * @cfg {Number} width Specifies the width of a table
5960 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5962 * @cfg {boolean} striped Should the rows be alternative striped
5963 * @cfg {boolean} bordered Add borders to the table
5964 * @cfg {boolean} hover Add hover highlighting
5965 * @cfg {boolean} condensed Format condensed
5966 * @cfg {boolean} responsive Format condensed
5967 * @cfg {Boolean} loadMask (true|false) default false
5968 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5969 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5970 * @cfg {Boolean} rowSelection (true|false) default false
5971 * @cfg {Boolean} cellSelection (true|false) default false
5972 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5973 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5974 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5975 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5979 * Create a new Table
5980 * @param {Object} config The config object
5983 Roo.bootstrap.Table = function(config){
5984 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5989 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5990 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5991 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5992 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5994 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5996 this.sm.grid = this;
5997 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5998 this.sm = this.selModel;
5999 this.sm.xmodule = this.xmodule || false;
6002 if (this.cm && typeof(this.cm.config) == 'undefined') {
6003 this.colModel = new Roo.grid.ColumnModel(this.cm);
6004 this.cm = this.colModel;
6005 this.cm.xmodule = this.xmodule || false;
6008 this.store= Roo.factory(this.store, Roo.data);
6009 this.ds = this.store;
6010 this.ds.xmodule = this.xmodule || false;
6013 if (this.footer && this.store) {
6014 this.footer.dataSource = this.ds;
6015 this.footer = Roo.factory(this.footer);
6022 * Fires when a cell is clicked
6023 * @param {Roo.bootstrap.Table} this
6024 * @param {Roo.Element} el
6025 * @param {Number} rowIndex
6026 * @param {Number} columnIndex
6027 * @param {Roo.EventObject} e
6031 * @event celldblclick
6032 * Fires when a cell is double clicked
6033 * @param {Roo.bootstrap.Table} this
6034 * @param {Roo.Element} el
6035 * @param {Number} rowIndex
6036 * @param {Number} columnIndex
6037 * @param {Roo.EventObject} e
6039 "celldblclick" : true,
6042 * Fires when a row is clicked
6043 * @param {Roo.bootstrap.Table} this
6044 * @param {Roo.Element} el
6045 * @param {Number} rowIndex
6046 * @param {Roo.EventObject} e
6050 * @event rowdblclick
6051 * Fires when a row is double clicked
6052 * @param {Roo.bootstrap.Table} this
6053 * @param {Roo.Element} el
6054 * @param {Number} rowIndex
6055 * @param {Roo.EventObject} e
6057 "rowdblclick" : true,
6060 * Fires when a mouseover occur
6061 * @param {Roo.bootstrap.Table} this
6062 * @param {Roo.Element} el
6063 * @param {Number} rowIndex
6064 * @param {Number} columnIndex
6065 * @param {Roo.EventObject} e
6070 * Fires when a mouseout occur
6071 * @param {Roo.bootstrap.Table} this
6072 * @param {Roo.Element} el
6073 * @param {Number} rowIndex
6074 * @param {Number} columnIndex
6075 * @param {Roo.EventObject} e
6080 * Fires when a row is rendered, so you can change add a style to it.
6081 * @param {Roo.bootstrap.Table} this
6082 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6086 * @event rowsrendered
6087 * Fires when all the rows have been rendered
6088 * @param {Roo.bootstrap.Table} this
6090 'rowsrendered' : true,
6092 * @event contextmenu
6093 * The raw contextmenu event for the entire grid.
6094 * @param {Roo.EventObject} e
6096 "contextmenu" : true,
6098 * @event rowcontextmenu
6099 * Fires when a row is right clicked
6100 * @param {Roo.bootstrap.Table} this
6101 * @param {Number} rowIndex
6102 * @param {Roo.EventObject} e
6104 "rowcontextmenu" : true,
6106 * @event cellcontextmenu
6107 * Fires when a cell is right clicked
6108 * @param {Roo.bootstrap.Table} this
6109 * @param {Number} rowIndex
6110 * @param {Number} cellIndex
6111 * @param {Roo.EventObject} e
6113 "cellcontextmenu" : true,
6115 * @event headercontextmenu
6116 * Fires when a header is right clicked
6117 * @param {Roo.bootstrap.Table} this
6118 * @param {Number} columnIndex
6119 * @param {Roo.EventObject} e
6121 "headercontextmenu" : true
6125 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6151 rowSelection : false,
6152 cellSelection : false,
6155 // Roo.Element - the tbody
6157 // Roo.Element - thead element
6160 container: false, // used by gridpanel...
6166 auto_hide_footer : false,
6168 getAutoCreate : function()
6170 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6177 if (this.scrollBody) {
6178 cfg.cls += ' table-body-fixed';
6181 cfg.cls += ' table-striped';
6185 cfg.cls += ' table-hover';
6187 if (this.bordered) {
6188 cfg.cls += ' table-bordered';
6190 if (this.condensed) {
6191 cfg.cls += ' table-condensed';
6193 if (this.responsive) {
6194 cfg.cls += ' table-responsive';
6198 cfg.cls+= ' ' +this.cls;
6201 // this lot should be simplifed...
6214 ].forEach(function(k) {
6222 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6225 if(this.store || this.cm){
6226 if(this.headerShow){
6227 cfg.cn.push(this.renderHeader());
6230 cfg.cn.push(this.renderBody());
6232 if(this.footerShow){
6233 cfg.cn.push(this.renderFooter());
6235 // where does this come from?
6236 //cfg.cls+= ' TableGrid';
6239 return { cn : [ cfg ] };
6242 initEvents : function()
6244 if(!this.store || !this.cm){
6247 if (this.selModel) {
6248 this.selModel.initEvents();
6252 //Roo.log('initEvents with ds!!!!');
6254 this.mainBody = this.el.select('tbody', true).first();
6255 this.mainHead = this.el.select('thead', true).first();
6256 this.mainFoot = this.el.select('tfoot', true).first();
6262 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6263 e.on('click', _this.sort, _this);
6266 this.mainBody.on("click", this.onClick, this);
6267 this.mainBody.on("dblclick", this.onDblClick, this);
6269 // why is this done????? = it breaks dialogs??
6270 //this.parent().el.setStyle('position', 'relative');
6274 this.footer.parentId = this.id;
6275 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6278 this.el.select('tfoot tr td').first().addClass('hide');
6283 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6286 this.store.on('load', this.onLoad, this);
6287 this.store.on('beforeload', this.onBeforeLoad, this);
6288 this.store.on('update', this.onUpdate, this);
6289 this.store.on('add', this.onAdd, this);
6290 this.store.on("clear", this.clear, this);
6292 this.el.on("contextmenu", this.onContextMenu, this);
6294 this.mainBody.on('scroll', this.onBodyScroll, this);
6296 this.cm.on("headerchange", this.onHeaderChange, this);
6298 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6302 onContextMenu : function(e, t)
6304 this.processEvent("contextmenu", e);
6307 processEvent : function(name, e)
6309 if (name != 'touchstart' ) {
6310 this.fireEvent(name, e);
6313 var t = e.getTarget();
6315 var cell = Roo.get(t);
6321 if(cell.findParent('tfoot', false, true)){
6325 if(cell.findParent('thead', false, true)){
6327 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6328 cell = Roo.get(t).findParent('th', false, true);
6330 Roo.log("failed to find th in thead?");
6331 Roo.log(e.getTarget());
6336 var cellIndex = cell.dom.cellIndex;
6338 var ename = name == 'touchstart' ? 'click' : name;
6339 this.fireEvent("header" + ename, this, cellIndex, e);
6344 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6345 cell = Roo.get(t).findParent('td', false, true);
6347 Roo.log("failed to find th in tbody?");
6348 Roo.log(e.getTarget());
6353 var row = cell.findParent('tr', false, true);
6354 var cellIndex = cell.dom.cellIndex;
6355 var rowIndex = row.dom.rowIndex - 1;
6359 this.fireEvent("row" + name, this, rowIndex, e);
6363 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6369 onMouseover : function(e, el)
6371 var cell = Roo.get(el);
6377 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6378 cell = cell.findParent('td', false, true);
6381 var row = cell.findParent('tr', false, true);
6382 var cellIndex = cell.dom.cellIndex;
6383 var rowIndex = row.dom.rowIndex - 1; // start from 0
6385 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6389 onMouseout : function(e, el)
6391 var cell = Roo.get(el);
6397 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6398 cell = cell.findParent('td', false, true);
6401 var row = cell.findParent('tr', false, true);
6402 var cellIndex = cell.dom.cellIndex;
6403 var rowIndex = row.dom.rowIndex - 1; // start from 0
6405 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6409 onClick : function(e, el)
6411 var cell = Roo.get(el);
6413 if(!cell || (!this.cellSelection && !this.rowSelection)){
6417 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6418 cell = cell.findParent('td', false, true);
6421 if(!cell || typeof(cell) == 'undefined'){
6425 var row = cell.findParent('tr', false, true);
6427 if(!row || typeof(row) == 'undefined'){
6431 var cellIndex = cell.dom.cellIndex;
6432 var rowIndex = this.getRowIndex(row);
6434 // why??? - should these not be based on SelectionModel?
6435 if(this.cellSelection){
6436 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6439 if(this.rowSelection){
6440 this.fireEvent('rowclick', this, row, rowIndex, e);
6446 onDblClick : function(e,el)
6448 var cell = Roo.get(el);
6450 if(!cell || (!this.cellSelection && !this.rowSelection)){
6454 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6455 cell = cell.findParent('td', false, true);
6458 if(!cell || typeof(cell) == 'undefined'){
6462 var row = cell.findParent('tr', false, true);
6464 if(!row || typeof(row) == 'undefined'){
6468 var cellIndex = cell.dom.cellIndex;
6469 var rowIndex = this.getRowIndex(row);
6471 if(this.cellSelection){
6472 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6475 if(this.rowSelection){
6476 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6480 sort : function(e,el)
6482 var col = Roo.get(el);
6484 if(!col.hasClass('sortable')){
6488 var sort = col.attr('sort');
6491 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6495 this.store.sortInfo = {field : sort, direction : dir};
6498 Roo.log("calling footer first");
6499 this.footer.onClick('first');
6502 this.store.load({ params : { start : 0 } });
6506 renderHeader : function()
6514 this.totalWidth = 0;
6516 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6518 var config = cm.config[i];
6522 cls : 'x-hcol-' + i,
6524 html: cm.getColumnHeader(i)
6529 if(typeof(config.sortable) != 'undefined' && config.sortable){
6531 c.html = '<i class="glyphicon"></i>' + c.html;
6534 if(typeof(config.lgHeader) != 'undefined'){
6535 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6538 if(typeof(config.mdHeader) != 'undefined'){
6539 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6542 if(typeof(config.smHeader) != 'undefined'){
6543 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6546 if(typeof(config.xsHeader) != 'undefined'){
6547 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6554 if(typeof(config.tooltip) != 'undefined'){
6555 c.tooltip = config.tooltip;
6558 if(typeof(config.colspan) != 'undefined'){
6559 c.colspan = config.colspan;
6562 if(typeof(config.hidden) != 'undefined' && config.hidden){
6563 c.style += ' display:none;';
6566 if(typeof(config.dataIndex) != 'undefined'){
6567 c.sort = config.dataIndex;
6572 if(typeof(config.align) != 'undefined' && config.align.length){
6573 c.style += ' text-align:' + config.align + ';';
6576 if(typeof(config.width) != 'undefined'){
6577 c.style += ' width:' + config.width + 'px;';
6578 this.totalWidth += config.width;
6580 this.totalWidth += 100; // assume minimum of 100 per column?
6583 if(typeof(config.cls) != 'undefined'){
6584 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6587 ['xs','sm','md','lg'].map(function(size){
6589 if(typeof(config[size]) == 'undefined'){
6593 if (!config[size]) { // 0 = hidden
6594 c.cls += ' hidden-' + size;
6598 c.cls += ' col-' + size + '-' + config[size];
6608 renderBody : function()
6618 colspan : this.cm.getColumnCount()
6628 renderFooter : function()
6638 colspan : this.cm.getColumnCount()
6652 // Roo.log('ds onload');
6657 var ds = this.store;
6659 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6660 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6661 if (_this.store.sortInfo) {
6663 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6664 e.select('i', true).addClass(['glyphicon-arrow-up']);
6667 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6668 e.select('i', true).addClass(['glyphicon-arrow-down']);
6673 var tbody = this.mainBody;
6675 if(ds.getCount() > 0){
6676 ds.data.each(function(d,rowIndex){
6677 var row = this.renderRow(cm, ds, rowIndex);
6679 tbody.createChild(row);
6683 if(row.cellObjects.length){
6684 Roo.each(row.cellObjects, function(r){
6685 _this.renderCellObject(r);
6692 var tfoot = this.el.select('tfoot', true).first();
6694 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6696 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6698 var total = this.ds.getTotalCount();
6700 if(this.footer.pageSize < total){
6701 this.mainFoot.show();
6705 Roo.each(this.el.select('tbody td', true).elements, function(e){
6706 e.on('mouseover', _this.onMouseover, _this);
6709 Roo.each(this.el.select('tbody td', true).elements, function(e){
6710 e.on('mouseout', _this.onMouseout, _this);
6712 this.fireEvent('rowsrendered', this);
6718 onUpdate : function(ds,record)
6720 this.refreshRow(record);
6724 onRemove : function(ds, record, index, isUpdate){
6725 if(isUpdate !== true){
6726 this.fireEvent("beforerowremoved", this, index, record);
6728 var bt = this.mainBody.dom;
6730 var rows = this.el.select('tbody > tr', true).elements;
6732 if(typeof(rows[index]) != 'undefined'){
6733 bt.removeChild(rows[index].dom);
6736 // if(bt.rows[index]){
6737 // bt.removeChild(bt.rows[index]);
6740 if(isUpdate !== true){
6741 //this.stripeRows(index);
6742 //this.syncRowHeights(index, index);
6744 this.fireEvent("rowremoved", this, index, record);
6748 onAdd : function(ds, records, rowIndex)
6750 //Roo.log('on Add called');
6751 // - note this does not handle multiple adding very well..
6752 var bt = this.mainBody.dom;
6753 for (var i =0 ; i < records.length;i++) {
6754 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6755 //Roo.log(records[i]);
6756 //Roo.log(this.store.getAt(rowIndex+i));
6757 this.insertRow(this.store, rowIndex + i, false);
6764 refreshRow : function(record){
6765 var ds = this.store, index;
6766 if(typeof record == 'number'){
6768 record = ds.getAt(index);
6770 index = ds.indexOf(record);
6772 this.insertRow(ds, index, true);
6774 this.onRemove(ds, record, index+1, true);
6776 //this.syncRowHeights(index, index);
6778 this.fireEvent("rowupdated", this, index, record);
6781 insertRow : function(dm, rowIndex, isUpdate){
6784 this.fireEvent("beforerowsinserted", this, rowIndex);
6786 //var s = this.getScrollState();
6787 var row = this.renderRow(this.cm, this.store, rowIndex);
6788 // insert before rowIndex..
6789 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6793 if(row.cellObjects.length){
6794 Roo.each(row.cellObjects, function(r){
6795 _this.renderCellObject(r);
6800 this.fireEvent("rowsinserted", this, rowIndex);
6801 //this.syncRowHeights(firstRow, lastRow);
6802 //this.stripeRows(firstRow);
6809 getRowDom : function(rowIndex)
6811 var rows = this.el.select('tbody > tr', true).elements;
6813 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6816 // returns the object tree for a tr..
6819 renderRow : function(cm, ds, rowIndex)
6821 var d = ds.getAt(rowIndex);
6825 cls : 'x-row-' + rowIndex,
6829 var cellObjects = [];
6831 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6832 var config = cm.config[i];
6834 var renderer = cm.getRenderer(i);
6838 if(typeof(renderer) !== 'undefined'){
6839 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6841 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6842 // and are rendered into the cells after the row is rendered - using the id for the element.
6844 if(typeof(value) === 'object'){
6854 rowIndex : rowIndex,
6859 this.fireEvent('rowclass', this, rowcfg);
6863 cls : rowcfg.rowClass + ' x-col-' + i,
6865 html: (typeof(value) === 'object') ? '' : value
6872 if(typeof(config.colspan) != 'undefined'){
6873 td.colspan = config.colspan;
6876 if(typeof(config.hidden) != 'undefined' && config.hidden){
6877 td.style += ' display:none;';
6880 if(typeof(config.align) != 'undefined' && config.align.length){
6881 td.style += ' text-align:' + config.align + ';';
6883 if(typeof(config.valign) != 'undefined' && config.valign.length){
6884 td.style += ' vertical-align:' + config.valign + ';';
6887 if(typeof(config.width) != 'undefined'){
6888 td.style += ' width:' + config.width + 'px;';
6891 if(typeof(config.cursor) != 'undefined'){
6892 td.style += ' cursor:' + config.cursor + ';';
6895 if(typeof(config.cls) != 'undefined'){
6896 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6899 ['xs','sm','md','lg'].map(function(size){
6901 if(typeof(config[size]) == 'undefined'){
6905 if (!config[size]) { // 0 = hidden
6906 td.cls += ' hidden-' + size;
6910 td.cls += ' col-' + size + '-' + config[size];
6918 row.cellObjects = cellObjects;
6926 onBeforeLoad : function()
6935 this.el.select('tbody', true).first().dom.innerHTML = '';
6938 * Show or hide a row.
6939 * @param {Number} rowIndex to show or hide
6940 * @param {Boolean} state hide
6942 setRowVisibility : function(rowIndex, state)
6944 var bt = this.mainBody.dom;
6946 var rows = this.el.select('tbody > tr', true).elements;
6948 if(typeof(rows[rowIndex]) == 'undefined'){
6951 rows[rowIndex].dom.style.display = state ? '' : 'none';
6955 getSelectionModel : function(){
6957 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6959 return this.selModel;
6962 * Render the Roo.bootstrap object from renderder
6964 renderCellObject : function(r)
6968 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6970 var t = r.cfg.render(r.container);
6973 Roo.each(r.cfg.cn, function(c){
6975 container: t.getChildContainer(),
6978 _this.renderCellObject(child);
6983 getRowIndex : function(row)
6987 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6998 * Returns the grid's underlying element = used by panel.Grid
6999 * @return {Element} The element
7001 getGridEl : function(){
7005 * Forces a resize - used by panel.Grid
7006 * @return {Element} The element
7008 autoSize : function()
7010 //var ctr = Roo.get(this.container.dom.parentElement);
7011 var ctr = Roo.get(this.el.dom);
7013 var thd = this.getGridEl().select('thead',true).first();
7014 var tbd = this.getGridEl().select('tbody', true).first();
7015 var tfd = this.getGridEl().select('tfoot', true).first();
7017 var cw = ctr.getWidth();
7021 tbd.setSize(ctr.getWidth(),
7022 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7024 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7027 cw = Math.max(cw, this.totalWidth);
7028 this.getGridEl().select('tr',true).setWidth(cw);
7029 // resize 'expandable coloumn?
7031 return; // we doe not have a view in this design..
7034 onBodyScroll: function()
7036 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7038 this.mainHead.setStyle({
7039 'position' : 'relative',
7040 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7046 var scrollHeight = this.mainBody.dom.scrollHeight;
7048 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7050 var height = this.mainBody.getHeight();
7052 if(scrollHeight - height == scrollTop) {
7054 var total = this.ds.getTotalCount();
7056 if(this.footer.cursor + this.footer.pageSize < total){
7058 this.footer.ds.load({
7060 start : this.footer.cursor + this.footer.pageSize,
7061 limit : this.footer.pageSize
7071 onHeaderChange : function()
7073 var header = this.renderHeader();
7074 var table = this.el.select('table', true).first();
7076 this.mainHead.remove();
7077 this.mainHead = table.createChild(header, this.mainBody, false);
7080 onHiddenChange : function(colModel, colIndex, hidden)
7082 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7083 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7085 this.CSS.updateRule(thSelector, "display", "");
7086 this.CSS.updateRule(tdSelector, "display", "");
7089 this.CSS.updateRule(thSelector, "display", "none");
7090 this.CSS.updateRule(tdSelector, "display", "none");
7093 this.onHeaderChange();
7110 * @class Roo.bootstrap.TableCell
7111 * @extends Roo.bootstrap.Component
7112 * Bootstrap TableCell class
7113 * @cfg {String} html cell contain text
7114 * @cfg {String} cls cell class
7115 * @cfg {String} tag cell tag (td|th) default td
7116 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7117 * @cfg {String} align Aligns the content in a cell
7118 * @cfg {String} axis Categorizes cells
7119 * @cfg {String} bgcolor Specifies the background color of a cell
7120 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7121 * @cfg {Number} colspan Specifies the number of columns a cell should span
7122 * @cfg {String} headers Specifies one or more header cells a cell is related to
7123 * @cfg {Number} height Sets the height of a cell
7124 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7125 * @cfg {Number} rowspan Sets the number of rows a cell should span
7126 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7127 * @cfg {String} valign Vertical aligns the content in a cell
7128 * @cfg {Number} width Specifies the width of a cell
7131 * Create a new TableCell
7132 * @param {Object} config The config object
7135 Roo.bootstrap.TableCell = function(config){
7136 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7139 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7159 getAutoCreate : function(){
7160 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7180 cfg.align=this.align
7186 cfg.bgcolor=this.bgcolor
7189 cfg.charoff=this.charoff
7192 cfg.colspan=this.colspan
7195 cfg.headers=this.headers
7198 cfg.height=this.height
7201 cfg.nowrap=this.nowrap
7204 cfg.rowspan=this.rowspan
7207 cfg.scope=this.scope
7210 cfg.valign=this.valign
7213 cfg.width=this.width
7232 * @class Roo.bootstrap.TableRow
7233 * @extends Roo.bootstrap.Component
7234 * Bootstrap TableRow class
7235 * @cfg {String} cls row class
7236 * @cfg {String} align Aligns the content in a table row
7237 * @cfg {String} bgcolor Specifies a background color for a table row
7238 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7239 * @cfg {String} valign Vertical aligns the content in a table row
7242 * Create a new TableRow
7243 * @param {Object} config The config object
7246 Roo.bootstrap.TableRow = function(config){
7247 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7250 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7258 getAutoCreate : function(){
7259 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7269 cfg.align = this.align;
7272 cfg.bgcolor = this.bgcolor;
7275 cfg.charoff = this.charoff;
7278 cfg.valign = this.valign;
7296 * @class Roo.bootstrap.TableBody
7297 * @extends Roo.bootstrap.Component
7298 * Bootstrap TableBody class
7299 * @cfg {String} cls element class
7300 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7301 * @cfg {String} align Aligns the content inside the element
7302 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7303 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7306 * Create a new TableBody
7307 * @param {Object} config The config object
7310 Roo.bootstrap.TableBody = function(config){
7311 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7314 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7322 getAutoCreate : function(){
7323 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7337 cfg.align = this.align;
7340 cfg.charoff = this.charoff;
7343 cfg.valign = this.valign;
7350 // initEvents : function()
7357 // this.store = Roo.factory(this.store, Roo.data);
7358 // this.store.on('load', this.onLoad, this);
7360 // this.store.load();
7364 // onLoad: function ()
7366 // this.fireEvent('load', this);
7376 * Ext JS Library 1.1.1
7377 * Copyright(c) 2006-2007, Ext JS, LLC.
7379 * Originally Released Under LGPL - original licence link has changed is not relivant.
7382 * <script type="text/javascript">
7385 // as we use this in bootstrap.
7386 Roo.namespace('Roo.form');
7388 * @class Roo.form.Action
7389 * Internal Class used to handle form actions
7391 * @param {Roo.form.BasicForm} el The form element or its id
7392 * @param {Object} config Configuration options
7397 // define the action interface
7398 Roo.form.Action = function(form, options){
7400 this.options = options || {};
7403 * Client Validation Failed
7406 Roo.form.Action.CLIENT_INVALID = 'client';
7408 * Server Validation Failed
7411 Roo.form.Action.SERVER_INVALID = 'server';
7413 * Connect to Server Failed
7416 Roo.form.Action.CONNECT_FAILURE = 'connect';
7418 * Reading Data from Server Failed
7421 Roo.form.Action.LOAD_FAILURE = 'load';
7423 Roo.form.Action.prototype = {
7425 failureType : undefined,
7426 response : undefined,
7430 run : function(options){
7435 success : function(response){
7440 handleResponse : function(response){
7444 // default connection failure
7445 failure : function(response){
7447 this.response = response;
7448 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7449 this.form.afterAction(this, false);
7452 processResponse : function(response){
7453 this.response = response;
7454 if(!response.responseText){
7457 this.result = this.handleResponse(response);
7461 // utility functions used internally
7462 getUrl : function(appendParams){
7463 var url = this.options.url || this.form.url || this.form.el.dom.action;
7465 var p = this.getParams();
7467 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7473 getMethod : function(){
7474 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7477 getParams : function(){
7478 var bp = this.form.baseParams;
7479 var p = this.options.params;
7481 if(typeof p == "object"){
7482 p = Roo.urlEncode(Roo.applyIf(p, bp));
7483 }else if(typeof p == 'string' && bp){
7484 p += '&' + Roo.urlEncode(bp);
7487 p = Roo.urlEncode(bp);
7492 createCallback : function(){
7494 success: this.success,
7495 failure: this.failure,
7497 timeout: (this.form.timeout*1000),
7498 upload: this.form.fileUpload ? this.success : undefined
7503 Roo.form.Action.Submit = function(form, options){
7504 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7507 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7510 haveProgress : false,
7511 uploadComplete : false,
7513 // uploadProgress indicator.
7514 uploadProgress : function()
7516 if (!this.form.progressUrl) {
7520 if (!this.haveProgress) {
7521 Roo.MessageBox.progress("Uploading", "Uploading");
7523 if (this.uploadComplete) {
7524 Roo.MessageBox.hide();
7528 this.haveProgress = true;
7530 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7532 var c = new Roo.data.Connection();
7534 url : this.form.progressUrl,
7539 success : function(req){
7540 //console.log(data);
7544 rdata = Roo.decode(req.responseText)
7546 Roo.log("Invalid data from server..");
7550 if (!rdata || !rdata.success) {
7552 Roo.MessageBox.alert(Roo.encode(rdata));
7555 var data = rdata.data;
7557 if (this.uploadComplete) {
7558 Roo.MessageBox.hide();
7563 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7564 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7567 this.uploadProgress.defer(2000,this);
7570 failure: function(data) {
7571 Roo.log('progress url failed ');
7582 // run get Values on the form, so it syncs any secondary forms.
7583 this.form.getValues();
7585 var o = this.options;
7586 var method = this.getMethod();
7587 var isPost = method == 'POST';
7588 if(o.clientValidation === false || this.form.isValid()){
7590 if (this.form.progressUrl) {
7591 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7592 (new Date() * 1) + '' + Math.random());
7597 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7598 form:this.form.el.dom,
7599 url:this.getUrl(!isPost),
7601 params:isPost ? this.getParams() : null,
7602 isUpload: this.form.fileUpload
7605 this.uploadProgress();
7607 }else if (o.clientValidation !== false){ // client validation failed
7608 this.failureType = Roo.form.Action.CLIENT_INVALID;
7609 this.form.afterAction(this, false);
7613 success : function(response)
7615 this.uploadComplete= true;
7616 if (this.haveProgress) {
7617 Roo.MessageBox.hide();
7621 var result = this.processResponse(response);
7622 if(result === true || result.success){
7623 this.form.afterAction(this, true);
7627 this.form.markInvalid(result.errors);
7628 this.failureType = Roo.form.Action.SERVER_INVALID;
7630 this.form.afterAction(this, false);
7632 failure : function(response)
7634 this.uploadComplete= true;
7635 if (this.haveProgress) {
7636 Roo.MessageBox.hide();
7639 this.response = response;
7640 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7641 this.form.afterAction(this, false);
7644 handleResponse : function(response){
7645 if(this.form.errorReader){
7646 var rs = this.form.errorReader.read(response);
7649 for(var i = 0, len = rs.records.length; i < len; i++) {
7650 var r = rs.records[i];
7654 if(errors.length < 1){
7658 success : rs.success,
7664 ret = Roo.decode(response.responseText);
7668 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7678 Roo.form.Action.Load = function(form, options){
7679 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7680 this.reader = this.form.reader;
7683 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7688 Roo.Ajax.request(Roo.apply(
7689 this.createCallback(), {
7690 method:this.getMethod(),
7691 url:this.getUrl(false),
7692 params:this.getParams()
7696 success : function(response){
7698 var result = this.processResponse(response);
7699 if(result === true || !result.success || !result.data){
7700 this.failureType = Roo.form.Action.LOAD_FAILURE;
7701 this.form.afterAction(this, false);
7704 this.form.clearInvalid();
7705 this.form.setValues(result.data);
7706 this.form.afterAction(this, true);
7709 handleResponse : function(response){
7710 if(this.form.reader){
7711 var rs = this.form.reader.read(response);
7712 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7714 success : rs.success,
7718 return Roo.decode(response.responseText);
7722 Roo.form.Action.ACTION_TYPES = {
7723 'load' : Roo.form.Action.Load,
7724 'submit' : Roo.form.Action.Submit
7733 * @class Roo.bootstrap.Form
7734 * @extends Roo.bootstrap.Component
7735 * Bootstrap Form class
7736 * @cfg {String} method GET | POST (default POST)
7737 * @cfg {String} labelAlign top | left (default top)
7738 * @cfg {String} align left | right - for navbars
7739 * @cfg {Boolean} loadMask load mask when submit (default true)
7744 * @param {Object} config The config object
7748 Roo.bootstrap.Form = function(config){
7750 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7752 Roo.bootstrap.Form.popover.apply();
7756 * @event clientvalidation
7757 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7758 * @param {Form} this
7759 * @param {Boolean} valid true if the form has passed client-side validation
7761 clientvalidation: true,
7763 * @event beforeaction
7764 * Fires before any action is performed. Return false to cancel the action.
7765 * @param {Form} this
7766 * @param {Action} action The action to be performed
7770 * @event actionfailed
7771 * Fires when an action fails.
7772 * @param {Form} this
7773 * @param {Action} action The action that failed
7775 actionfailed : true,
7777 * @event actioncomplete
7778 * Fires when an action is completed.
7779 * @param {Form} this
7780 * @param {Action} action The action that completed
7782 actioncomplete : true
7786 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7789 * @cfg {String} method
7790 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7795 * The URL to use for form actions if one isn't supplied in the action options.
7798 * @cfg {Boolean} fileUpload
7799 * Set to true if this form is a file upload.
7803 * @cfg {Object} baseParams
7804 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7808 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7812 * @cfg {Sting} align (left|right) for navbar forms
7817 activeAction : null,
7820 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7821 * element by passing it or its id or mask the form itself by passing in true.
7824 waitMsgTarget : false,
7829 * @cfg {Boolean} errorMask (true|false) default false
7834 * @cfg {Number} maskOffset Default 100
7839 * @cfg {Boolean} maskBody
7843 getAutoCreate : function(){
7847 method : this.method || 'POST',
7848 id : this.id || Roo.id(),
7851 if (this.parent().xtype.match(/^Nav/)) {
7852 cfg.cls = 'navbar-form navbar-' + this.align;
7856 if (this.labelAlign == 'left' ) {
7857 cfg.cls += ' form-horizontal';
7863 initEvents : function()
7865 this.el.on('submit', this.onSubmit, this);
7866 // this was added as random key presses on the form where triggering form submit.
7867 this.el.on('keypress', function(e) {
7868 if (e.getCharCode() != 13) {
7871 // we might need to allow it for textareas.. and some other items.
7872 // check e.getTarget().
7874 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7878 Roo.log("keypress blocked");
7886 onSubmit : function(e){
7891 * Returns true if client-side validation on the form is successful.
7894 isValid : function(){
7895 var items = this.getItems();
7899 items.each(function(f){
7905 Roo.log('invalid field: ' + f.name);
7909 if(!target && f.el.isVisible(true)){
7915 if(this.errorMask && !valid){
7916 Roo.bootstrap.Form.popover.mask(this, target);
7923 * Returns true if any fields in this form have changed since their original load.
7926 isDirty : function(){
7928 var items = this.getItems();
7929 items.each(function(f){
7939 * Performs a predefined action (submit or load) or custom actions you define on this form.
7940 * @param {String} actionName The name of the action type
7941 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7942 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7943 * accept other config options):
7945 Property Type Description
7946 ---------------- --------------- ----------------------------------------------------------------------------------
7947 url String The url for the action (defaults to the form's url)
7948 method String The form method to use (defaults to the form's method, or POST if not defined)
7949 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7950 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7951 validate the form on the client (defaults to false)
7953 * @return {BasicForm} this
7955 doAction : function(action, options){
7956 if(typeof action == 'string'){
7957 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7959 if(this.fireEvent('beforeaction', this, action) !== false){
7960 this.beforeAction(action);
7961 action.run.defer(100, action);
7967 beforeAction : function(action){
7968 var o = action.options;
7973 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7975 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7978 // not really supported yet.. ??
7980 //if(this.waitMsgTarget === true){
7981 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7982 //}else if(this.waitMsgTarget){
7983 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7984 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7986 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7992 afterAction : function(action, success){
7993 this.activeAction = null;
7994 var o = action.options;
7999 Roo.get(document.body).unmask();
8005 //if(this.waitMsgTarget === true){
8006 // this.el.unmask();
8007 //}else if(this.waitMsgTarget){
8008 // this.waitMsgTarget.unmask();
8010 // Roo.MessageBox.updateProgress(1);
8011 // Roo.MessageBox.hide();
8018 Roo.callback(o.success, o.scope, [this, action]);
8019 this.fireEvent('actioncomplete', this, action);
8023 // failure condition..
8024 // we have a scenario where updates need confirming.
8025 // eg. if a locking scenario exists..
8026 // we look for { errors : { needs_confirm : true }} in the response.
8028 (typeof(action.result) != 'undefined') &&
8029 (typeof(action.result.errors) != 'undefined') &&
8030 (typeof(action.result.errors.needs_confirm) != 'undefined')
8033 Roo.log("not supported yet");
8036 Roo.MessageBox.confirm(
8037 "Change requires confirmation",
8038 action.result.errorMsg,
8043 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8053 Roo.callback(o.failure, o.scope, [this, action]);
8054 // show an error message if no failed handler is set..
8055 if (!this.hasListener('actionfailed')) {
8056 Roo.log("need to add dialog support");
8058 Roo.MessageBox.alert("Error",
8059 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8060 action.result.errorMsg :
8061 "Saving Failed, please check your entries or try again"
8066 this.fireEvent('actionfailed', this, action);
8071 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8072 * @param {String} id The value to search for
8075 findField : function(id){
8076 var items = this.getItems();
8077 var field = items.get(id);
8079 items.each(function(f){
8080 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8087 return field || null;
8090 * Mark fields in this form invalid in bulk.
8091 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8092 * @return {BasicForm} this
8094 markInvalid : function(errors){
8095 if(errors instanceof Array){
8096 for(var i = 0, len = errors.length; i < len; i++){
8097 var fieldError = errors[i];
8098 var f = this.findField(fieldError.id);
8100 f.markInvalid(fieldError.msg);
8106 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8107 field.markInvalid(errors[id]);
8111 //Roo.each(this.childForms || [], function (f) {
8112 // f.markInvalid(errors);
8119 * Set values for fields in this form in bulk.
8120 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8121 * @return {BasicForm} this
8123 setValues : function(values){
8124 if(values instanceof Array){ // array of objects
8125 for(var i = 0, len = values.length; i < len; i++){
8127 var f = this.findField(v.id);
8129 f.setValue(v.value);
8130 if(this.trackResetOnLoad){
8131 f.originalValue = f.getValue();
8135 }else{ // object hash
8138 if(typeof values[id] != 'function' && (field = this.findField(id))){
8140 if (field.setFromData &&
8142 field.displayField &&
8143 // combos' with local stores can
8144 // be queried via setValue()
8145 // to set their value..
8146 (field.store && !field.store.isLocal)
8150 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8151 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8152 field.setFromData(sd);
8154 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8156 field.setFromData(values);
8159 field.setValue(values[id]);
8163 if(this.trackResetOnLoad){
8164 field.originalValue = field.getValue();
8170 //Roo.each(this.childForms || [], function (f) {
8171 // f.setValues(values);
8178 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8179 * they are returned as an array.
8180 * @param {Boolean} asString
8183 getValues : function(asString){
8184 //if (this.childForms) {
8185 // copy values from the child forms
8186 // Roo.each(this.childForms, function (f) {
8187 // this.setValues(f.getValues());
8193 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8194 if(asString === true){
8197 return Roo.urlDecode(fs);
8201 * Returns the fields in this form as an object with key/value pairs.
8202 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8205 getFieldValues : function(with_hidden)
8207 var items = this.getItems();
8209 items.each(function(f){
8215 var v = f.getValue();
8217 if (f.inputType =='radio') {
8218 if (typeof(ret[f.getName()]) == 'undefined') {
8219 ret[f.getName()] = ''; // empty..
8222 if (!f.el.dom.checked) {
8230 if(f.xtype == 'MoneyField'){
8231 ret[f.currencyName] = f.getCurrency();
8234 // not sure if this supported any more..
8235 if ((typeof(v) == 'object') && f.getRawValue) {
8236 v = f.getRawValue() ; // dates..
8238 // combo boxes where name != hiddenName...
8239 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8240 ret[f.name] = f.getRawValue();
8242 ret[f.getName()] = v;
8249 * Clears all invalid messages in this form.
8250 * @return {BasicForm} this
8252 clearInvalid : function(){
8253 var items = this.getItems();
8255 items.each(function(f){
8264 * @return {BasicForm} this
8267 var items = this.getItems();
8268 items.each(function(f){
8272 Roo.each(this.childForms || [], function (f) {
8280 getItems : function()
8282 var r=new Roo.util.MixedCollection(false, function(o){
8283 return o.id || (o.id = Roo.id());
8285 var iter = function(el) {
8292 Roo.each(el.items,function(e) {
8301 hideFields : function(items)
8303 Roo.each(items, function(i){
8305 var f = this.findField(i);
8316 showFields : function(items)
8318 Roo.each(items, function(i){
8320 var f = this.findField(i);
8333 Roo.apply(Roo.bootstrap.Form, {
8360 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8361 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8362 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8363 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8366 this.maskEl.top.enableDisplayMode("block");
8367 this.maskEl.left.enableDisplayMode("block");
8368 this.maskEl.bottom.enableDisplayMode("block");
8369 this.maskEl.right.enableDisplayMode("block");
8371 this.toolTip = new Roo.bootstrap.Tooltip({
8372 cls : 'roo-form-error-popover',
8374 'left' : ['r-l', [-2,0], 'right'],
8375 'right' : ['l-r', [2,0], 'left'],
8376 'bottom' : ['tl-bl', [0,2], 'top'],
8377 'top' : [ 'bl-tl', [0,-2], 'bottom']
8381 this.toolTip.render(Roo.get(document.body));
8383 this.toolTip.el.enableDisplayMode("block");
8385 Roo.get(document.body).on('click', function(){
8389 Roo.get(document.body).on('touchstart', function(){
8393 this.isApplied = true
8396 mask : function(form, target)
8400 this.target = target;
8402 if(!this.form.errorMask || !target.el){
8406 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8408 Roo.log(scrollable);
8410 var ot = this.target.el.calcOffsetsTo(scrollable);
8412 var scrollTo = ot[1] - this.form.maskOffset;
8414 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8416 scrollable.scrollTo('top', scrollTo);
8418 var box = this.target.el.getBox();
8420 var zIndex = Roo.bootstrap.Modal.zIndex++;
8423 this.maskEl.top.setStyle('position', 'absolute');
8424 this.maskEl.top.setStyle('z-index', zIndex);
8425 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8426 this.maskEl.top.setLeft(0);
8427 this.maskEl.top.setTop(0);
8428 this.maskEl.top.show();
8430 this.maskEl.left.setStyle('position', 'absolute');
8431 this.maskEl.left.setStyle('z-index', zIndex);
8432 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8433 this.maskEl.left.setLeft(0);
8434 this.maskEl.left.setTop(box.y - this.padding);
8435 this.maskEl.left.show();
8437 this.maskEl.bottom.setStyle('position', 'absolute');
8438 this.maskEl.bottom.setStyle('z-index', zIndex);
8439 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8440 this.maskEl.bottom.setLeft(0);
8441 this.maskEl.bottom.setTop(box.bottom + this.padding);
8442 this.maskEl.bottom.show();
8444 this.maskEl.right.setStyle('position', 'absolute');
8445 this.maskEl.right.setStyle('z-index', zIndex);
8446 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8447 this.maskEl.right.setLeft(box.right + this.padding);
8448 this.maskEl.right.setTop(box.y - this.padding);
8449 this.maskEl.right.show();
8451 this.toolTip.bindEl = this.target.el;
8453 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8455 var tip = this.target.blankText;
8457 if(this.target.getValue() !== '' ) {
8459 if (this.target.invalidText.length) {
8460 tip = this.target.invalidText;
8461 } else if (this.target.regexText.length){
8462 tip = this.target.regexText;
8466 this.toolTip.show(tip);
8468 this.intervalID = window.setInterval(function() {
8469 Roo.bootstrap.Form.popover.unmask();
8472 window.onwheel = function(){ return false;};
8474 (function(){ this.isMasked = true; }).defer(500, this);
8480 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8484 this.maskEl.top.setStyle('position', 'absolute');
8485 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8486 this.maskEl.top.hide();
8488 this.maskEl.left.setStyle('position', 'absolute');
8489 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8490 this.maskEl.left.hide();
8492 this.maskEl.bottom.setStyle('position', 'absolute');
8493 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8494 this.maskEl.bottom.hide();
8496 this.maskEl.right.setStyle('position', 'absolute');
8497 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8498 this.maskEl.right.hide();
8500 this.toolTip.hide();
8502 this.toolTip.el.hide();
8504 window.onwheel = function(){ return true;};
8506 if(this.intervalID){
8507 window.clearInterval(this.intervalID);
8508 this.intervalID = false;
8511 this.isMasked = false;
8521 * Ext JS Library 1.1.1
8522 * Copyright(c) 2006-2007, Ext JS, LLC.
8524 * Originally Released Under LGPL - original licence link has changed is not relivant.
8527 * <script type="text/javascript">
8530 * @class Roo.form.VTypes
8531 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8534 Roo.form.VTypes = function(){
8535 // closure these in so they are only created once.
8536 var alpha = /^[a-zA-Z_]+$/;
8537 var alphanum = /^[a-zA-Z0-9_]+$/;
8538 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8539 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8541 // All these messages and functions are configurable
8544 * The function used to validate email addresses
8545 * @param {String} value The email address
8547 'email' : function(v){
8548 return email.test(v);
8551 * The error text to display when the email validation function returns false
8554 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8556 * The keystroke filter mask to be applied on email input
8559 'emailMask' : /[a-z0-9_\.\-@]/i,
8562 * The function used to validate URLs
8563 * @param {String} value The URL
8565 'url' : function(v){
8569 * The error text to display when the url validation function returns false
8572 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8575 * The function used to validate alpha values
8576 * @param {String} value The value
8578 'alpha' : function(v){
8579 return alpha.test(v);
8582 * The error text to display when the alpha validation function returns false
8585 'alphaText' : 'This field should only contain letters and _',
8587 * The keystroke filter mask to be applied on alpha input
8590 'alphaMask' : /[a-z_]/i,
8593 * The function used to validate alphanumeric values
8594 * @param {String} value The value
8596 'alphanum' : function(v){
8597 return alphanum.test(v);
8600 * The error text to display when the alphanumeric validation function returns false
8603 'alphanumText' : 'This field should only contain letters, numbers and _',
8605 * The keystroke filter mask to be applied on alphanumeric input
8608 'alphanumMask' : /[a-z0-9_]/i
8618 * @class Roo.bootstrap.Input
8619 * @extends Roo.bootstrap.Component
8620 * Bootstrap Input class
8621 * @cfg {Boolean} disabled is it disabled
8622 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8623 * @cfg {String} name name of the input
8624 * @cfg {string} fieldLabel - the label associated
8625 * @cfg {string} placeholder - placeholder to put in text.
8626 * @cfg {string} before - input group add on before
8627 * @cfg {string} after - input group add on after
8628 * @cfg {string} size - (lg|sm) or leave empty..
8629 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8630 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8631 * @cfg {Number} md colspan out of 12 for computer-sized screens
8632 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8633 * @cfg {string} value default value of the input
8634 * @cfg {Number} labelWidth set the width of label
8635 * @cfg {Number} labellg set the width of label (1-12)
8636 * @cfg {Number} labelmd set the width of label (1-12)
8637 * @cfg {Number} labelsm set the width of label (1-12)
8638 * @cfg {Number} labelxs set the width of label (1-12)
8639 * @cfg {String} labelAlign (top|left)
8640 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8641 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8642 * @cfg {String} indicatorpos (left|right) default left
8643 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8644 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8646 * @cfg {String} align (left|center|right) Default left
8647 * @cfg {Boolean} forceFeedback (true|false) Default false
8650 * Create a new Input
8651 * @param {Object} config The config object
8654 Roo.bootstrap.Input = function(config){
8656 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8661 * Fires when this field receives input focus.
8662 * @param {Roo.form.Field} this
8667 * Fires when this field loses input focus.
8668 * @param {Roo.form.Field} this
8673 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8674 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8675 * @param {Roo.form.Field} this
8676 * @param {Roo.EventObject} e The event object
8681 * Fires just before the field blurs if the field value has changed.
8682 * @param {Roo.form.Field} this
8683 * @param {Mixed} newValue The new value
8684 * @param {Mixed} oldValue The original value
8689 * Fires after the field has been marked as invalid.
8690 * @param {Roo.form.Field} this
8691 * @param {String} msg The validation message
8696 * Fires after the field has been validated with no errors.
8697 * @param {Roo.form.Field} this
8702 * Fires after the key up
8703 * @param {Roo.form.Field} this
8704 * @param {Roo.EventObject} e The event Object
8710 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8712 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8713 automatic validation (defaults to "keyup").
8715 validationEvent : "keyup",
8717 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8719 validateOnBlur : true,
8721 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8723 validationDelay : 250,
8725 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8727 focusClass : "x-form-focus", // not needed???
8731 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8733 invalidClass : "has-warning",
8736 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8738 validClass : "has-success",
8741 * @cfg {Boolean} hasFeedback (true|false) default true
8746 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8748 invalidFeedbackClass : "glyphicon-warning-sign",
8751 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8753 validFeedbackClass : "glyphicon-ok",
8756 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8758 selectOnFocus : false,
8761 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8765 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8770 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8772 disableKeyFilter : false,
8775 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8779 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8783 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8785 blankText : "Please complete this mandatory field",
8788 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8792 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8794 maxLength : Number.MAX_VALUE,
8796 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8798 minLengthText : "The minimum length for this field is {0}",
8800 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8802 maxLengthText : "The maximum length for this field is {0}",
8806 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8807 * If available, this function will be called only after the basic validators all return true, and will be passed the
8808 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8812 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8813 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8814 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8818 * @cfg {String} regexText -- Depricated - use Invalid Text
8823 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8829 autocomplete: false,
8848 formatedValue : false,
8849 forceFeedback : false,
8851 indicatorpos : 'left',
8861 parentLabelAlign : function()
8864 while (parent.parent()) {
8865 parent = parent.parent();
8866 if (typeof(parent.labelAlign) !='undefined') {
8867 return parent.labelAlign;
8874 getAutoCreate : function()
8876 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8882 if(this.inputType != 'hidden'){
8883 cfg.cls = 'form-group' //input-group
8889 type : this.inputType,
8891 cls : 'form-control',
8892 placeholder : this.placeholder || '',
8893 autocomplete : this.autocomplete || 'new-password'
8896 if(this.capture.length){
8897 input.capture = this.capture;
8900 if(this.accept.length){
8901 input.accept = this.accept + "/*";
8905 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8908 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8909 input.maxLength = this.maxLength;
8912 if (this.disabled) {
8913 input.disabled=true;
8916 if (this.readOnly) {
8917 input.readonly=true;
8921 input.name = this.name;
8925 input.cls += ' input-' + this.size;
8929 ['xs','sm','md','lg'].map(function(size){
8930 if (settings[size]) {
8931 cfg.cls += ' col-' + size + '-' + settings[size];
8935 var inputblock = input;
8939 cls: 'glyphicon form-control-feedback'
8942 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8945 cls : 'has-feedback',
8953 if (this.before || this.after) {
8956 cls : 'input-group',
8960 if (this.before && typeof(this.before) == 'string') {
8962 inputblock.cn.push({
8964 cls : 'roo-input-before input-group-addon',
8968 if (this.before && typeof(this.before) == 'object') {
8969 this.before = Roo.factory(this.before);
8971 inputblock.cn.push({
8973 cls : 'roo-input-before input-group-' +
8974 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8978 inputblock.cn.push(input);
8980 if (this.after && typeof(this.after) == 'string') {
8981 inputblock.cn.push({
8983 cls : 'roo-input-after input-group-addon',
8987 if (this.after && typeof(this.after) == 'object') {
8988 this.after = Roo.factory(this.after);
8990 inputblock.cn.push({
8992 cls : 'roo-input-after input-group-' +
8993 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8997 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8998 inputblock.cls += ' has-feedback';
8999 inputblock.cn.push(feedback);
9003 if (align ==='left' && this.fieldLabel.length) {
9005 cfg.cls += ' roo-form-group-label-left';
9010 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9011 tooltip : 'This field is required'
9016 cls : 'control-label',
9017 html : this.fieldLabel
9028 var labelCfg = cfg.cn[1];
9029 var contentCfg = cfg.cn[2];
9031 if(this.indicatorpos == 'right'){
9036 cls : 'control-label',
9040 html : this.fieldLabel
9044 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9045 tooltip : 'This field is required'
9058 labelCfg = cfg.cn[0];
9059 contentCfg = cfg.cn[1];
9063 if(this.labelWidth > 12){
9064 labelCfg.style = "width: " + this.labelWidth + 'px';
9067 if(this.labelWidth < 13 && this.labelmd == 0){
9068 this.labelmd = this.labelWidth;
9071 if(this.labellg > 0){
9072 labelCfg.cls += ' col-lg-' + this.labellg;
9073 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9076 if(this.labelmd > 0){
9077 labelCfg.cls += ' col-md-' + this.labelmd;
9078 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9081 if(this.labelsm > 0){
9082 labelCfg.cls += ' col-sm-' + this.labelsm;
9083 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9086 if(this.labelxs > 0){
9087 labelCfg.cls += ' col-xs-' + this.labelxs;
9088 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9092 } else if ( this.fieldLabel.length) {
9097 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9098 tooltip : 'This field is required'
9102 //cls : 'input-group-addon',
9103 html : this.fieldLabel
9111 if(this.indicatorpos == 'right'){
9116 //cls : 'input-group-addon',
9117 html : this.fieldLabel
9122 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9123 tooltip : 'This field is required'
9143 if (this.parentType === 'Navbar' && this.parent().bar) {
9144 cfg.cls += ' navbar-form';
9147 if (this.parentType === 'NavGroup') {
9148 cfg.cls += ' navbar-form';
9156 * return the real input element.
9158 inputEl: function ()
9160 return this.el.select('input.form-control',true).first();
9163 tooltipEl : function()
9165 return this.inputEl();
9168 indicatorEl : function()
9170 var indicator = this.el.select('i.roo-required-indicator',true).first();
9180 setDisabled : function(v)
9182 var i = this.inputEl().dom;
9184 i.removeAttribute('disabled');
9188 i.setAttribute('disabled','true');
9190 initEvents : function()
9193 this.inputEl().on("keydown" , this.fireKey, this);
9194 this.inputEl().on("focus", this.onFocus, this);
9195 this.inputEl().on("blur", this.onBlur, this);
9197 this.inputEl().relayEvent('keyup', this);
9199 this.indicator = this.indicatorEl();
9202 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9205 // reference to original value for reset
9206 this.originalValue = this.getValue();
9207 //Roo.form.TextField.superclass.initEvents.call(this);
9208 if(this.validationEvent == 'keyup'){
9209 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9210 this.inputEl().on('keyup', this.filterValidation, this);
9212 else if(this.validationEvent !== false){
9213 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9216 if(this.selectOnFocus){
9217 this.on("focus", this.preFocus, this);
9220 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9221 this.inputEl().on("keypress", this.filterKeys, this);
9223 this.inputEl().relayEvent('keypress', this);
9226 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9227 this.el.on("click", this.autoSize, this);
9230 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9231 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9234 if (typeof(this.before) == 'object') {
9235 this.before.render(this.el.select('.roo-input-before',true).first());
9237 if (typeof(this.after) == 'object') {
9238 this.after.render(this.el.select('.roo-input-after',true).first());
9241 this.inputEl().on('change', this.onChange, this);
9244 filterValidation : function(e){
9245 if(!e.isNavKeyPress()){
9246 this.validationTask.delay(this.validationDelay);
9250 * Validates the field value
9251 * @return {Boolean} True if the value is valid, else false
9253 validate : function(){
9254 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9255 if(this.disabled || this.validateValue(this.getRawValue())){
9266 * Validates a value according to the field's validation rules and marks the field as invalid
9267 * if the validation fails
9268 * @param {Mixed} value The value to validate
9269 * @return {Boolean} True if the value is valid, else false
9271 validateValue : function(value)
9273 if(this.getVisibilityEl().hasClass('hidden')){
9277 if(value.length < 1) { // if it's blank
9278 if(this.allowBlank){
9284 if(value.length < this.minLength){
9287 if(value.length > this.maxLength){
9291 var vt = Roo.form.VTypes;
9292 if(!vt[this.vtype](value, this)){
9296 if(typeof this.validator == "function"){
9297 var msg = this.validator(value);
9301 if (typeof(msg) == 'string') {
9302 this.invalidText = msg;
9306 if(this.regex && !this.regex.test(value)){
9314 fireKey : function(e){
9315 //Roo.log('field ' + e.getKey());
9316 if(e.isNavKeyPress()){
9317 this.fireEvent("specialkey", this, e);
9320 focus : function (selectText){
9322 this.inputEl().focus();
9323 if(selectText === true){
9324 this.inputEl().dom.select();
9330 onFocus : function(){
9331 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9332 // this.el.addClass(this.focusClass);
9335 this.hasFocus = true;
9336 this.startValue = this.getValue();
9337 this.fireEvent("focus", this);
9341 beforeBlur : Roo.emptyFn,
9345 onBlur : function(){
9347 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9348 //this.el.removeClass(this.focusClass);
9350 this.hasFocus = false;
9351 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9354 var v = this.getValue();
9355 if(String(v) !== String(this.startValue)){
9356 this.fireEvent('change', this, v, this.startValue);
9358 this.fireEvent("blur", this);
9361 onChange : function(e)
9363 var v = this.getValue();
9364 if(String(v) !== String(this.startValue)){
9365 this.fireEvent('change', this, v, this.startValue);
9371 * Resets the current field value to the originally loaded value and clears any validation messages
9374 this.setValue(this.originalValue);
9378 * Returns the name of the field
9379 * @return {Mixed} name The name field
9381 getName: function(){
9385 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9386 * @return {Mixed} value The field value
9388 getValue : function(){
9390 var v = this.inputEl().getValue();
9395 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9396 * @return {Mixed} value The field value
9398 getRawValue : function(){
9399 var v = this.inputEl().getValue();
9405 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9406 * @param {Mixed} value The value to set
9408 setRawValue : function(v){
9409 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9412 selectText : function(start, end){
9413 var v = this.getRawValue();
9415 start = start === undefined ? 0 : start;
9416 end = end === undefined ? v.length : end;
9417 var d = this.inputEl().dom;
9418 if(d.setSelectionRange){
9419 d.setSelectionRange(start, end);
9420 }else if(d.createTextRange){
9421 var range = d.createTextRange();
9422 range.moveStart("character", start);
9423 range.moveEnd("character", v.length-end);
9430 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9431 * @param {Mixed} value The value to set
9433 setValue : function(v){
9436 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9442 processValue : function(value){
9443 if(this.stripCharsRe){
9444 var newValue = value.replace(this.stripCharsRe, '');
9445 if(newValue !== value){
9446 this.setRawValue(newValue);
9453 preFocus : function(){
9455 if(this.selectOnFocus){
9456 this.inputEl().dom.select();
9459 filterKeys : function(e){
9461 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9464 var c = e.getCharCode(), cc = String.fromCharCode(c);
9465 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9468 if(!this.maskRe.test(cc)){
9473 * Clear any invalid styles/messages for this field
9475 clearInvalid : function(){
9477 if(!this.el || this.preventMark){ // not rendered
9482 this.el.removeClass(this.invalidClass);
9484 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9486 var feedback = this.el.select('.form-control-feedback', true).first();
9489 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9495 this.indicator.removeClass('visible');
9496 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9499 this.fireEvent('valid', this);
9503 * Mark this field as valid
9505 markValid : function()
9507 if(!this.el || this.preventMark){ // not rendered...
9511 this.el.removeClass([this.invalidClass, this.validClass]);
9513 var feedback = this.el.select('.form-control-feedback', true).first();
9516 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9520 this.indicator.removeClass('visible');
9521 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9528 if(this.allowBlank && !this.getRawValue().length){
9532 this.el.addClass(this.validClass);
9534 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9536 var feedback = this.el.select('.form-control-feedback', true).first();
9539 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9540 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9545 this.fireEvent('valid', this);
9549 * Mark this field as invalid
9550 * @param {String} msg The validation message
9552 markInvalid : function(msg)
9554 if(!this.el || this.preventMark){ // not rendered
9558 this.el.removeClass([this.invalidClass, this.validClass]);
9560 var feedback = this.el.select('.form-control-feedback', true).first();
9563 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9570 if(this.allowBlank && !this.getRawValue().length){
9575 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9576 this.indicator.addClass('visible');
9579 this.el.addClass(this.invalidClass);
9581 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9583 var feedback = this.el.select('.form-control-feedback', true).first();
9586 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9588 if(this.getValue().length || this.forceFeedback){
9589 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9596 this.fireEvent('invalid', this, msg);
9599 SafariOnKeyDown : function(event)
9601 // this is a workaround for a password hang bug on chrome/ webkit.
9602 if (this.inputEl().dom.type != 'password') {
9606 var isSelectAll = false;
9608 if(this.inputEl().dom.selectionEnd > 0){
9609 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9611 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9612 event.preventDefault();
9617 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9619 event.preventDefault();
9620 // this is very hacky as keydown always get's upper case.
9622 var cc = String.fromCharCode(event.getCharCode());
9623 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9627 adjustWidth : function(tag, w){
9628 tag = tag.toLowerCase();
9629 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9630 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9634 if(tag == 'textarea'){
9637 }else if(Roo.isOpera){
9641 if(tag == 'textarea'){
9649 setFieldLabel : function(v)
9656 var ar = this.el.select('label > span',true);
9658 if (ar.elements.length) {
9659 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9660 this.fieldLabel = v;
9664 var br = this.el.select('label',true);
9666 if(br.elements.length) {
9667 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9668 this.fieldLabel = v;
9672 Roo.log('Cannot Found any of label > span || label in input');
9676 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9677 this.fieldLabel = v;
9692 * @class Roo.bootstrap.TextArea
9693 * @extends Roo.bootstrap.Input
9694 * Bootstrap TextArea class
9695 * @cfg {Number} cols Specifies the visible width of a text area
9696 * @cfg {Number} rows Specifies the visible number of lines in a text area
9697 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9698 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9699 * @cfg {string} html text
9702 * Create a new TextArea
9703 * @param {Object} config The config object
9706 Roo.bootstrap.TextArea = function(config){
9707 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9711 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9721 getAutoCreate : function(){
9723 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9729 if(this.inputType != 'hidden'){
9730 cfg.cls = 'form-group' //input-group
9738 value : this.value || '',
9739 html: this.html || '',
9740 cls : 'form-control',
9741 placeholder : this.placeholder || ''
9745 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9746 input.maxLength = this.maxLength;
9750 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9754 input.cols = this.cols;
9757 if (this.readOnly) {
9758 input.readonly = true;
9762 input.name = this.name;
9766 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9770 ['xs','sm','md','lg'].map(function(size){
9771 if (settings[size]) {
9772 cfg.cls += ' col-' + size + '-' + settings[size];
9776 var inputblock = input;
9778 if(this.hasFeedback && !this.allowBlank){
9782 cls: 'glyphicon form-control-feedback'
9786 cls : 'has-feedback',
9795 if (this.before || this.after) {
9798 cls : 'input-group',
9802 inputblock.cn.push({
9804 cls : 'input-group-addon',
9809 inputblock.cn.push(input);
9811 if(this.hasFeedback && !this.allowBlank){
9812 inputblock.cls += ' has-feedback';
9813 inputblock.cn.push(feedback);
9817 inputblock.cn.push({
9819 cls : 'input-group-addon',
9826 if (align ==='left' && this.fieldLabel.length) {
9831 cls : 'control-label',
9832 html : this.fieldLabel
9843 if(this.labelWidth > 12){
9844 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9847 if(this.labelWidth < 13 && this.labelmd == 0){
9848 this.labelmd = this.labelWidth;
9851 if(this.labellg > 0){
9852 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9853 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9856 if(this.labelmd > 0){
9857 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9858 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9861 if(this.labelsm > 0){
9862 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9863 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9866 if(this.labelxs > 0){
9867 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9868 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9871 } else if ( this.fieldLabel.length) {
9876 //cls : 'input-group-addon',
9877 html : this.fieldLabel
9895 if (this.disabled) {
9896 input.disabled=true;
9903 * return the real textarea element.
9905 inputEl: function ()
9907 return this.el.select('textarea.form-control',true).first();
9911 * Clear any invalid styles/messages for this field
9913 clearInvalid : function()
9916 if(!this.el || this.preventMark){ // not rendered
9920 var label = this.el.select('label', true).first();
9921 var icon = this.el.select('i.fa-star', true).first();
9927 this.el.removeClass(this.invalidClass);
9929 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9931 var feedback = this.el.select('.form-control-feedback', true).first();
9934 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9939 this.fireEvent('valid', this);
9943 * Mark this field as valid
9945 markValid : function()
9947 if(!this.el || this.preventMark){ // not rendered
9951 this.el.removeClass([this.invalidClass, this.validClass]);
9953 var feedback = this.el.select('.form-control-feedback', true).first();
9956 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9959 if(this.disabled || this.allowBlank){
9963 var label = this.el.select('label', true).first();
9964 var icon = this.el.select('i.fa-star', true).first();
9970 this.el.addClass(this.validClass);
9972 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9974 var feedback = this.el.select('.form-control-feedback', true).first();
9977 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9978 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9983 this.fireEvent('valid', this);
9987 * Mark this field as invalid
9988 * @param {String} msg The validation message
9990 markInvalid : function(msg)
9992 if(!this.el || this.preventMark){ // not rendered
9996 this.el.removeClass([this.invalidClass, this.validClass]);
9998 var feedback = this.el.select('.form-control-feedback', true).first();
10001 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10004 if(this.disabled || this.allowBlank){
10008 var label = this.el.select('label', true).first();
10009 var icon = this.el.select('i.fa-star', true).first();
10011 if(!this.getValue().length && label && !icon){
10012 this.el.createChild({
10014 cls : 'text-danger fa fa-lg fa-star',
10015 tooltip : 'This field is required',
10016 style : 'margin-right:5px;'
10020 this.el.addClass(this.invalidClass);
10022 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10024 var feedback = this.el.select('.form-control-feedback', true).first();
10027 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10029 if(this.getValue().length || this.forceFeedback){
10030 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10037 this.fireEvent('invalid', this, msg);
10045 * trigger field - base class for combo..
10050 * @class Roo.bootstrap.TriggerField
10051 * @extends Roo.bootstrap.Input
10052 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10053 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10054 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10055 * for which you can provide a custom implementation. For example:
10057 var trigger = new Roo.bootstrap.TriggerField();
10058 trigger.onTriggerClick = myTriggerFn;
10059 trigger.applyTo('my-field');
10062 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10063 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10064 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10065 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10066 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10069 * Create a new TriggerField.
10070 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10071 * to the base TextField)
10073 Roo.bootstrap.TriggerField = function(config){
10074 this.mimicing = false;
10075 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10078 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10080 * @cfg {String} triggerClass A CSS class to apply to the trigger
10083 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10088 * @cfg {Boolean} removable (true|false) special filter default false
10092 /** @cfg {Boolean} grow @hide */
10093 /** @cfg {Number} growMin @hide */
10094 /** @cfg {Number} growMax @hide */
10100 autoSize: Roo.emptyFn,
10104 deferHeight : true,
10107 actionMode : 'wrap',
10112 getAutoCreate : function(){
10114 var align = this.labelAlign || this.parentLabelAlign();
10119 cls: 'form-group' //input-group
10126 type : this.inputType,
10127 cls : 'form-control',
10128 autocomplete: 'new-password',
10129 placeholder : this.placeholder || ''
10133 input.name = this.name;
10136 input.cls += ' input-' + this.size;
10139 if (this.disabled) {
10140 input.disabled=true;
10143 var inputblock = input;
10145 if(this.hasFeedback && !this.allowBlank){
10149 cls: 'glyphicon form-control-feedback'
10152 if(this.removable && !this.editable && !this.tickable){
10154 cls : 'has-feedback',
10160 cls : 'roo-combo-removable-btn close'
10167 cls : 'has-feedback',
10176 if(this.removable && !this.editable && !this.tickable){
10178 cls : 'roo-removable',
10184 cls : 'roo-combo-removable-btn close'
10191 if (this.before || this.after) {
10194 cls : 'input-group',
10198 inputblock.cn.push({
10200 cls : 'input-group-addon',
10205 inputblock.cn.push(input);
10207 if(this.hasFeedback && !this.allowBlank){
10208 inputblock.cls += ' has-feedback';
10209 inputblock.cn.push(feedback);
10213 inputblock.cn.push({
10215 cls : 'input-group-addon',
10228 cls: 'form-hidden-field'
10242 cls: 'form-hidden-field'
10246 cls: 'roo-select2-choices',
10250 cls: 'roo-select2-search-field',
10263 cls: 'roo-select2-container input-group',
10268 // cls: 'typeahead typeahead-long dropdown-menu',
10269 // style: 'display:none'
10274 if(!this.multiple && this.showToggleBtn){
10280 if (this.caret != false) {
10283 cls: 'fa fa-' + this.caret
10290 cls : 'input-group-addon btn dropdown-toggle',
10295 cls: 'combobox-clear',
10309 combobox.cls += ' roo-select2-container-multi';
10312 if (align ==='left' && this.fieldLabel.length) {
10314 cfg.cls += ' roo-form-group-label-left';
10319 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10320 tooltip : 'This field is required'
10325 cls : 'control-label',
10326 html : this.fieldLabel
10338 var labelCfg = cfg.cn[1];
10339 var contentCfg = cfg.cn[2];
10341 if(this.indicatorpos == 'right'){
10346 cls : 'control-label',
10350 html : this.fieldLabel
10354 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10355 tooltip : 'This field is required'
10368 labelCfg = cfg.cn[0];
10369 contentCfg = cfg.cn[1];
10372 if(this.labelWidth > 12){
10373 labelCfg.style = "width: " + this.labelWidth + 'px';
10376 if(this.labelWidth < 13 && this.labelmd == 0){
10377 this.labelmd = this.labelWidth;
10380 if(this.labellg > 0){
10381 labelCfg.cls += ' col-lg-' + this.labellg;
10382 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10385 if(this.labelmd > 0){
10386 labelCfg.cls += ' col-md-' + this.labelmd;
10387 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10390 if(this.labelsm > 0){
10391 labelCfg.cls += ' col-sm-' + this.labelsm;
10392 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10395 if(this.labelxs > 0){
10396 labelCfg.cls += ' col-xs-' + this.labelxs;
10397 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10400 } else if ( this.fieldLabel.length) {
10401 // Roo.log(" label");
10405 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10406 tooltip : 'This field is required'
10410 //cls : 'input-group-addon',
10411 html : this.fieldLabel
10419 if(this.indicatorpos == 'right'){
10427 html : this.fieldLabel
10431 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10432 tooltip : 'This field is required'
10445 // Roo.log(" no label && no align");
10452 ['xs','sm','md','lg'].map(function(size){
10453 if (settings[size]) {
10454 cfg.cls += ' col-' + size + '-' + settings[size];
10465 onResize : function(w, h){
10466 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10467 // if(typeof w == 'number'){
10468 // var x = w - this.trigger.getWidth();
10469 // this.inputEl().setWidth(this.adjustWidth('input', x));
10470 // this.trigger.setStyle('left', x+'px');
10475 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10478 getResizeEl : function(){
10479 return this.inputEl();
10483 getPositionEl : function(){
10484 return this.inputEl();
10488 alignErrorIcon : function(){
10489 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10493 initEvents : function(){
10497 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10498 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10499 if(!this.multiple && this.showToggleBtn){
10500 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10501 if(this.hideTrigger){
10502 this.trigger.setDisplayed(false);
10504 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10508 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10511 if(this.removable && !this.editable && !this.tickable){
10512 var close = this.closeTriggerEl();
10515 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10516 close.on('click', this.removeBtnClick, this, close);
10520 //this.trigger.addClassOnOver('x-form-trigger-over');
10521 //this.trigger.addClassOnClick('x-form-trigger-click');
10524 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10528 closeTriggerEl : function()
10530 var close = this.el.select('.roo-combo-removable-btn', true).first();
10531 return close ? close : false;
10534 removeBtnClick : function(e, h, el)
10536 e.preventDefault();
10538 if(this.fireEvent("remove", this) !== false){
10540 this.fireEvent("afterremove", this)
10544 createList : function()
10546 this.list = Roo.get(document.body).createChild({
10548 cls: 'typeahead typeahead-long dropdown-menu',
10549 style: 'display:none'
10552 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10557 initTrigger : function(){
10562 onDestroy : function(){
10564 this.trigger.removeAllListeners();
10565 // this.trigger.remove();
10568 // this.wrap.remove();
10570 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10574 onFocus : function(){
10575 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10577 if(!this.mimicing){
10578 this.wrap.addClass('x-trigger-wrap-focus');
10579 this.mimicing = true;
10580 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10581 if(this.monitorTab){
10582 this.el.on("keydown", this.checkTab, this);
10589 checkTab : function(e){
10590 if(e.getKey() == e.TAB){
10591 this.triggerBlur();
10596 onBlur : function(){
10601 mimicBlur : function(e, t){
10603 if(!this.wrap.contains(t) && this.validateBlur()){
10604 this.triggerBlur();
10610 triggerBlur : function(){
10611 this.mimicing = false;
10612 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10613 if(this.monitorTab){
10614 this.el.un("keydown", this.checkTab, this);
10616 //this.wrap.removeClass('x-trigger-wrap-focus');
10617 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10621 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10622 validateBlur : function(e, t){
10627 onDisable : function(){
10628 this.inputEl().dom.disabled = true;
10629 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10631 // this.wrap.addClass('x-item-disabled');
10636 onEnable : function(){
10637 this.inputEl().dom.disabled = false;
10638 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10640 // this.el.removeClass('x-item-disabled');
10645 onShow : function(){
10646 var ae = this.getActionEl();
10649 ae.dom.style.display = '';
10650 ae.dom.style.visibility = 'visible';
10656 onHide : function(){
10657 var ae = this.getActionEl();
10658 ae.dom.style.display = 'none';
10662 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10663 * by an implementing function.
10665 * @param {EventObject} e
10667 onTriggerClick : Roo.emptyFn
10671 * Ext JS Library 1.1.1
10672 * Copyright(c) 2006-2007, Ext JS, LLC.
10674 * Originally Released Under LGPL - original licence link has changed is not relivant.
10677 * <script type="text/javascript">
10682 * @class Roo.data.SortTypes
10684 * Defines the default sorting (casting?) comparison functions used when sorting data.
10686 Roo.data.SortTypes = {
10688 * Default sort that does nothing
10689 * @param {Mixed} s The value being converted
10690 * @return {Mixed} The comparison value
10692 none : function(s){
10697 * The regular expression used to strip tags
10701 stripTagsRE : /<\/?[^>]+>/gi,
10704 * Strips all HTML tags to sort on text only
10705 * @param {Mixed} s The value being converted
10706 * @return {String} The comparison value
10708 asText : function(s){
10709 return String(s).replace(this.stripTagsRE, "");
10713 * Strips all HTML tags to sort on text only - Case insensitive
10714 * @param {Mixed} s The value being converted
10715 * @return {String} The comparison value
10717 asUCText : function(s){
10718 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10722 * Case insensitive string
10723 * @param {Mixed} s The value being converted
10724 * @return {String} The comparison value
10726 asUCString : function(s) {
10727 return String(s).toUpperCase();
10732 * @param {Mixed} s The value being converted
10733 * @return {Number} The comparison value
10735 asDate : function(s) {
10739 if(s instanceof Date){
10740 return s.getTime();
10742 return Date.parse(String(s));
10747 * @param {Mixed} s The value being converted
10748 * @return {Float} The comparison value
10750 asFloat : function(s) {
10751 var val = parseFloat(String(s).replace(/,/g, ""));
10760 * @param {Mixed} s The value being converted
10761 * @return {Number} The comparison value
10763 asInt : function(s) {
10764 var val = parseInt(String(s).replace(/,/g, ""));
10772 * Ext JS Library 1.1.1
10773 * Copyright(c) 2006-2007, Ext JS, LLC.
10775 * Originally Released Under LGPL - original licence link has changed is not relivant.
10778 * <script type="text/javascript">
10782 * @class Roo.data.Record
10783 * Instances of this class encapsulate both record <em>definition</em> information, and record
10784 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10785 * to access Records cached in an {@link Roo.data.Store} object.<br>
10787 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10788 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10791 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10793 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10794 * {@link #create}. The parameters are the same.
10795 * @param {Array} data An associative Array of data values keyed by the field name.
10796 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10797 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10798 * not specified an integer id is generated.
10800 Roo.data.Record = function(data, id){
10801 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10806 * Generate a constructor for a specific record layout.
10807 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10808 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10809 * Each field definition object may contain the following properties: <ul>
10810 * <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,
10811 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10812 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10813 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10814 * is being used, then this is a string containing the javascript expression to reference the data relative to
10815 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10816 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10817 * this may be omitted.</p></li>
10818 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10819 * <ul><li>auto (Default, implies no conversion)</li>
10824 * <li>date</li></ul></p></li>
10825 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10826 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10827 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10828 * by the Reader into an object that will be stored in the Record. It is passed the
10829 * following parameters:<ul>
10830 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10832 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10834 * <br>usage:<br><pre><code>
10835 var TopicRecord = Roo.data.Record.create(
10836 {name: 'title', mapping: 'topic_title'},
10837 {name: 'author', mapping: 'username'},
10838 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10839 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10840 {name: 'lastPoster', mapping: 'user2'},
10841 {name: 'excerpt', mapping: 'post_text'}
10844 var myNewRecord = new TopicRecord({
10845 title: 'Do my job please',
10848 lastPost: new Date(),
10849 lastPoster: 'Animal',
10850 excerpt: 'No way dude!'
10852 myStore.add(myNewRecord);
10857 Roo.data.Record.create = function(o){
10858 var f = function(){
10859 f.superclass.constructor.apply(this, arguments);
10861 Roo.extend(f, Roo.data.Record);
10862 var p = f.prototype;
10863 p.fields = new Roo.util.MixedCollection(false, function(field){
10866 for(var i = 0, len = o.length; i < len; i++){
10867 p.fields.add(new Roo.data.Field(o[i]));
10869 f.getField = function(name){
10870 return p.fields.get(name);
10875 Roo.data.Record.AUTO_ID = 1000;
10876 Roo.data.Record.EDIT = 'edit';
10877 Roo.data.Record.REJECT = 'reject';
10878 Roo.data.Record.COMMIT = 'commit';
10880 Roo.data.Record.prototype = {
10882 * Readonly flag - true if this record has been modified.
10891 join : function(store){
10892 this.store = store;
10896 * Set the named field to the specified value.
10897 * @param {String} name The name of the field to set.
10898 * @param {Object} value The value to set the field to.
10900 set : function(name, value){
10901 if(this.data[name] == value){
10905 if(!this.modified){
10906 this.modified = {};
10908 if(typeof this.modified[name] == 'undefined'){
10909 this.modified[name] = this.data[name];
10911 this.data[name] = value;
10912 if(!this.editing && this.store){
10913 this.store.afterEdit(this);
10918 * Get the value of the named field.
10919 * @param {String} name The name of the field to get the value of.
10920 * @return {Object} The value of the field.
10922 get : function(name){
10923 return this.data[name];
10927 beginEdit : function(){
10928 this.editing = true;
10929 this.modified = {};
10933 cancelEdit : function(){
10934 this.editing = false;
10935 delete this.modified;
10939 endEdit : function(){
10940 this.editing = false;
10941 if(this.dirty && this.store){
10942 this.store.afterEdit(this);
10947 * Usually called by the {@link Roo.data.Store} which owns the Record.
10948 * Rejects all changes made to the Record since either creation, or the last commit operation.
10949 * Modified fields are reverted to their original values.
10951 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10952 * of reject operations.
10954 reject : function(){
10955 var m = this.modified;
10957 if(typeof m[n] != "function"){
10958 this.data[n] = m[n];
10961 this.dirty = false;
10962 delete this.modified;
10963 this.editing = false;
10965 this.store.afterReject(this);
10970 * Usually called by the {@link Roo.data.Store} which owns the Record.
10971 * Commits all changes made to the Record since either creation, or the last commit operation.
10973 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10974 * of commit operations.
10976 commit : function(){
10977 this.dirty = false;
10978 delete this.modified;
10979 this.editing = false;
10981 this.store.afterCommit(this);
10986 hasError : function(){
10987 return this.error != null;
10991 clearError : function(){
10996 * Creates a copy of this record.
10997 * @param {String} id (optional) A new record id if you don't want to use this record's id
11000 copy : function(newId) {
11001 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11005 * Ext JS Library 1.1.1
11006 * Copyright(c) 2006-2007, Ext JS, LLC.
11008 * Originally Released Under LGPL - original licence link has changed is not relivant.
11011 * <script type="text/javascript">
11017 * @class Roo.data.Store
11018 * @extends Roo.util.Observable
11019 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11020 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11022 * 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
11023 * has no knowledge of the format of the data returned by the Proxy.<br>
11025 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11026 * instances from the data object. These records are cached and made available through accessor functions.
11028 * Creates a new Store.
11029 * @param {Object} config A config object containing the objects needed for the Store to access data,
11030 * and read the data into Records.
11032 Roo.data.Store = function(config){
11033 this.data = new Roo.util.MixedCollection(false);
11034 this.data.getKey = function(o){
11037 this.baseParams = {};
11039 this.paramNames = {
11044 "multisort" : "_multisort"
11047 if(config && config.data){
11048 this.inlineData = config.data;
11049 delete config.data;
11052 Roo.apply(this, config);
11054 if(this.reader){ // reader passed
11055 this.reader = Roo.factory(this.reader, Roo.data);
11056 this.reader.xmodule = this.xmodule || false;
11057 if(!this.recordType){
11058 this.recordType = this.reader.recordType;
11060 if(this.reader.onMetaChange){
11061 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11065 if(this.recordType){
11066 this.fields = this.recordType.prototype.fields;
11068 this.modified = [];
11072 * @event datachanged
11073 * Fires when the data cache has changed, and a widget which is using this Store
11074 * as a Record cache should refresh its view.
11075 * @param {Store} this
11077 datachanged : true,
11079 * @event metachange
11080 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11081 * @param {Store} this
11082 * @param {Object} meta The JSON metadata
11087 * Fires when Records have been added to the Store
11088 * @param {Store} this
11089 * @param {Roo.data.Record[]} records The array of Records added
11090 * @param {Number} index The index at which the record(s) were added
11095 * Fires when a Record has been removed from the Store
11096 * @param {Store} this
11097 * @param {Roo.data.Record} record The Record that was removed
11098 * @param {Number} index The index at which the record was removed
11103 * Fires when a Record has been updated
11104 * @param {Store} this
11105 * @param {Roo.data.Record} record The Record that was updated
11106 * @param {String} operation The update operation being performed. Value may be one of:
11108 Roo.data.Record.EDIT
11109 Roo.data.Record.REJECT
11110 Roo.data.Record.COMMIT
11116 * Fires when the data cache has been cleared.
11117 * @param {Store} this
11121 * @event beforeload
11122 * Fires before a request is made for a new data object. If the beforeload handler returns false
11123 * the load action will be canceled.
11124 * @param {Store} this
11125 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11129 * @event beforeloadadd
11130 * Fires after a new set of Records has been loaded.
11131 * @param {Store} this
11132 * @param {Roo.data.Record[]} records The Records that were loaded
11133 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11135 beforeloadadd : true,
11138 * Fires after a new set of Records has been loaded, before they are added to the store.
11139 * @param {Store} this
11140 * @param {Roo.data.Record[]} records The Records that were loaded
11141 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11142 * @params {Object} return from reader
11146 * @event loadexception
11147 * Fires if an exception occurs in the Proxy during loading.
11148 * Called with the signature of the Proxy's "loadexception" event.
11149 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11152 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11153 * @param {Object} load options
11154 * @param {Object} jsonData from your request (normally this contains the Exception)
11156 loadexception : true
11160 this.proxy = Roo.factory(this.proxy, Roo.data);
11161 this.proxy.xmodule = this.xmodule || false;
11162 this.relayEvents(this.proxy, ["loadexception"]);
11164 this.sortToggle = {};
11165 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11167 Roo.data.Store.superclass.constructor.call(this);
11169 if(this.inlineData){
11170 this.loadData(this.inlineData);
11171 delete this.inlineData;
11175 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11177 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11178 * without a remote query - used by combo/forms at present.
11182 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11185 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11188 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11189 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11192 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11193 * on any HTTP request
11196 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11199 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11203 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11204 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11206 remoteSort : false,
11209 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11210 * loaded or when a record is removed. (defaults to false).
11212 pruneModifiedRecords : false,
11215 lastOptions : null,
11218 * Add Records to the Store and fires the add event.
11219 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11221 add : function(records){
11222 records = [].concat(records);
11223 for(var i = 0, len = records.length; i < len; i++){
11224 records[i].join(this);
11226 var index = this.data.length;
11227 this.data.addAll(records);
11228 this.fireEvent("add", this, records, index);
11232 * Remove a Record from the Store and fires the remove event.
11233 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11235 remove : function(record){
11236 var index = this.data.indexOf(record);
11237 this.data.removeAt(index);
11239 if(this.pruneModifiedRecords){
11240 this.modified.remove(record);
11242 this.fireEvent("remove", this, record, index);
11246 * Remove all Records from the Store and fires the clear event.
11248 removeAll : function(){
11250 if(this.pruneModifiedRecords){
11251 this.modified = [];
11253 this.fireEvent("clear", this);
11257 * Inserts Records to the Store at the given index and fires the add event.
11258 * @param {Number} index The start index at which to insert the passed Records.
11259 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11261 insert : function(index, records){
11262 records = [].concat(records);
11263 for(var i = 0, len = records.length; i < len; i++){
11264 this.data.insert(index, records[i]);
11265 records[i].join(this);
11267 this.fireEvent("add", this, records, index);
11271 * Get the index within the cache of the passed Record.
11272 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11273 * @return {Number} The index of the passed Record. Returns -1 if not found.
11275 indexOf : function(record){
11276 return this.data.indexOf(record);
11280 * Get the index within the cache of the Record with the passed id.
11281 * @param {String} id The id of the Record to find.
11282 * @return {Number} The index of the Record. Returns -1 if not found.
11284 indexOfId : function(id){
11285 return this.data.indexOfKey(id);
11289 * Get the Record with the specified id.
11290 * @param {String} id The id of the Record to find.
11291 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11293 getById : function(id){
11294 return this.data.key(id);
11298 * Get the Record at the specified index.
11299 * @param {Number} index The index of the Record to find.
11300 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11302 getAt : function(index){
11303 return this.data.itemAt(index);
11307 * Returns a range of Records between specified indices.
11308 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11309 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11310 * @return {Roo.data.Record[]} An array of Records
11312 getRange : function(start, end){
11313 return this.data.getRange(start, end);
11317 storeOptions : function(o){
11318 o = Roo.apply({}, o);
11321 this.lastOptions = o;
11325 * Loads the Record cache from the configured Proxy using the configured Reader.
11327 * If using remote paging, then the first load call must specify the <em>start</em>
11328 * and <em>limit</em> properties in the options.params property to establish the initial
11329 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11331 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11332 * and this call will return before the new data has been loaded. Perform any post-processing
11333 * in a callback function, or in a "load" event handler.</strong>
11335 * @param {Object} options An object containing properties which control loading options:<ul>
11336 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11337 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11338 * passed the following arguments:<ul>
11339 * <li>r : Roo.data.Record[]</li>
11340 * <li>options: Options object from the load call</li>
11341 * <li>success: Boolean success indicator</li></ul></li>
11342 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11343 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11346 load : function(options){
11347 options = options || {};
11348 if(this.fireEvent("beforeload", this, options) !== false){
11349 this.storeOptions(options);
11350 var p = Roo.apply(options.params || {}, this.baseParams);
11351 // if meta was not loaded from remote source.. try requesting it.
11352 if (!this.reader.metaFromRemote) {
11353 p._requestMeta = 1;
11355 if(this.sortInfo && this.remoteSort){
11356 var pn = this.paramNames;
11357 p[pn["sort"]] = this.sortInfo.field;
11358 p[pn["dir"]] = this.sortInfo.direction;
11360 if (this.multiSort) {
11361 var pn = this.paramNames;
11362 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11365 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11370 * Reloads the Record cache from the configured Proxy using the configured Reader and
11371 * the options from the last load operation performed.
11372 * @param {Object} options (optional) An object containing properties which may override the options
11373 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11374 * the most recently used options are reused).
11376 reload : function(options){
11377 this.load(Roo.applyIf(options||{}, this.lastOptions));
11381 // Called as a callback by the Reader during a load operation.
11382 loadRecords : function(o, options, success){
11383 if(!o || success === false){
11384 if(success !== false){
11385 this.fireEvent("load", this, [], options, o);
11387 if(options.callback){
11388 options.callback.call(options.scope || this, [], options, false);
11392 // if data returned failure - throw an exception.
11393 if (o.success === false) {
11394 // show a message if no listener is registered.
11395 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11396 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11398 // loadmask wil be hooked into this..
11399 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11402 var r = o.records, t = o.totalRecords || r.length;
11404 this.fireEvent("beforeloadadd", this, r, options, o);
11406 if(!options || options.add !== true){
11407 if(this.pruneModifiedRecords){
11408 this.modified = [];
11410 for(var i = 0, len = r.length; i < len; i++){
11414 this.data = this.snapshot;
11415 delete this.snapshot;
11418 this.data.addAll(r);
11419 this.totalLength = t;
11421 this.fireEvent("datachanged", this);
11423 this.totalLength = Math.max(t, this.data.length+r.length);
11427 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11429 var e = new Roo.data.Record({});
11431 e.set(this.parent.displayField, this.parent.emptyTitle);
11432 e.set(this.parent.valueField, '');
11437 this.fireEvent("load", this, r, options, o);
11438 if(options.callback){
11439 options.callback.call(options.scope || this, r, options, true);
11445 * Loads data from a passed data block. A Reader which understands the format of the data
11446 * must have been configured in the constructor.
11447 * @param {Object} data The data block from which to read the Records. The format of the data expected
11448 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11449 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11451 loadData : function(o, append){
11452 var r = this.reader.readRecords(o);
11453 this.loadRecords(r, {add: append}, true);
11457 * Gets the number of cached records.
11459 * <em>If using paging, this may not be the total size of the dataset. If the data object
11460 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11461 * the data set size</em>
11463 getCount : function(){
11464 return this.data.length || 0;
11468 * Gets the total number of records in the dataset as returned by the server.
11470 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11471 * the dataset size</em>
11473 getTotalCount : function(){
11474 return this.totalLength || 0;
11478 * Returns the sort state of the Store as an object with two properties:
11480 field {String} The name of the field by which the Records are sorted
11481 direction {String} The sort order, "ASC" or "DESC"
11484 getSortState : function(){
11485 return this.sortInfo;
11489 applySort : function(){
11490 if(this.sortInfo && !this.remoteSort){
11491 var s = this.sortInfo, f = s.field;
11492 var st = this.fields.get(f).sortType;
11493 var fn = function(r1, r2){
11494 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11495 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11497 this.data.sort(s.direction, fn);
11498 if(this.snapshot && this.snapshot != this.data){
11499 this.snapshot.sort(s.direction, fn);
11505 * Sets the default sort column and order to be used by the next load operation.
11506 * @param {String} fieldName The name of the field to sort by.
11507 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11509 setDefaultSort : function(field, dir){
11510 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11514 * Sort the Records.
11515 * If remote sorting is used, the sort is performed on the server, and the cache is
11516 * reloaded. If local sorting is used, the cache is sorted internally.
11517 * @param {String} fieldName The name of the field to sort by.
11518 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11520 sort : function(fieldName, dir){
11521 var f = this.fields.get(fieldName);
11523 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11525 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11526 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11531 this.sortToggle[f.name] = dir;
11532 this.sortInfo = {field: f.name, direction: dir};
11533 if(!this.remoteSort){
11535 this.fireEvent("datachanged", this);
11537 this.load(this.lastOptions);
11542 * Calls the specified function for each of the Records in the cache.
11543 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11544 * Returning <em>false</em> aborts and exits the iteration.
11545 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11547 each : function(fn, scope){
11548 this.data.each(fn, scope);
11552 * Gets all records modified since the last commit. Modified records are persisted across load operations
11553 * (e.g., during paging).
11554 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11556 getModifiedRecords : function(){
11557 return this.modified;
11561 createFilterFn : function(property, value, anyMatch){
11562 if(!value.exec){ // not a regex
11563 value = String(value);
11564 if(value.length == 0){
11567 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11569 return function(r){
11570 return value.test(r.data[property]);
11575 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11576 * @param {String} property A field on your records
11577 * @param {Number} start The record index to start at (defaults to 0)
11578 * @param {Number} end The last record index to include (defaults to length - 1)
11579 * @return {Number} The sum
11581 sum : function(property, start, end){
11582 var rs = this.data.items, v = 0;
11583 start = start || 0;
11584 end = (end || end === 0) ? end : rs.length-1;
11586 for(var i = start; i <= end; i++){
11587 v += (rs[i].data[property] || 0);
11593 * Filter the records by a specified property.
11594 * @param {String} field A field on your records
11595 * @param {String/RegExp} value Either a string that the field
11596 * should start with or a RegExp to test against the field
11597 * @param {Boolean} anyMatch True to match any part not just the beginning
11599 filter : function(property, value, anyMatch){
11600 var fn = this.createFilterFn(property, value, anyMatch);
11601 return fn ? this.filterBy(fn) : this.clearFilter();
11605 * Filter by a function. The specified function will be called with each
11606 * record in this data source. If the function returns true the record is included,
11607 * otherwise it is filtered.
11608 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11609 * @param {Object} scope (optional) The scope of the function (defaults to this)
11611 filterBy : function(fn, scope){
11612 this.snapshot = this.snapshot || this.data;
11613 this.data = this.queryBy(fn, scope||this);
11614 this.fireEvent("datachanged", this);
11618 * Query the records by a specified property.
11619 * @param {String} field A field on your records
11620 * @param {String/RegExp} value Either a string that the field
11621 * should start with or a RegExp to test against the field
11622 * @param {Boolean} anyMatch True to match any part not just the beginning
11623 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11625 query : function(property, value, anyMatch){
11626 var fn = this.createFilterFn(property, value, anyMatch);
11627 return fn ? this.queryBy(fn) : this.data.clone();
11631 * Query by a function. The specified function will be called with each
11632 * record in this data source. If the function returns true the record is included
11634 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11635 * @param {Object} scope (optional) The scope of the function (defaults to this)
11636 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11638 queryBy : function(fn, scope){
11639 var data = this.snapshot || this.data;
11640 return data.filterBy(fn, scope||this);
11644 * Collects unique values for a particular dataIndex from this store.
11645 * @param {String} dataIndex The property to collect
11646 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11647 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11648 * @return {Array} An array of the unique values
11650 collect : function(dataIndex, allowNull, bypassFilter){
11651 var d = (bypassFilter === true && this.snapshot) ?
11652 this.snapshot.items : this.data.items;
11653 var v, sv, r = [], l = {};
11654 for(var i = 0, len = d.length; i < len; i++){
11655 v = d[i].data[dataIndex];
11657 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11666 * Revert to a view of the Record cache with no filtering applied.
11667 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11669 clearFilter : function(suppressEvent){
11670 if(this.snapshot && this.snapshot != this.data){
11671 this.data = this.snapshot;
11672 delete this.snapshot;
11673 if(suppressEvent !== true){
11674 this.fireEvent("datachanged", this);
11680 afterEdit : function(record){
11681 if(this.modified.indexOf(record) == -1){
11682 this.modified.push(record);
11684 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11688 afterReject : function(record){
11689 this.modified.remove(record);
11690 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11694 afterCommit : function(record){
11695 this.modified.remove(record);
11696 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11700 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11701 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11703 commitChanges : function(){
11704 var m = this.modified.slice(0);
11705 this.modified = [];
11706 for(var i = 0, len = m.length; i < len; i++){
11712 * Cancel outstanding changes on all changed records.
11714 rejectChanges : function(){
11715 var m = this.modified.slice(0);
11716 this.modified = [];
11717 for(var i = 0, len = m.length; i < len; i++){
11722 onMetaChange : function(meta, rtype, o){
11723 this.recordType = rtype;
11724 this.fields = rtype.prototype.fields;
11725 delete this.snapshot;
11726 this.sortInfo = meta.sortInfo || this.sortInfo;
11727 this.modified = [];
11728 this.fireEvent('metachange', this, this.reader.meta);
11731 moveIndex : function(data, type)
11733 var index = this.indexOf(data);
11735 var newIndex = index + type;
11739 this.insert(newIndex, data);
11744 * Ext JS Library 1.1.1
11745 * Copyright(c) 2006-2007, Ext JS, LLC.
11747 * Originally Released Under LGPL - original licence link has changed is not relivant.
11750 * <script type="text/javascript">
11754 * @class Roo.data.SimpleStore
11755 * @extends Roo.data.Store
11756 * Small helper class to make creating Stores from Array data easier.
11757 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11758 * @cfg {Array} fields An array of field definition objects, or field name strings.
11759 * @cfg {Array} data The multi-dimensional array of data
11761 * @param {Object} config
11763 Roo.data.SimpleStore = function(config){
11764 Roo.data.SimpleStore.superclass.constructor.call(this, {
11766 reader: new Roo.data.ArrayReader({
11769 Roo.data.Record.create(config.fields)
11771 proxy : new Roo.data.MemoryProxy(config.data)
11775 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11777 * Ext JS Library 1.1.1
11778 * Copyright(c) 2006-2007, Ext JS, LLC.
11780 * Originally Released Under LGPL - original licence link has changed is not relivant.
11783 * <script type="text/javascript">
11788 * @extends Roo.data.Store
11789 * @class Roo.data.JsonStore
11790 * Small helper class to make creating Stores for JSON data easier. <br/>
11792 var store = new Roo.data.JsonStore({
11793 url: 'get-images.php',
11795 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11798 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11799 * JsonReader and HttpProxy (unless inline data is provided).</b>
11800 * @cfg {Array} fields An array of field definition objects, or field name strings.
11802 * @param {Object} config
11804 Roo.data.JsonStore = function(c){
11805 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11806 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11807 reader: new Roo.data.JsonReader(c, c.fields)
11810 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11812 * Ext JS Library 1.1.1
11813 * Copyright(c) 2006-2007, Ext JS, LLC.
11815 * Originally Released Under LGPL - original licence link has changed is not relivant.
11818 * <script type="text/javascript">
11822 Roo.data.Field = function(config){
11823 if(typeof config == "string"){
11824 config = {name: config};
11826 Roo.apply(this, config);
11829 this.type = "auto";
11832 var st = Roo.data.SortTypes;
11833 // named sortTypes are supported, here we look them up
11834 if(typeof this.sortType == "string"){
11835 this.sortType = st[this.sortType];
11838 // set default sortType for strings and dates
11839 if(!this.sortType){
11842 this.sortType = st.asUCString;
11845 this.sortType = st.asDate;
11848 this.sortType = st.none;
11853 var stripRe = /[\$,%]/g;
11855 // prebuilt conversion function for this field, instead of
11856 // switching every time we're reading a value
11858 var cv, dateFormat = this.dateFormat;
11863 cv = function(v){ return v; };
11866 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11870 return v !== undefined && v !== null && v !== '' ?
11871 parseInt(String(v).replace(stripRe, ""), 10) : '';
11876 return v !== undefined && v !== null && v !== '' ?
11877 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11882 cv = function(v){ return v === true || v === "true" || v == 1; };
11889 if(v instanceof Date){
11893 if(dateFormat == "timestamp"){
11894 return new Date(v*1000);
11896 return Date.parseDate(v, dateFormat);
11898 var parsed = Date.parse(v);
11899 return parsed ? new Date(parsed) : null;
11908 Roo.data.Field.prototype = {
11916 * Ext JS Library 1.1.1
11917 * Copyright(c) 2006-2007, Ext JS, LLC.
11919 * Originally Released Under LGPL - original licence link has changed is not relivant.
11922 * <script type="text/javascript">
11925 // Base class for reading structured data from a data source. This class is intended to be
11926 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11929 * @class Roo.data.DataReader
11930 * Base class for reading structured data from a data source. This class is intended to be
11931 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11934 Roo.data.DataReader = function(meta, recordType){
11938 this.recordType = recordType instanceof Array ?
11939 Roo.data.Record.create(recordType) : recordType;
11942 Roo.data.DataReader.prototype = {
11944 * Create an empty record
11945 * @param {Object} data (optional) - overlay some values
11946 * @return {Roo.data.Record} record created.
11948 newRow : function(d) {
11950 this.recordType.prototype.fields.each(function(c) {
11952 case 'int' : da[c.name] = 0; break;
11953 case 'date' : da[c.name] = new Date(); break;
11954 case 'float' : da[c.name] = 0.0; break;
11955 case 'boolean' : da[c.name] = false; break;
11956 default : da[c.name] = ""; break;
11960 return new this.recordType(Roo.apply(da, d));
11965 * Ext JS Library 1.1.1
11966 * Copyright(c) 2006-2007, Ext JS, LLC.
11968 * Originally Released Under LGPL - original licence link has changed is not relivant.
11971 * <script type="text/javascript">
11975 * @class Roo.data.DataProxy
11976 * @extends Roo.data.Observable
11977 * This class is an abstract base class for implementations which provide retrieval of
11978 * unformatted data objects.<br>
11980 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11981 * (of the appropriate type which knows how to parse the data object) to provide a block of
11982 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11984 * Custom implementations must implement the load method as described in
11985 * {@link Roo.data.HttpProxy#load}.
11987 Roo.data.DataProxy = function(){
11990 * @event beforeload
11991 * Fires before a network request is made to retrieve a data object.
11992 * @param {Object} This DataProxy object.
11993 * @param {Object} params The params parameter to the load function.
11998 * Fires before the load method's callback is called.
11999 * @param {Object} This DataProxy object.
12000 * @param {Object} o The data object.
12001 * @param {Object} arg The callback argument object passed to the load function.
12005 * @event loadexception
12006 * Fires if an Exception occurs during data retrieval.
12007 * @param {Object} This DataProxy object.
12008 * @param {Object} o The data object.
12009 * @param {Object} arg The callback argument object passed to the load function.
12010 * @param {Object} e The Exception.
12012 loadexception : true
12014 Roo.data.DataProxy.superclass.constructor.call(this);
12017 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12020 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12024 * Ext JS Library 1.1.1
12025 * Copyright(c) 2006-2007, Ext JS, LLC.
12027 * Originally Released Under LGPL - original licence link has changed is not relivant.
12030 * <script type="text/javascript">
12033 * @class Roo.data.MemoryProxy
12034 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12035 * to the Reader when its load method is called.
12037 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12039 Roo.data.MemoryProxy = function(data){
12043 Roo.data.MemoryProxy.superclass.constructor.call(this);
12047 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12050 * Load data from the requested source (in this case an in-memory
12051 * data object passed to the constructor), read the data object into
12052 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12053 * process that block using the passed callback.
12054 * @param {Object} params This parameter is not used by the MemoryProxy class.
12055 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12056 * object into a block of Roo.data.Records.
12057 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12058 * The function must be passed <ul>
12059 * <li>The Record block object</li>
12060 * <li>The "arg" argument from the load function</li>
12061 * <li>A boolean success indicator</li>
12063 * @param {Object} scope The scope in which to call the callback
12064 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12066 load : function(params, reader, callback, scope, arg){
12067 params = params || {};
12070 result = reader.readRecords(this.data);
12072 this.fireEvent("loadexception", this, arg, null, e);
12073 callback.call(scope, null, arg, false);
12076 callback.call(scope, result, arg, true);
12080 update : function(params, records){
12085 * Ext JS Library 1.1.1
12086 * Copyright(c) 2006-2007, Ext JS, LLC.
12088 * Originally Released Under LGPL - original licence link has changed is not relivant.
12091 * <script type="text/javascript">
12094 * @class Roo.data.HttpProxy
12095 * @extends Roo.data.DataProxy
12096 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12097 * configured to reference a certain URL.<br><br>
12099 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12100 * from which the running page was served.<br><br>
12102 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12104 * Be aware that to enable the browser to parse an XML document, the server must set
12105 * the Content-Type header in the HTTP response to "text/xml".
12107 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12108 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12109 * will be used to make the request.
12111 Roo.data.HttpProxy = function(conn){
12112 Roo.data.HttpProxy.superclass.constructor.call(this);
12113 // is conn a conn config or a real conn?
12115 this.useAjax = !conn || !conn.events;
12119 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12120 // thse are take from connection...
12123 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12126 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12127 * extra parameters to each request made by this object. (defaults to undefined)
12130 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12131 * to each request made by this object. (defaults to undefined)
12134 * @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)
12137 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12140 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12146 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12150 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12151 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12152 * a finer-grained basis than the DataProxy events.
12154 getConnection : function(){
12155 return this.useAjax ? Roo.Ajax : this.conn;
12159 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12160 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12161 * process that block using the passed callback.
12162 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12163 * for the request to the remote server.
12164 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12165 * object into a block of Roo.data.Records.
12166 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12167 * The function must be passed <ul>
12168 * <li>The Record block object</li>
12169 * <li>The "arg" argument from the load function</li>
12170 * <li>A boolean success indicator</li>
12172 * @param {Object} scope The scope in which to call the callback
12173 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12175 load : function(params, reader, callback, scope, arg){
12176 if(this.fireEvent("beforeload", this, params) !== false){
12178 params : params || {},
12180 callback : callback,
12185 callback : this.loadResponse,
12189 Roo.applyIf(o, this.conn);
12190 if(this.activeRequest){
12191 Roo.Ajax.abort(this.activeRequest);
12193 this.activeRequest = Roo.Ajax.request(o);
12195 this.conn.request(o);
12198 callback.call(scope||this, null, arg, false);
12203 loadResponse : function(o, success, response){
12204 delete this.activeRequest;
12206 this.fireEvent("loadexception", this, o, response);
12207 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12212 result = o.reader.read(response);
12214 this.fireEvent("loadexception", this, o, response, e);
12215 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12219 this.fireEvent("load", this, o, o.request.arg);
12220 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12224 update : function(dataSet){
12229 updateResponse : function(dataSet){
12234 * Ext JS Library 1.1.1
12235 * Copyright(c) 2006-2007, Ext JS, LLC.
12237 * Originally Released Under LGPL - original licence link has changed is not relivant.
12240 * <script type="text/javascript">
12244 * @class Roo.data.ScriptTagProxy
12245 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12246 * other than the originating domain of the running page.<br><br>
12248 * <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
12249 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12251 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12252 * source code that is used as the source inside a <script> tag.<br><br>
12254 * In order for the browser to process the returned data, the server must wrap the data object
12255 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12256 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12257 * depending on whether the callback name was passed:
12260 boolean scriptTag = false;
12261 String cb = request.getParameter("callback");
12264 response.setContentType("text/javascript");
12266 response.setContentType("application/x-json");
12268 Writer out = response.getWriter();
12270 out.write(cb + "(");
12272 out.print(dataBlock.toJsonString());
12279 * @param {Object} config A configuration object.
12281 Roo.data.ScriptTagProxy = function(config){
12282 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12283 Roo.apply(this, config);
12284 this.head = document.getElementsByTagName("head")[0];
12287 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12289 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12291 * @cfg {String} url The URL from which to request the data object.
12294 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12298 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12299 * the server the name of the callback function set up by the load call to process the returned data object.
12300 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12301 * javascript output which calls this named function passing the data object as its only parameter.
12303 callbackParam : "callback",
12305 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12306 * name to the request.
12311 * Load data from the configured URL, read the data object into
12312 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12313 * process that block using the passed callback.
12314 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12315 * for the request to the remote server.
12316 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12317 * object into a block of Roo.data.Records.
12318 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12319 * The function must be passed <ul>
12320 * <li>The Record block object</li>
12321 * <li>The "arg" argument from the load function</li>
12322 * <li>A boolean success indicator</li>
12324 * @param {Object} scope The scope in which to call the callback
12325 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12327 load : function(params, reader, callback, scope, arg){
12328 if(this.fireEvent("beforeload", this, params) !== false){
12330 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12332 var url = this.url;
12333 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12335 url += "&_dc=" + (new Date().getTime());
12337 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12340 cb : "stcCallback"+transId,
12341 scriptId : "stcScript"+transId,
12345 callback : callback,
12351 window[trans.cb] = function(o){
12352 conn.handleResponse(o, trans);
12355 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12357 if(this.autoAbort !== false){
12361 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12363 var script = document.createElement("script");
12364 script.setAttribute("src", url);
12365 script.setAttribute("type", "text/javascript");
12366 script.setAttribute("id", trans.scriptId);
12367 this.head.appendChild(script);
12369 this.trans = trans;
12371 callback.call(scope||this, null, arg, false);
12376 isLoading : function(){
12377 return this.trans ? true : false;
12381 * Abort the current server request.
12383 abort : function(){
12384 if(this.isLoading()){
12385 this.destroyTrans(this.trans);
12390 destroyTrans : function(trans, isLoaded){
12391 this.head.removeChild(document.getElementById(trans.scriptId));
12392 clearTimeout(trans.timeoutId);
12394 window[trans.cb] = undefined;
12396 delete window[trans.cb];
12399 // if hasn't been loaded, wait for load to remove it to prevent script error
12400 window[trans.cb] = function(){
12401 window[trans.cb] = undefined;
12403 delete window[trans.cb];
12410 handleResponse : function(o, trans){
12411 this.trans = false;
12412 this.destroyTrans(trans, true);
12415 result = trans.reader.readRecords(o);
12417 this.fireEvent("loadexception", this, o, trans.arg, e);
12418 trans.callback.call(trans.scope||window, null, trans.arg, false);
12421 this.fireEvent("load", this, o, trans.arg);
12422 trans.callback.call(trans.scope||window, result, trans.arg, true);
12426 handleFailure : function(trans){
12427 this.trans = false;
12428 this.destroyTrans(trans, false);
12429 this.fireEvent("loadexception", this, null, trans.arg);
12430 trans.callback.call(trans.scope||window, null, trans.arg, false);
12434 * Ext JS Library 1.1.1
12435 * Copyright(c) 2006-2007, Ext JS, LLC.
12437 * Originally Released Under LGPL - original licence link has changed is not relivant.
12440 * <script type="text/javascript">
12444 * @class Roo.data.JsonReader
12445 * @extends Roo.data.DataReader
12446 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12447 * based on mappings in a provided Roo.data.Record constructor.
12449 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12450 * in the reply previously.
12455 var RecordDef = Roo.data.Record.create([
12456 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12457 {name: 'occupation'} // This field will use "occupation" as the mapping.
12459 var myReader = new Roo.data.JsonReader({
12460 totalProperty: "results", // The property which contains the total dataset size (optional)
12461 root: "rows", // The property which contains an Array of row objects
12462 id: "id" // The property within each row object that provides an ID for the record (optional)
12466 * This would consume a JSON file like this:
12468 { 'results': 2, 'rows': [
12469 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12470 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12473 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12474 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12475 * paged from the remote server.
12476 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12477 * @cfg {String} root name of the property which contains the Array of row objects.
12478 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12479 * @cfg {Array} fields Array of field definition objects
12481 * Create a new JsonReader
12482 * @param {Object} meta Metadata configuration options
12483 * @param {Object} recordType Either an Array of field definition objects,
12484 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12486 Roo.data.JsonReader = function(meta, recordType){
12489 // set some defaults:
12490 Roo.applyIf(meta, {
12491 totalProperty: 'total',
12492 successProperty : 'success',
12497 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12499 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12502 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12503 * Used by Store query builder to append _requestMeta to params.
12506 metaFromRemote : false,
12508 * This method is only used by a DataProxy which has retrieved data from a remote server.
12509 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12510 * @return {Object} data A data block which is used by an Roo.data.Store object as
12511 * a cache of Roo.data.Records.
12513 read : function(response){
12514 var json = response.responseText;
12516 var o = /* eval:var:o */ eval("("+json+")");
12518 throw {message: "JsonReader.read: Json object not found"};
12524 this.metaFromRemote = true;
12525 this.meta = o.metaData;
12526 this.recordType = Roo.data.Record.create(o.metaData.fields);
12527 this.onMetaChange(this.meta, this.recordType, o);
12529 return this.readRecords(o);
12532 // private function a store will implement
12533 onMetaChange : function(meta, recordType, o){
12540 simpleAccess: function(obj, subsc) {
12547 getJsonAccessor: function(){
12549 return function(expr) {
12551 return(re.test(expr))
12552 ? new Function("obj", "return obj." + expr)
12557 return Roo.emptyFn;
12562 * Create a data block containing Roo.data.Records from an XML document.
12563 * @param {Object} o An object which contains an Array of row objects in the property specified
12564 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12565 * which contains the total size of the dataset.
12566 * @return {Object} data A data block which is used by an Roo.data.Store object as
12567 * a cache of Roo.data.Records.
12569 readRecords : function(o){
12571 * After any data loads, the raw JSON data is available for further custom processing.
12575 var s = this.meta, Record = this.recordType,
12576 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12578 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12580 if(s.totalProperty) {
12581 this.getTotal = this.getJsonAccessor(s.totalProperty);
12583 if(s.successProperty) {
12584 this.getSuccess = this.getJsonAccessor(s.successProperty);
12586 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12588 var g = this.getJsonAccessor(s.id);
12589 this.getId = function(rec) {
12591 return (r === undefined || r === "") ? null : r;
12594 this.getId = function(){return null;};
12597 for(var jj = 0; jj < fl; jj++){
12599 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12600 this.ef[jj] = this.getJsonAccessor(map);
12604 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12605 if(s.totalProperty){
12606 var vt = parseInt(this.getTotal(o), 10);
12611 if(s.successProperty){
12612 var vs = this.getSuccess(o);
12613 if(vs === false || vs === 'false'){
12618 for(var i = 0; i < c; i++){
12621 var id = this.getId(n);
12622 for(var j = 0; j < fl; j++){
12624 var v = this.ef[j](n);
12626 Roo.log('missing convert for ' + f.name);
12630 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12632 var record = new Record(values, id);
12634 records[i] = record;
12640 totalRecords : totalRecords
12645 * Ext JS Library 1.1.1
12646 * Copyright(c) 2006-2007, Ext JS, LLC.
12648 * Originally Released Under LGPL - original licence link has changed is not relivant.
12651 * <script type="text/javascript">
12655 * @class Roo.data.ArrayReader
12656 * @extends Roo.data.DataReader
12657 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12658 * Each element of that Array represents a row of data fields. The
12659 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12660 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12664 var RecordDef = Roo.data.Record.create([
12665 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12666 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12668 var myReader = new Roo.data.ArrayReader({
12669 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12673 * This would consume an Array like this:
12675 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12677 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12679 * Create a new JsonReader
12680 * @param {Object} meta Metadata configuration options.
12681 * @param {Object} recordType Either an Array of field definition objects
12682 * as specified to {@link Roo.data.Record#create},
12683 * or an {@link Roo.data.Record} object
12684 * created using {@link Roo.data.Record#create}.
12686 Roo.data.ArrayReader = function(meta, recordType){
12687 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12690 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12692 * Create a data block containing Roo.data.Records from an XML document.
12693 * @param {Object} o An Array of row objects which represents the dataset.
12694 * @return {Object} data A data block which is used by an Roo.data.Store object as
12695 * a cache of Roo.data.Records.
12697 readRecords : function(o){
12698 var sid = this.meta ? this.meta.id : null;
12699 var recordType = this.recordType, fields = recordType.prototype.fields;
12702 for(var i = 0; i < root.length; i++){
12705 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12706 for(var j = 0, jlen = fields.length; j < jlen; j++){
12707 var f = fields.items[j];
12708 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12709 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12711 values[f.name] = v;
12713 var record = new recordType(values, id);
12715 records[records.length] = record;
12719 totalRecords : records.length
12728 * @class Roo.bootstrap.ComboBox
12729 * @extends Roo.bootstrap.TriggerField
12730 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12731 * @cfg {Boolean} append (true|false) default false
12732 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12733 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12734 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12735 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12736 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12737 * @cfg {Boolean} animate default true
12738 * @cfg {Boolean} emptyResultText only for touch device
12739 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12740 * @cfg {String} emptyTitle default ''
12742 * Create a new ComboBox.
12743 * @param {Object} config Configuration options
12745 Roo.bootstrap.ComboBox = function(config){
12746 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12750 * Fires when the dropdown list is expanded
12751 * @param {Roo.bootstrap.ComboBox} combo This combo box
12756 * Fires when the dropdown list is collapsed
12757 * @param {Roo.bootstrap.ComboBox} combo This combo box
12761 * @event beforeselect
12762 * Fires before a list item is selected. Return false to cancel the selection.
12763 * @param {Roo.bootstrap.ComboBox} combo This combo box
12764 * @param {Roo.data.Record} record The data record returned from the underlying store
12765 * @param {Number} index The index of the selected item in the dropdown list
12767 'beforeselect' : true,
12770 * Fires when a list item is selected
12771 * @param {Roo.bootstrap.ComboBox} combo This combo box
12772 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12773 * @param {Number} index The index of the selected item in the dropdown list
12777 * @event beforequery
12778 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12779 * The event object passed has these properties:
12780 * @param {Roo.bootstrap.ComboBox} combo This combo box
12781 * @param {String} query The query
12782 * @param {Boolean} forceAll true to force "all" query
12783 * @param {Boolean} cancel true to cancel the query
12784 * @param {Object} e The query event object
12786 'beforequery': true,
12789 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12790 * @param {Roo.bootstrap.ComboBox} combo This combo box
12795 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12796 * @param {Roo.bootstrap.ComboBox} combo This combo box
12797 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12802 * Fires when the remove value from the combobox array
12803 * @param {Roo.bootstrap.ComboBox} combo This combo box
12807 * @event afterremove
12808 * Fires when the remove value from the combobox array
12809 * @param {Roo.bootstrap.ComboBox} combo This combo box
12811 'afterremove' : true,
12813 * @event specialfilter
12814 * Fires when specialfilter
12815 * @param {Roo.bootstrap.ComboBox} combo This combo box
12817 'specialfilter' : true,
12820 * Fires when tick the element
12821 * @param {Roo.bootstrap.ComboBox} combo This combo box
12825 * @event touchviewdisplay
12826 * Fires when touch view require special display (default is using displayField)
12827 * @param {Roo.bootstrap.ComboBox} combo This combo box
12828 * @param {Object} cfg set html .
12830 'touchviewdisplay' : true
12835 this.tickItems = [];
12837 this.selectedIndex = -1;
12838 if(this.mode == 'local'){
12839 if(config.queryDelay === undefined){
12840 this.queryDelay = 10;
12842 if(config.minChars === undefined){
12848 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12851 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12852 * rendering into an Roo.Editor, defaults to false)
12855 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12856 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12859 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12862 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12863 * the dropdown list (defaults to undefined, with no header element)
12867 * @cfg {String/Roo.Template} tpl The template to use to render the output
12871 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12873 listWidth: undefined,
12875 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12876 * mode = 'remote' or 'text' if mode = 'local')
12878 displayField: undefined,
12881 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12882 * mode = 'remote' or 'value' if mode = 'local').
12883 * Note: use of a valueField requires the user make a selection
12884 * in order for a value to be mapped.
12886 valueField: undefined,
12888 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12893 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12894 * field's data value (defaults to the underlying DOM element's name)
12896 hiddenName: undefined,
12898 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12902 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12904 selectedClass: 'active',
12907 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12911 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12912 * anchor positions (defaults to 'tl-bl')
12914 listAlign: 'tl-bl?',
12916 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12920 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12921 * query specified by the allQuery config option (defaults to 'query')
12923 triggerAction: 'query',
12925 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12926 * (defaults to 4, does not apply if editable = false)
12930 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12931 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12935 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12936 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12940 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12941 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12945 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12946 * when editable = true (defaults to false)
12948 selectOnFocus:false,
12950 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12952 queryParam: 'query',
12954 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12955 * when mode = 'remote' (defaults to 'Loading...')
12957 loadingText: 'Loading...',
12959 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12963 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12967 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12968 * traditional select (defaults to true)
12972 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12976 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12980 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12981 * listWidth has a higher value)
12985 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12986 * allow the user to set arbitrary text into the field (defaults to false)
12988 forceSelection:false,
12990 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12991 * if typeAhead = true (defaults to 250)
12993 typeAheadDelay : 250,
12995 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12996 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12998 valueNotFoundText : undefined,
13000 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13002 blockFocus : false,
13005 * @cfg {Boolean} disableClear Disable showing of clear button.
13007 disableClear : false,
13009 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13011 alwaysQuery : false,
13014 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13019 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13021 invalidClass : "has-warning",
13024 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13026 validClass : "has-success",
13029 * @cfg {Boolean} specialFilter (true|false) special filter default false
13031 specialFilter : false,
13034 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13036 mobileTouchView : true,
13039 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13041 useNativeIOS : false,
13044 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13046 mobile_restrict_height : false,
13048 ios_options : false,
13060 btnPosition : 'right',
13061 triggerList : true,
13062 showToggleBtn : true,
13064 emptyResultText: 'Empty',
13065 triggerText : 'Select',
13068 // element that contains real text value.. (when hidden is used..)
13070 getAutoCreate : function()
13075 * Render classic select for iso
13078 if(Roo.isIOS && this.useNativeIOS){
13079 cfg = this.getAutoCreateNativeIOS();
13087 if(Roo.isTouch && this.mobileTouchView){
13088 cfg = this.getAutoCreateTouchView();
13095 if(!this.tickable){
13096 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13101 * ComboBox with tickable selections
13104 var align = this.labelAlign || this.parentLabelAlign();
13107 cls : 'form-group roo-combobox-tickable' //input-group
13110 var btn_text_select = '';
13111 var btn_text_done = '';
13112 var btn_text_cancel = '';
13114 if (this.btn_text_show) {
13115 btn_text_select = 'Select';
13116 btn_text_done = 'Done';
13117 btn_text_cancel = 'Cancel';
13122 cls : 'tickable-buttons',
13127 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13128 //html : this.triggerText
13129 html: btn_text_select
13135 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13137 html: btn_text_done
13143 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13145 html: btn_text_cancel
13151 buttons.cn.unshift({
13153 cls: 'roo-select2-search-field-input'
13159 Roo.each(buttons.cn, function(c){
13161 c.cls += ' btn-' + _this.size;
13164 if (_this.disabled) {
13175 cls: 'form-hidden-field'
13179 cls: 'roo-select2-choices',
13183 cls: 'roo-select2-search-field',
13194 cls: 'roo-select2-container input-group roo-select2-container-multi',
13199 // cls: 'typeahead typeahead-long dropdown-menu',
13200 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13205 if(this.hasFeedback && !this.allowBlank){
13209 cls: 'glyphicon form-control-feedback'
13212 combobox.cn.push(feedback);
13216 if (align ==='left' && this.fieldLabel.length) {
13218 cfg.cls += ' roo-form-group-label-left';
13223 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13224 tooltip : 'This field is required'
13229 cls : 'control-label',
13230 html : this.fieldLabel
13242 var labelCfg = cfg.cn[1];
13243 var contentCfg = cfg.cn[2];
13246 if(this.indicatorpos == 'right'){
13252 cls : 'control-label',
13256 html : this.fieldLabel
13260 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13261 tooltip : 'This field is required'
13276 labelCfg = cfg.cn[0];
13277 contentCfg = cfg.cn[1];
13281 if(this.labelWidth > 12){
13282 labelCfg.style = "width: " + this.labelWidth + 'px';
13285 if(this.labelWidth < 13 && this.labelmd == 0){
13286 this.labelmd = this.labelWidth;
13289 if(this.labellg > 0){
13290 labelCfg.cls += ' col-lg-' + this.labellg;
13291 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13294 if(this.labelmd > 0){
13295 labelCfg.cls += ' col-md-' + this.labelmd;
13296 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13299 if(this.labelsm > 0){
13300 labelCfg.cls += ' col-sm-' + this.labelsm;
13301 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13304 if(this.labelxs > 0){
13305 labelCfg.cls += ' col-xs-' + this.labelxs;
13306 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13310 } else if ( this.fieldLabel.length) {
13311 // Roo.log(" label");
13315 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13316 tooltip : 'This field is required'
13320 //cls : 'input-group-addon',
13321 html : this.fieldLabel
13326 if(this.indicatorpos == 'right'){
13330 //cls : 'input-group-addon',
13331 html : this.fieldLabel
13335 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13336 tooltip : 'This field is required'
13345 // Roo.log(" no label && no align");
13352 ['xs','sm','md','lg'].map(function(size){
13353 if (settings[size]) {
13354 cfg.cls += ' col-' + size + '-' + settings[size];
13362 _initEventsCalled : false,
13365 initEvents: function()
13367 if (this._initEventsCalled) { // as we call render... prevent looping...
13370 this._initEventsCalled = true;
13373 throw "can not find store for combo";
13376 this.indicator = this.indicatorEl();
13378 this.store = Roo.factory(this.store, Roo.data);
13379 this.store.parent = this;
13381 // if we are building from html. then this element is so complex, that we can not really
13382 // use the rendered HTML.
13383 // so we have to trash and replace the previous code.
13384 if (Roo.XComponent.build_from_html) {
13385 // remove this element....
13386 var e = this.el.dom, k=0;
13387 while (e ) { e = e.previousSibling; ++k;}
13392 this.rendered = false;
13394 this.render(this.parent().getChildContainer(true), k);
13397 if(Roo.isIOS && this.useNativeIOS){
13398 this.initIOSView();
13406 if(Roo.isTouch && this.mobileTouchView){
13407 this.initTouchView();
13412 this.initTickableEvents();
13416 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13418 if(this.hiddenName){
13420 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13422 this.hiddenField.dom.value =
13423 this.hiddenValue !== undefined ? this.hiddenValue :
13424 this.value !== undefined ? this.value : '';
13426 // prevent input submission
13427 this.el.dom.removeAttribute('name');
13428 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13433 // this.el.dom.setAttribute('autocomplete', 'off');
13436 var cls = 'x-combo-list';
13438 //this.list = new Roo.Layer({
13439 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13445 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13446 _this.list.setWidth(lw);
13449 this.list.on('mouseover', this.onViewOver, this);
13450 this.list.on('mousemove', this.onViewMove, this);
13451 this.list.on('scroll', this.onViewScroll, this);
13454 this.list.swallowEvent('mousewheel');
13455 this.assetHeight = 0;
13458 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13459 this.assetHeight += this.header.getHeight();
13462 this.innerList = this.list.createChild({cls:cls+'-inner'});
13463 this.innerList.on('mouseover', this.onViewOver, this);
13464 this.innerList.on('mousemove', this.onViewMove, this);
13465 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13467 if(this.allowBlank && !this.pageSize && !this.disableClear){
13468 this.footer = this.list.createChild({cls:cls+'-ft'});
13469 this.pageTb = new Roo.Toolbar(this.footer);
13473 this.footer = this.list.createChild({cls:cls+'-ft'});
13474 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13475 {pageSize: this.pageSize});
13479 if (this.pageTb && this.allowBlank && !this.disableClear) {
13481 this.pageTb.add(new Roo.Toolbar.Fill(), {
13482 cls: 'x-btn-icon x-btn-clear',
13484 handler: function()
13487 _this.clearValue();
13488 _this.onSelect(false, -1);
13493 this.assetHeight += this.footer.getHeight();
13498 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13501 this.view = new Roo.View(this.list, this.tpl, {
13502 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13504 //this.view.wrapEl.setDisplayed(false);
13505 this.view.on('click', this.onViewClick, this);
13508 this.store.on('beforeload', this.onBeforeLoad, this);
13509 this.store.on('load', this.onLoad, this);
13510 this.store.on('loadexception', this.onLoadException, this);
13512 if(this.resizable){
13513 this.resizer = new Roo.Resizable(this.list, {
13514 pinned:true, handles:'se'
13516 this.resizer.on('resize', function(r, w, h){
13517 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13518 this.listWidth = w;
13519 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13520 this.restrictHeight();
13522 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13525 if(!this.editable){
13526 this.editable = true;
13527 this.setEditable(false);
13532 if (typeof(this.events.add.listeners) != 'undefined') {
13534 this.addicon = this.wrap.createChild(
13535 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13537 this.addicon.on('click', function(e) {
13538 this.fireEvent('add', this);
13541 if (typeof(this.events.edit.listeners) != 'undefined') {
13543 this.editicon = this.wrap.createChild(
13544 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13545 if (this.addicon) {
13546 this.editicon.setStyle('margin-left', '40px');
13548 this.editicon.on('click', function(e) {
13550 // we fire even if inothing is selected..
13551 this.fireEvent('edit', this, this.lastData );
13557 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13558 "up" : function(e){
13559 this.inKeyMode = true;
13563 "down" : function(e){
13564 if(!this.isExpanded()){
13565 this.onTriggerClick();
13567 this.inKeyMode = true;
13572 "enter" : function(e){
13573 // this.onViewClick();
13577 if(this.fireEvent("specialkey", this, e)){
13578 this.onViewClick(false);
13584 "esc" : function(e){
13588 "tab" : function(e){
13591 if(this.fireEvent("specialkey", this, e)){
13592 this.onViewClick(false);
13600 doRelay : function(foo, bar, hname){
13601 if(hname == 'down' || this.scope.isExpanded()){
13602 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13611 this.queryDelay = Math.max(this.queryDelay || 10,
13612 this.mode == 'local' ? 10 : 250);
13615 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13617 if(this.typeAhead){
13618 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13620 if(this.editable !== false){
13621 this.inputEl().on("keyup", this.onKeyUp, this);
13623 if(this.forceSelection){
13624 this.inputEl().on('blur', this.doForce, this);
13628 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13629 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13633 initTickableEvents: function()
13637 if(this.hiddenName){
13639 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13641 this.hiddenField.dom.value =
13642 this.hiddenValue !== undefined ? this.hiddenValue :
13643 this.value !== undefined ? this.value : '';
13645 // prevent input submission
13646 this.el.dom.removeAttribute('name');
13647 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13652 // this.list = this.el.select('ul.dropdown-menu',true).first();
13654 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13655 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13656 if(this.triggerList){
13657 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13660 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13661 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13663 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13664 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13666 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13667 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13669 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13670 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13671 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13674 this.cancelBtn.hide();
13679 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13680 _this.list.setWidth(lw);
13683 this.list.on('mouseover', this.onViewOver, this);
13684 this.list.on('mousemove', this.onViewMove, this);
13686 this.list.on('scroll', this.onViewScroll, this);
13689 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13690 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13693 this.view = new Roo.View(this.list, this.tpl, {
13698 selectedClass: this.selectedClass
13701 //this.view.wrapEl.setDisplayed(false);
13702 this.view.on('click', this.onViewClick, this);
13706 this.store.on('beforeload', this.onBeforeLoad, this);
13707 this.store.on('load', this.onLoad, this);
13708 this.store.on('loadexception', this.onLoadException, this);
13711 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13712 "up" : function(e){
13713 this.inKeyMode = true;
13717 "down" : function(e){
13718 this.inKeyMode = true;
13722 "enter" : function(e){
13723 if(this.fireEvent("specialkey", this, e)){
13724 this.onViewClick(false);
13730 "esc" : function(e){
13731 this.onTickableFooterButtonClick(e, false, false);
13734 "tab" : function(e){
13735 this.fireEvent("specialkey", this, e);
13737 this.onTickableFooterButtonClick(e, false, false);
13744 doRelay : function(e, fn, key){
13745 if(this.scope.isExpanded()){
13746 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13755 this.queryDelay = Math.max(this.queryDelay || 10,
13756 this.mode == 'local' ? 10 : 250);
13759 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13761 if(this.typeAhead){
13762 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13765 if(this.editable !== false){
13766 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13769 this.indicator = this.indicatorEl();
13771 if(this.indicator){
13772 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13773 this.indicator.hide();
13778 onDestroy : function(){
13780 this.view.setStore(null);
13781 this.view.el.removeAllListeners();
13782 this.view.el.remove();
13783 this.view.purgeListeners();
13786 this.list.dom.innerHTML = '';
13790 this.store.un('beforeload', this.onBeforeLoad, this);
13791 this.store.un('load', this.onLoad, this);
13792 this.store.un('loadexception', this.onLoadException, this);
13794 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13798 fireKey : function(e){
13799 if(e.isNavKeyPress() && !this.list.isVisible()){
13800 this.fireEvent("specialkey", this, e);
13805 onResize: function(w, h){
13806 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13808 // if(typeof w != 'number'){
13809 // // we do not handle it!?!?
13812 // var tw = this.trigger.getWidth();
13813 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13814 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13816 // this.inputEl().setWidth( this.adjustWidth('input', x));
13818 // //this.trigger.setStyle('left', x+'px');
13820 // if(this.list && this.listWidth === undefined){
13821 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13822 // this.list.setWidth(lw);
13823 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13831 * Allow or prevent the user from directly editing the field text. If false is passed,
13832 * the user will only be able to select from the items defined in the dropdown list. This method
13833 * is the runtime equivalent of setting the 'editable' config option at config time.
13834 * @param {Boolean} value True to allow the user to directly edit the field text
13836 setEditable : function(value){
13837 if(value == this.editable){
13840 this.editable = value;
13842 this.inputEl().dom.setAttribute('readOnly', true);
13843 this.inputEl().on('mousedown', this.onTriggerClick, this);
13844 this.inputEl().addClass('x-combo-noedit');
13846 this.inputEl().dom.setAttribute('readOnly', false);
13847 this.inputEl().un('mousedown', this.onTriggerClick, this);
13848 this.inputEl().removeClass('x-combo-noedit');
13854 onBeforeLoad : function(combo,opts){
13855 if(!this.hasFocus){
13859 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13861 this.restrictHeight();
13862 this.selectedIndex = -1;
13866 onLoad : function(){
13868 this.hasQuery = false;
13870 if(!this.hasFocus){
13874 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13875 this.loading.hide();
13878 if(this.store.getCount() > 0){
13881 this.restrictHeight();
13882 if(this.lastQuery == this.allQuery){
13883 if(this.editable && !this.tickable){
13884 this.inputEl().dom.select();
13888 !this.selectByValue(this.value, true) &&
13891 !this.store.lastOptions ||
13892 typeof(this.store.lastOptions.add) == 'undefined' ||
13893 this.store.lastOptions.add != true
13896 this.select(0, true);
13899 if(this.autoFocus){
13902 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13903 this.taTask.delay(this.typeAheadDelay);
13907 this.onEmptyResults();
13913 onLoadException : function()
13915 this.hasQuery = false;
13917 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13918 this.loading.hide();
13921 if(this.tickable && this.editable){
13926 // only causes errors at present
13927 //Roo.log(this.store.reader.jsonData);
13928 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13930 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13936 onTypeAhead : function(){
13937 if(this.store.getCount() > 0){
13938 var r = this.store.getAt(0);
13939 var newValue = r.data[this.displayField];
13940 var len = newValue.length;
13941 var selStart = this.getRawValue().length;
13943 if(selStart != len){
13944 this.setRawValue(newValue);
13945 this.selectText(selStart, newValue.length);
13951 onSelect : function(record, index){
13953 if(this.fireEvent('beforeselect', this, record, index) !== false){
13955 this.setFromData(index > -1 ? record.data : false);
13958 this.fireEvent('select', this, record, index);
13963 * Returns the currently selected field value or empty string if no value is set.
13964 * @return {String} value The selected value
13966 getValue : function()
13968 if(Roo.isIOS && this.useNativeIOS){
13969 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13973 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13976 if(this.valueField){
13977 return typeof this.value != 'undefined' ? this.value : '';
13979 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13983 getRawValue : function()
13985 if(Roo.isIOS && this.useNativeIOS){
13986 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13989 var v = this.inputEl().getValue();
13995 * Clears any text/value currently set in the field
13997 clearValue : function(){
13999 if(this.hiddenField){
14000 this.hiddenField.dom.value = '';
14003 this.setRawValue('');
14004 this.lastSelectionText = '';
14005 this.lastData = false;
14007 var close = this.closeTriggerEl();
14018 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14019 * will be displayed in the field. If the value does not match the data value of an existing item,
14020 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14021 * Otherwise the field will be blank (although the value will still be set).
14022 * @param {String} value The value to match
14024 setValue : function(v)
14026 if(Roo.isIOS && this.useNativeIOS){
14027 this.setIOSValue(v);
14037 if(this.valueField){
14038 var r = this.findRecord(this.valueField, v);
14040 text = r.data[this.displayField];
14041 }else if(this.valueNotFoundText !== undefined){
14042 text = this.valueNotFoundText;
14045 this.lastSelectionText = text;
14046 if(this.hiddenField){
14047 this.hiddenField.dom.value = v;
14049 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14052 var close = this.closeTriggerEl();
14055 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14061 * @property {Object} the last set data for the element
14066 * Sets the value of the field based on a object which is related to the record format for the store.
14067 * @param {Object} value the value to set as. or false on reset?
14069 setFromData : function(o){
14076 var dv = ''; // display value
14077 var vv = ''; // value value..
14079 if (this.displayField) {
14080 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14082 // this is an error condition!!!
14083 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14086 if(this.valueField){
14087 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14090 var close = this.closeTriggerEl();
14093 if(dv.length || vv * 1 > 0){
14095 this.blockFocus=true;
14101 if(this.hiddenField){
14102 this.hiddenField.dom.value = vv;
14104 this.lastSelectionText = dv;
14105 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14109 // no hidden field.. - we store the value in 'value', but still display
14110 // display field!!!!
14111 this.lastSelectionText = dv;
14112 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14119 reset : function(){
14120 // overridden so that last data is reset..
14127 this.setValue(this.originalValue);
14128 //this.clearInvalid();
14129 this.lastData = false;
14131 this.view.clearSelections();
14137 findRecord : function(prop, value){
14139 if(this.store.getCount() > 0){
14140 this.store.each(function(r){
14141 if(r.data[prop] == value){
14151 getName: function()
14153 // returns hidden if it's set..
14154 if (!this.rendered) {return ''};
14155 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14159 onViewMove : function(e, t){
14160 this.inKeyMode = false;
14164 onViewOver : function(e, t){
14165 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14168 var item = this.view.findItemFromChild(t);
14171 var index = this.view.indexOf(item);
14172 this.select(index, false);
14177 onViewClick : function(view, doFocus, el, e)
14179 var index = this.view.getSelectedIndexes()[0];
14181 var r = this.store.getAt(index);
14185 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14192 Roo.each(this.tickItems, function(v,k){
14194 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14196 _this.tickItems.splice(k, 1);
14198 if(typeof(e) == 'undefined' && view == false){
14199 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14211 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14212 this.tickItems.push(r.data);
14215 if(typeof(e) == 'undefined' && view == false){
14216 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14223 this.onSelect(r, index);
14225 if(doFocus !== false && !this.blockFocus){
14226 this.inputEl().focus();
14231 restrictHeight : function(){
14232 //this.innerList.dom.style.height = '';
14233 //var inner = this.innerList.dom;
14234 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14235 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14236 //this.list.beginUpdate();
14237 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14238 this.list.alignTo(this.inputEl(), this.listAlign);
14239 this.list.alignTo(this.inputEl(), this.listAlign);
14240 //this.list.endUpdate();
14244 onEmptyResults : function(){
14246 if(this.tickable && this.editable){
14247 this.hasFocus = false;
14248 this.restrictHeight();
14256 * Returns true if the dropdown list is expanded, else false.
14258 isExpanded : function(){
14259 return this.list.isVisible();
14263 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14264 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14265 * @param {String} value The data value of the item to select
14266 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14267 * selected item if it is not currently in view (defaults to true)
14268 * @return {Boolean} True if the value matched an item in the list, else false
14270 selectByValue : function(v, scrollIntoView){
14271 if(v !== undefined && v !== null){
14272 var r = this.findRecord(this.valueField || this.displayField, v);
14274 this.select(this.store.indexOf(r), scrollIntoView);
14282 * Select an item in the dropdown list by its numeric index in the list. 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 {Number} index The zero-based index of the list 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)
14288 select : function(index, scrollIntoView){
14289 this.selectedIndex = index;
14290 this.view.select(index);
14291 if(scrollIntoView !== false){
14292 var el = this.view.getNode(index);
14294 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14297 this.list.scrollChildIntoView(el, false);
14303 selectNext : function(){
14304 var ct = this.store.getCount();
14306 if(this.selectedIndex == -1){
14308 }else if(this.selectedIndex < ct-1){
14309 this.select(this.selectedIndex+1);
14315 selectPrev : function(){
14316 var ct = this.store.getCount();
14318 if(this.selectedIndex == -1){
14320 }else if(this.selectedIndex != 0){
14321 this.select(this.selectedIndex-1);
14327 onKeyUp : function(e){
14328 if(this.editable !== false && !e.isSpecialKey()){
14329 this.lastKey = e.getKey();
14330 this.dqTask.delay(this.queryDelay);
14335 validateBlur : function(){
14336 return !this.list || !this.list.isVisible();
14340 initQuery : function(){
14342 var v = this.getRawValue();
14344 if(this.tickable && this.editable){
14345 v = this.tickableInputEl().getValue();
14352 doForce : function(){
14353 if(this.inputEl().dom.value.length > 0){
14354 this.inputEl().dom.value =
14355 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14361 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14362 * query allowing the query action to be canceled if needed.
14363 * @param {String} query The SQL query to execute
14364 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14365 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14366 * saved in the current store (defaults to false)
14368 doQuery : function(q, forceAll){
14370 if(q === undefined || q === null){
14375 forceAll: forceAll,
14379 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14384 forceAll = qe.forceAll;
14385 if(forceAll === true || (q.length >= this.minChars)){
14387 this.hasQuery = true;
14389 if(this.lastQuery != q || this.alwaysQuery){
14390 this.lastQuery = q;
14391 if(this.mode == 'local'){
14392 this.selectedIndex = -1;
14394 this.store.clearFilter();
14397 if(this.specialFilter){
14398 this.fireEvent('specialfilter', this);
14403 this.store.filter(this.displayField, q);
14406 this.store.fireEvent("datachanged", this.store);
14413 this.store.baseParams[this.queryParam] = q;
14415 var options = {params : this.getParams(q)};
14418 options.add = true;
14419 options.params.start = this.page * this.pageSize;
14422 this.store.load(options);
14425 * this code will make the page width larger, at the beginning, the list not align correctly,
14426 * we should expand the list on onLoad
14427 * so command out it
14432 this.selectedIndex = -1;
14437 this.loadNext = false;
14441 getParams : function(q){
14443 //p[this.queryParam] = q;
14447 p.limit = this.pageSize;
14453 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14455 collapse : function(){
14456 if(!this.isExpanded()){
14462 this.hasFocus = false;
14466 this.cancelBtn.hide();
14467 this.trigger.show();
14470 this.tickableInputEl().dom.value = '';
14471 this.tickableInputEl().blur();
14476 Roo.get(document).un('mousedown', this.collapseIf, this);
14477 Roo.get(document).un('mousewheel', this.collapseIf, this);
14478 if (!this.editable) {
14479 Roo.get(document).un('keydown', this.listKeyPress, this);
14481 this.fireEvent('collapse', this);
14487 collapseIf : function(e){
14488 var in_combo = e.within(this.el);
14489 var in_list = e.within(this.list);
14490 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14492 if (in_combo || in_list || is_list) {
14493 //e.stopPropagation();
14498 this.onTickableFooterButtonClick(e, false, false);
14506 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14508 expand : function(){
14510 if(this.isExpanded() || !this.hasFocus){
14514 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14515 this.list.setWidth(lw);
14521 this.restrictHeight();
14525 this.tickItems = Roo.apply([], this.item);
14528 this.cancelBtn.show();
14529 this.trigger.hide();
14532 this.tickableInputEl().focus();
14537 Roo.get(document).on('mousedown', this.collapseIf, this);
14538 Roo.get(document).on('mousewheel', this.collapseIf, this);
14539 if (!this.editable) {
14540 Roo.get(document).on('keydown', this.listKeyPress, this);
14543 this.fireEvent('expand', this);
14547 // Implements the default empty TriggerField.onTriggerClick function
14548 onTriggerClick : function(e)
14550 Roo.log('trigger click');
14552 if(this.disabled || !this.triggerList){
14557 this.loadNext = false;
14559 if(this.isExpanded()){
14561 if (!this.blockFocus) {
14562 this.inputEl().focus();
14566 this.hasFocus = true;
14567 if(this.triggerAction == 'all') {
14568 this.doQuery(this.allQuery, true);
14570 this.doQuery(this.getRawValue());
14572 if (!this.blockFocus) {
14573 this.inputEl().focus();
14578 onTickableTriggerClick : function(e)
14585 this.loadNext = false;
14586 this.hasFocus = true;
14588 if(this.triggerAction == 'all') {
14589 this.doQuery(this.allQuery, true);
14591 this.doQuery(this.getRawValue());
14595 onSearchFieldClick : function(e)
14597 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14598 this.onTickableFooterButtonClick(e, false, false);
14602 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14607 this.loadNext = false;
14608 this.hasFocus = true;
14610 if(this.triggerAction == 'all') {
14611 this.doQuery(this.allQuery, true);
14613 this.doQuery(this.getRawValue());
14617 listKeyPress : function(e)
14619 //Roo.log('listkeypress');
14620 // scroll to first matching element based on key pres..
14621 if (e.isSpecialKey()) {
14624 var k = String.fromCharCode(e.getKey()).toUpperCase();
14627 var csel = this.view.getSelectedNodes();
14628 var cselitem = false;
14630 var ix = this.view.indexOf(csel[0]);
14631 cselitem = this.store.getAt(ix);
14632 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14638 this.store.each(function(v) {
14640 // start at existing selection.
14641 if (cselitem.id == v.id) {
14647 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14648 match = this.store.indexOf(v);
14654 if (match === false) {
14655 return true; // no more action?
14658 this.view.select(match);
14659 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14660 sn.scrollIntoView(sn.dom.parentNode, false);
14663 onViewScroll : function(e, t){
14665 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){
14669 this.hasQuery = true;
14671 this.loading = this.list.select('.loading', true).first();
14673 if(this.loading === null){
14674 this.list.createChild({
14676 cls: 'loading roo-select2-more-results roo-select2-active',
14677 html: 'Loading more results...'
14680 this.loading = this.list.select('.loading', true).first();
14682 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14684 this.loading.hide();
14687 this.loading.show();
14692 this.loadNext = true;
14694 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14699 addItem : function(o)
14701 var dv = ''; // display value
14703 if (this.displayField) {
14704 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14706 // this is an error condition!!!
14707 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14714 var choice = this.choices.createChild({
14716 cls: 'roo-select2-search-choice',
14725 cls: 'roo-select2-search-choice-close fa fa-times',
14730 }, this.searchField);
14732 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14734 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14742 this.inputEl().dom.value = '';
14747 onRemoveItem : function(e, _self, o)
14749 e.preventDefault();
14751 this.lastItem = Roo.apply([], this.item);
14753 var index = this.item.indexOf(o.data) * 1;
14756 Roo.log('not this item?!');
14760 this.item.splice(index, 1);
14765 this.fireEvent('remove', this, e);
14771 syncValue : function()
14773 if(!this.item.length){
14780 Roo.each(this.item, function(i){
14781 if(_this.valueField){
14782 value.push(i[_this.valueField]);
14789 this.value = value.join(',');
14791 if(this.hiddenField){
14792 this.hiddenField.dom.value = this.value;
14795 this.store.fireEvent("datachanged", this.store);
14800 clearItem : function()
14802 if(!this.multiple){
14808 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14816 if(this.tickable && !Roo.isTouch){
14817 this.view.refresh();
14821 inputEl: function ()
14823 if(Roo.isIOS && this.useNativeIOS){
14824 return this.el.select('select.roo-ios-select', true).first();
14827 if(Roo.isTouch && this.mobileTouchView){
14828 return this.el.select('input.form-control',true).first();
14832 return this.searchField;
14835 return this.el.select('input.form-control',true).first();
14838 onTickableFooterButtonClick : function(e, btn, el)
14840 e.preventDefault();
14842 this.lastItem = Roo.apply([], this.item);
14844 if(btn && btn.name == 'cancel'){
14845 this.tickItems = Roo.apply([], this.item);
14854 Roo.each(this.tickItems, function(o){
14862 validate : function()
14864 if(this.getVisibilityEl().hasClass('hidden')){
14868 var v = this.getRawValue();
14871 v = this.getValue();
14874 if(this.disabled || this.allowBlank || v.length){
14879 this.markInvalid();
14883 tickableInputEl : function()
14885 if(!this.tickable || !this.editable){
14886 return this.inputEl();
14889 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14893 getAutoCreateTouchView : function()
14898 cls: 'form-group' //input-group
14904 type : this.inputType,
14905 cls : 'form-control x-combo-noedit',
14906 autocomplete: 'new-password',
14907 placeholder : this.placeholder || '',
14912 input.name = this.name;
14916 input.cls += ' input-' + this.size;
14919 if (this.disabled) {
14920 input.disabled = true;
14931 inputblock.cls += ' input-group';
14933 inputblock.cn.unshift({
14935 cls : 'input-group-addon',
14940 if(this.removable && !this.multiple){
14941 inputblock.cls += ' roo-removable';
14943 inputblock.cn.push({
14946 cls : 'roo-combo-removable-btn close'
14950 if(this.hasFeedback && !this.allowBlank){
14952 inputblock.cls += ' has-feedback';
14954 inputblock.cn.push({
14956 cls: 'glyphicon form-control-feedback'
14963 inputblock.cls += (this.before) ? '' : ' input-group';
14965 inputblock.cn.push({
14967 cls : 'input-group-addon',
14978 cls: 'form-hidden-field'
14992 cls: 'form-hidden-field'
14996 cls: 'roo-select2-choices',
15000 cls: 'roo-select2-search-field',
15013 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15019 if(!this.multiple && this.showToggleBtn){
15026 if (this.caret != false) {
15029 cls: 'fa fa-' + this.caret
15036 cls : 'input-group-addon btn dropdown-toggle',
15041 cls: 'combobox-clear',
15055 combobox.cls += ' roo-select2-container-multi';
15058 var align = this.labelAlign || this.parentLabelAlign();
15060 if (align ==='left' && this.fieldLabel.length) {
15065 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15066 tooltip : 'This field is required'
15070 cls : 'control-label',
15071 html : this.fieldLabel
15082 var labelCfg = cfg.cn[1];
15083 var contentCfg = cfg.cn[2];
15086 if(this.indicatorpos == 'right'){
15091 cls : 'control-label',
15095 html : this.fieldLabel
15099 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15100 tooltip : 'This field is required'
15113 labelCfg = cfg.cn[0];
15114 contentCfg = cfg.cn[1];
15119 if(this.labelWidth > 12){
15120 labelCfg.style = "width: " + this.labelWidth + 'px';
15123 if(this.labelWidth < 13 && this.labelmd == 0){
15124 this.labelmd = this.labelWidth;
15127 if(this.labellg > 0){
15128 labelCfg.cls += ' col-lg-' + this.labellg;
15129 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15132 if(this.labelmd > 0){
15133 labelCfg.cls += ' col-md-' + this.labelmd;
15134 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15137 if(this.labelsm > 0){
15138 labelCfg.cls += ' col-sm-' + this.labelsm;
15139 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15142 if(this.labelxs > 0){
15143 labelCfg.cls += ' col-xs-' + this.labelxs;
15144 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15148 } else if ( this.fieldLabel.length) {
15152 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15153 tooltip : 'This field is required'
15157 cls : 'control-label',
15158 html : this.fieldLabel
15169 if(this.indicatorpos == 'right'){
15173 cls : 'control-label',
15174 html : this.fieldLabel,
15178 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15179 tooltip : 'This field is required'
15196 var settings = this;
15198 ['xs','sm','md','lg'].map(function(size){
15199 if (settings[size]) {
15200 cfg.cls += ' col-' + size + '-' + settings[size];
15207 initTouchView : function()
15209 this.renderTouchView();
15211 this.touchViewEl.on('scroll', function(){
15212 this.el.dom.scrollTop = 0;
15215 this.originalValue = this.getValue();
15217 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15219 this.inputEl().on("click", this.showTouchView, this);
15220 if (this.triggerEl) {
15221 this.triggerEl.on("click", this.showTouchView, this);
15225 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15226 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15228 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15230 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15231 this.store.on('load', this.onTouchViewLoad, this);
15232 this.store.on('loadexception', this.onTouchViewLoadException, this);
15234 if(this.hiddenName){
15236 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15238 this.hiddenField.dom.value =
15239 this.hiddenValue !== undefined ? this.hiddenValue :
15240 this.value !== undefined ? this.value : '';
15242 this.el.dom.removeAttribute('name');
15243 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15247 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15248 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15251 if(this.removable && !this.multiple){
15252 var close = this.closeTriggerEl();
15254 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15255 close.on('click', this.removeBtnClick, this, close);
15259 * fix the bug in Safari iOS8
15261 this.inputEl().on("focus", function(e){
15262 document.activeElement.blur();
15265 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15272 renderTouchView : function()
15274 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15275 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15277 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15278 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15280 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15281 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15282 this.touchViewBodyEl.setStyle('overflow', 'auto');
15284 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15285 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15287 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15288 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15292 showTouchView : function()
15298 this.touchViewHeaderEl.hide();
15300 if(this.modalTitle.length){
15301 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15302 this.touchViewHeaderEl.show();
15305 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15306 this.touchViewEl.show();
15308 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15310 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15311 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15313 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15315 if(this.modalTitle.length){
15316 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15319 this.touchViewBodyEl.setHeight(bodyHeight);
15323 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15325 this.touchViewEl.addClass('in');
15328 if(this._touchViewMask){
15329 Roo.get(document.body).addClass("x-body-masked");
15330 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15331 this._touchViewMask.setStyle('z-index', 10000);
15332 this._touchViewMask.addClass('show');
15335 this.doTouchViewQuery();
15339 hideTouchView : function()
15341 this.touchViewEl.removeClass('in');
15345 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15347 this.touchViewEl.setStyle('display', 'none');
15350 if(this._touchViewMask){
15351 this._touchViewMask.removeClass('show');
15352 Roo.get(document.body).removeClass("x-body-masked");
15356 setTouchViewValue : function()
15363 Roo.each(this.tickItems, function(o){
15368 this.hideTouchView();
15371 doTouchViewQuery : function()
15380 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15384 if(!this.alwaysQuery || this.mode == 'local'){
15385 this.onTouchViewLoad();
15392 onTouchViewBeforeLoad : function(combo,opts)
15398 onTouchViewLoad : function()
15400 if(this.store.getCount() < 1){
15401 this.onTouchViewEmptyResults();
15405 this.clearTouchView();
15407 var rawValue = this.getRawValue();
15409 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15411 this.tickItems = [];
15413 this.store.data.each(function(d, rowIndex){
15414 var row = this.touchViewListGroup.createChild(template);
15416 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15417 row.addClass(d.data.cls);
15420 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15423 html : d.data[this.displayField]
15426 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15427 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15430 row.removeClass('selected');
15431 if(!this.multiple && this.valueField &&
15432 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15435 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15436 row.addClass('selected');
15439 if(this.multiple && this.valueField &&
15440 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15444 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15445 this.tickItems.push(d.data);
15448 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15452 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15454 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15456 if(this.modalTitle.length){
15457 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15460 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15462 if(this.mobile_restrict_height && listHeight < bodyHeight){
15463 this.touchViewBodyEl.setHeight(listHeight);
15468 if(firstChecked && listHeight > bodyHeight){
15469 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15474 onTouchViewLoadException : function()
15476 this.hideTouchView();
15479 onTouchViewEmptyResults : function()
15481 this.clearTouchView();
15483 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15485 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15489 clearTouchView : function()
15491 this.touchViewListGroup.dom.innerHTML = '';
15494 onTouchViewClick : function(e, el, o)
15496 e.preventDefault();
15499 var rowIndex = o.rowIndex;
15501 var r = this.store.getAt(rowIndex);
15503 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15505 if(!this.multiple){
15506 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15507 c.dom.removeAttribute('checked');
15510 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15512 this.setFromData(r.data);
15514 var close = this.closeTriggerEl();
15520 this.hideTouchView();
15522 this.fireEvent('select', this, r, rowIndex);
15527 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15528 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15529 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15533 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15534 this.addItem(r.data);
15535 this.tickItems.push(r.data);
15539 getAutoCreateNativeIOS : function()
15542 cls: 'form-group' //input-group,
15547 cls : 'roo-ios-select'
15551 combobox.name = this.name;
15554 if (this.disabled) {
15555 combobox.disabled = true;
15558 var settings = this;
15560 ['xs','sm','md','lg'].map(function(size){
15561 if (settings[size]) {
15562 cfg.cls += ' col-' + size + '-' + settings[size];
15572 initIOSView : function()
15574 this.store.on('load', this.onIOSViewLoad, this);
15579 onIOSViewLoad : function()
15581 if(this.store.getCount() < 1){
15585 this.clearIOSView();
15587 if(this.allowBlank) {
15589 var default_text = '-- SELECT --';
15591 if(this.placeholder.length){
15592 default_text = this.placeholder;
15595 if(this.emptyTitle.length){
15596 default_text += ' - ' + this.emptyTitle + ' -';
15599 var opt = this.inputEl().createChild({
15602 html : default_text
15606 o[this.valueField] = 0;
15607 o[this.displayField] = default_text;
15609 this.ios_options.push({
15616 this.store.data.each(function(d, rowIndex){
15620 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15621 html = d.data[this.displayField];
15626 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15627 value = d.data[this.valueField];
15636 if(this.value == d.data[this.valueField]){
15637 option['selected'] = true;
15640 var opt = this.inputEl().createChild(option);
15642 this.ios_options.push({
15649 this.inputEl().on('change', function(){
15650 this.fireEvent('select', this);
15655 clearIOSView: function()
15657 this.inputEl().dom.innerHTML = '';
15659 this.ios_options = [];
15662 setIOSValue: function(v)
15666 if(!this.ios_options){
15670 Roo.each(this.ios_options, function(opts){
15672 opts.el.dom.removeAttribute('selected');
15674 if(opts.data[this.valueField] != v){
15678 opts.el.dom.setAttribute('selected', true);
15684 * @cfg {Boolean} grow
15688 * @cfg {Number} growMin
15692 * @cfg {Number} growMax
15701 Roo.apply(Roo.bootstrap.ComboBox, {
15705 cls: 'modal-header',
15727 cls: 'list-group-item',
15731 cls: 'roo-combobox-list-group-item-value'
15735 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15749 listItemCheckbox : {
15751 cls: 'list-group-item',
15755 cls: 'roo-combobox-list-group-item-value'
15759 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15775 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15780 cls: 'modal-footer',
15788 cls: 'col-xs-6 text-left',
15791 cls: 'btn btn-danger roo-touch-view-cancel',
15797 cls: 'col-xs-6 text-right',
15800 cls: 'btn btn-success roo-touch-view-ok',
15811 Roo.apply(Roo.bootstrap.ComboBox, {
15813 touchViewTemplate : {
15815 cls: 'modal fade roo-combobox-touch-view',
15819 cls: 'modal-dialog',
15820 style : 'position:fixed', // we have to fix position....
15824 cls: 'modal-content',
15826 Roo.bootstrap.ComboBox.header,
15827 Roo.bootstrap.ComboBox.body,
15828 Roo.bootstrap.ComboBox.footer
15837 * Ext JS Library 1.1.1
15838 * Copyright(c) 2006-2007, Ext JS, LLC.
15840 * Originally Released Under LGPL - original licence link has changed is not relivant.
15843 * <script type="text/javascript">
15848 * @extends Roo.util.Observable
15849 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15850 * This class also supports single and multi selection modes. <br>
15851 * Create a data model bound view:
15853 var store = new Roo.data.Store(...);
15855 var view = new Roo.View({
15857 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15859 singleSelect: true,
15860 selectedClass: "ydataview-selected",
15864 // listen for node click?
15865 view.on("click", function(vw, index, node, e){
15866 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15870 dataModel.load("foobar.xml");
15872 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15874 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15875 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15877 * Note: old style constructor is still suported (container, template, config)
15880 * Create a new View
15881 * @param {Object} config The config object
15884 Roo.View = function(config, depreciated_tpl, depreciated_config){
15886 this.parent = false;
15888 if (typeof(depreciated_tpl) == 'undefined') {
15889 // new way.. - universal constructor.
15890 Roo.apply(this, config);
15891 this.el = Roo.get(this.el);
15894 this.el = Roo.get(config);
15895 this.tpl = depreciated_tpl;
15896 Roo.apply(this, depreciated_config);
15898 this.wrapEl = this.el.wrap().wrap();
15899 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15902 if(typeof(this.tpl) == "string"){
15903 this.tpl = new Roo.Template(this.tpl);
15905 // support xtype ctors..
15906 this.tpl = new Roo.factory(this.tpl, Roo);
15910 this.tpl.compile();
15915 * @event beforeclick
15916 * Fires before a click is processed. Returns false to cancel the default action.
15917 * @param {Roo.View} this
15918 * @param {Number} index The index of the target node
15919 * @param {HTMLElement} node The target node
15920 * @param {Roo.EventObject} e The raw event object
15922 "beforeclick" : true,
15925 * Fires when a template node is clicked.
15926 * @param {Roo.View} this
15927 * @param {Number} index The index of the target node
15928 * @param {HTMLElement} node The target node
15929 * @param {Roo.EventObject} e The raw event object
15934 * Fires when a template node is double clicked.
15935 * @param {Roo.View} this
15936 * @param {Number} index The index of the target node
15937 * @param {HTMLElement} node The target node
15938 * @param {Roo.EventObject} e The raw event object
15942 * @event contextmenu
15943 * Fires when a template node is right clicked.
15944 * @param {Roo.View} this
15945 * @param {Number} index The index of the target node
15946 * @param {HTMLElement} node The target node
15947 * @param {Roo.EventObject} e The raw event object
15949 "contextmenu" : true,
15951 * @event selectionchange
15952 * Fires when the selected nodes change.
15953 * @param {Roo.View} this
15954 * @param {Array} selections Array of the selected nodes
15956 "selectionchange" : true,
15959 * @event beforeselect
15960 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15961 * @param {Roo.View} this
15962 * @param {HTMLElement} node The node to be selected
15963 * @param {Array} selections Array of currently selected nodes
15965 "beforeselect" : true,
15967 * @event preparedata
15968 * Fires on every row to render, to allow you to change the data.
15969 * @param {Roo.View} this
15970 * @param {Object} data to be rendered (change this)
15972 "preparedata" : true
15980 "click": this.onClick,
15981 "dblclick": this.onDblClick,
15982 "contextmenu": this.onContextMenu,
15986 this.selections = [];
15988 this.cmp = new Roo.CompositeElementLite([]);
15990 this.store = Roo.factory(this.store, Roo.data);
15991 this.setStore(this.store, true);
15994 if ( this.footer && this.footer.xtype) {
15996 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15998 this.footer.dataSource = this.store;
15999 this.footer.container = fctr;
16000 this.footer = Roo.factory(this.footer, Roo);
16001 fctr.insertFirst(this.el);
16003 // this is a bit insane - as the paging toolbar seems to detach the el..
16004 // dom.parentNode.parentNode.parentNode
16005 // they get detached?
16009 Roo.View.superclass.constructor.call(this);
16014 Roo.extend(Roo.View, Roo.util.Observable, {
16017 * @cfg {Roo.data.Store} store Data store to load data from.
16022 * @cfg {String|Roo.Element} el The container element.
16027 * @cfg {String|Roo.Template} tpl The template used by this View
16031 * @cfg {String} dataName the named area of the template to use as the data area
16032 * Works with domtemplates roo-name="name"
16036 * @cfg {String} selectedClass The css class to add to selected nodes
16038 selectedClass : "x-view-selected",
16040 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16045 * @cfg {String} text to display on mask (default Loading)
16049 * @cfg {Boolean} multiSelect Allow multiple selection
16051 multiSelect : false,
16053 * @cfg {Boolean} singleSelect Allow single selection
16055 singleSelect: false,
16058 * @cfg {Boolean} toggleSelect - selecting
16060 toggleSelect : false,
16063 * @cfg {Boolean} tickable - selecting
16068 * Returns the element this view is bound to.
16069 * @return {Roo.Element}
16071 getEl : function(){
16072 return this.wrapEl;
16078 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16080 refresh : function(){
16081 //Roo.log('refresh');
16084 // if we are using something like 'domtemplate', then
16085 // the what gets used is:
16086 // t.applySubtemplate(NAME, data, wrapping data..)
16087 // the outer template then get' applied with
16088 // the store 'extra data'
16089 // and the body get's added to the
16090 // roo-name="data" node?
16091 // <span class='roo-tpl-{name}'></span> ?????
16095 this.clearSelections();
16096 this.el.update("");
16098 var records = this.store.getRange();
16099 if(records.length < 1) {
16101 // is this valid?? = should it render a template??
16103 this.el.update(this.emptyText);
16107 if (this.dataName) {
16108 this.el.update(t.apply(this.store.meta)); //????
16109 el = this.el.child('.roo-tpl-' + this.dataName);
16112 for(var i = 0, len = records.length; i < len; i++){
16113 var data = this.prepareData(records[i].data, i, records[i]);
16114 this.fireEvent("preparedata", this, data, i, records[i]);
16116 var d = Roo.apply({}, data);
16119 Roo.apply(d, {'roo-id' : Roo.id()});
16123 Roo.each(this.parent.item, function(item){
16124 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16127 Roo.apply(d, {'roo-data-checked' : 'checked'});
16131 html[html.length] = Roo.util.Format.trim(
16133 t.applySubtemplate(this.dataName, d, this.store.meta) :
16140 el.update(html.join(""));
16141 this.nodes = el.dom.childNodes;
16142 this.updateIndexes(0);
16147 * Function to override to reformat the data that is sent to
16148 * the template for each node.
16149 * DEPRICATED - use the preparedata event handler.
16150 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16151 * a JSON object for an UpdateManager bound view).
16153 prepareData : function(data, index, record)
16155 this.fireEvent("preparedata", this, data, index, record);
16159 onUpdate : function(ds, record){
16160 // Roo.log('on update');
16161 this.clearSelections();
16162 var index = this.store.indexOf(record);
16163 var n = this.nodes[index];
16164 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16165 n.parentNode.removeChild(n);
16166 this.updateIndexes(index, index);
16172 onAdd : function(ds, records, index)
16174 //Roo.log(['on Add', ds, records, index] );
16175 this.clearSelections();
16176 if(this.nodes.length == 0){
16180 var n = this.nodes[index];
16181 for(var i = 0, len = records.length; i < len; i++){
16182 var d = this.prepareData(records[i].data, i, records[i]);
16184 this.tpl.insertBefore(n, d);
16187 this.tpl.append(this.el, d);
16190 this.updateIndexes(index);
16193 onRemove : function(ds, record, index){
16194 // Roo.log('onRemove');
16195 this.clearSelections();
16196 var el = this.dataName ?
16197 this.el.child('.roo-tpl-' + this.dataName) :
16200 el.dom.removeChild(this.nodes[index]);
16201 this.updateIndexes(index);
16205 * Refresh an individual node.
16206 * @param {Number} index
16208 refreshNode : function(index){
16209 this.onUpdate(this.store, this.store.getAt(index));
16212 updateIndexes : function(startIndex, endIndex){
16213 var ns = this.nodes;
16214 startIndex = startIndex || 0;
16215 endIndex = endIndex || ns.length - 1;
16216 for(var i = startIndex; i <= endIndex; i++){
16217 ns[i].nodeIndex = i;
16222 * Changes the data store this view uses and refresh the view.
16223 * @param {Store} store
16225 setStore : function(store, initial){
16226 if(!initial && this.store){
16227 this.store.un("datachanged", this.refresh);
16228 this.store.un("add", this.onAdd);
16229 this.store.un("remove", this.onRemove);
16230 this.store.un("update", this.onUpdate);
16231 this.store.un("clear", this.refresh);
16232 this.store.un("beforeload", this.onBeforeLoad);
16233 this.store.un("load", this.onLoad);
16234 this.store.un("loadexception", this.onLoad);
16238 store.on("datachanged", this.refresh, this);
16239 store.on("add", this.onAdd, this);
16240 store.on("remove", this.onRemove, this);
16241 store.on("update", this.onUpdate, this);
16242 store.on("clear", this.refresh, this);
16243 store.on("beforeload", this.onBeforeLoad, this);
16244 store.on("load", this.onLoad, this);
16245 store.on("loadexception", this.onLoad, this);
16253 * onbeforeLoad - masks the loading area.
16256 onBeforeLoad : function(store,opts)
16258 //Roo.log('onBeforeLoad');
16260 this.el.update("");
16262 this.el.mask(this.mask ? this.mask : "Loading" );
16264 onLoad : function ()
16271 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16272 * @param {HTMLElement} node
16273 * @return {HTMLElement} The template node
16275 findItemFromChild : function(node){
16276 var el = this.dataName ?
16277 this.el.child('.roo-tpl-' + this.dataName,true) :
16280 if(!node || node.parentNode == el){
16283 var p = node.parentNode;
16284 while(p && p != el){
16285 if(p.parentNode == el){
16294 onClick : function(e){
16295 var item = this.findItemFromChild(e.getTarget());
16297 var index = this.indexOf(item);
16298 if(this.onItemClick(item, index, e) !== false){
16299 this.fireEvent("click", this, index, item, e);
16302 this.clearSelections();
16307 onContextMenu : function(e){
16308 var item = this.findItemFromChild(e.getTarget());
16310 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16315 onDblClick : function(e){
16316 var item = this.findItemFromChild(e.getTarget());
16318 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16322 onItemClick : function(item, index, e)
16324 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16327 if (this.toggleSelect) {
16328 var m = this.isSelected(item) ? 'unselect' : 'select';
16331 _t[m](item, true, false);
16334 if(this.multiSelect || this.singleSelect){
16335 if(this.multiSelect && e.shiftKey && this.lastSelection){
16336 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16338 this.select(item, this.multiSelect && e.ctrlKey);
16339 this.lastSelection = item;
16342 if(!this.tickable){
16343 e.preventDefault();
16351 * Get the number of selected nodes.
16354 getSelectionCount : function(){
16355 return this.selections.length;
16359 * Get the currently selected nodes.
16360 * @return {Array} An array of HTMLElements
16362 getSelectedNodes : function(){
16363 return this.selections;
16367 * Get the indexes of the selected nodes.
16370 getSelectedIndexes : function(){
16371 var indexes = [], s = this.selections;
16372 for(var i = 0, len = s.length; i < len; i++){
16373 indexes.push(s[i].nodeIndex);
16379 * Clear all selections
16380 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16382 clearSelections : function(suppressEvent){
16383 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16384 this.cmp.elements = this.selections;
16385 this.cmp.removeClass(this.selectedClass);
16386 this.selections = [];
16387 if(!suppressEvent){
16388 this.fireEvent("selectionchange", this, this.selections);
16394 * Returns true if the passed node is selected
16395 * @param {HTMLElement/Number} node The node or node index
16396 * @return {Boolean}
16398 isSelected : function(node){
16399 var s = this.selections;
16403 node = this.getNode(node);
16404 return s.indexOf(node) !== -1;
16409 * @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
16410 * @param {Boolean} keepExisting (optional) true to keep existing selections
16411 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16413 select : function(nodeInfo, keepExisting, suppressEvent){
16414 if(nodeInfo instanceof Array){
16416 this.clearSelections(true);
16418 for(var i = 0, len = nodeInfo.length; i < len; i++){
16419 this.select(nodeInfo[i], true, true);
16423 var node = this.getNode(nodeInfo);
16424 if(!node || this.isSelected(node)){
16425 return; // already selected.
16428 this.clearSelections(true);
16431 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16432 Roo.fly(node).addClass(this.selectedClass);
16433 this.selections.push(node);
16434 if(!suppressEvent){
16435 this.fireEvent("selectionchange", this, this.selections);
16443 * @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
16444 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16445 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16447 unselect : function(nodeInfo, keepExisting, suppressEvent)
16449 if(nodeInfo instanceof Array){
16450 Roo.each(this.selections, function(s) {
16451 this.unselect(s, nodeInfo);
16455 var node = this.getNode(nodeInfo);
16456 if(!node || !this.isSelected(node)){
16457 //Roo.log("not selected");
16458 return; // not selected.
16462 Roo.each(this.selections, function(s) {
16464 Roo.fly(node).removeClass(this.selectedClass);
16471 this.selections= ns;
16472 this.fireEvent("selectionchange", this, this.selections);
16476 * Gets a template node.
16477 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16478 * @return {HTMLElement} The node or null if it wasn't found
16480 getNode : function(nodeInfo){
16481 if(typeof nodeInfo == "string"){
16482 return document.getElementById(nodeInfo);
16483 }else if(typeof nodeInfo == "number"){
16484 return this.nodes[nodeInfo];
16490 * Gets a range template nodes.
16491 * @param {Number} startIndex
16492 * @param {Number} endIndex
16493 * @return {Array} An array of nodes
16495 getNodes : function(start, end){
16496 var ns = this.nodes;
16497 start = start || 0;
16498 end = typeof end == "undefined" ? ns.length - 1 : end;
16501 for(var i = start; i <= end; i++){
16505 for(var i = start; i >= end; i--){
16513 * Finds the index of the passed node
16514 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16515 * @return {Number} The index of the node or -1
16517 indexOf : function(node){
16518 node = this.getNode(node);
16519 if(typeof node.nodeIndex == "number"){
16520 return node.nodeIndex;
16522 var ns = this.nodes;
16523 for(var i = 0, len = ns.length; i < len; i++){
16534 * based on jquery fullcalendar
16538 Roo.bootstrap = Roo.bootstrap || {};
16540 * @class Roo.bootstrap.Calendar
16541 * @extends Roo.bootstrap.Component
16542 * Bootstrap Calendar class
16543 * @cfg {Boolean} loadMask (true|false) default false
16544 * @cfg {Object} header generate the user specific header of the calendar, default false
16547 * Create a new Container
16548 * @param {Object} config The config object
16553 Roo.bootstrap.Calendar = function(config){
16554 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16558 * Fires when a date is selected
16559 * @param {DatePicker} this
16560 * @param {Date} date The selected date
16564 * @event monthchange
16565 * Fires when the displayed month changes
16566 * @param {DatePicker} this
16567 * @param {Date} date The selected month
16569 'monthchange': true,
16571 * @event evententer
16572 * Fires when mouse over an event
16573 * @param {Calendar} this
16574 * @param {event} Event
16576 'evententer': true,
16578 * @event eventleave
16579 * Fires when the mouse leaves an
16580 * @param {Calendar} this
16583 'eventleave': true,
16585 * @event eventclick
16586 * Fires when the mouse click an
16587 * @param {Calendar} this
16596 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16599 * @cfg {Number} startDay
16600 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16608 getAutoCreate : function(){
16611 var fc_button = function(name, corner, style, content ) {
16612 return Roo.apply({},{
16614 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16616 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16619 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16630 style : 'width:100%',
16637 cls : 'fc-header-left',
16639 fc_button('prev', 'left', 'arrow', '‹' ),
16640 fc_button('next', 'right', 'arrow', '›' ),
16641 { tag: 'span', cls: 'fc-header-space' },
16642 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16650 cls : 'fc-header-center',
16654 cls: 'fc-header-title',
16657 html : 'month / year'
16665 cls : 'fc-header-right',
16667 /* fc_button('month', 'left', '', 'month' ),
16668 fc_button('week', '', '', 'week' ),
16669 fc_button('day', 'right', '', 'day' )
16681 header = this.header;
16684 var cal_heads = function() {
16686 // fixme - handle this.
16688 for (var i =0; i < Date.dayNames.length; i++) {
16689 var d = Date.dayNames[i];
16692 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16693 html : d.substring(0,3)
16697 ret[0].cls += ' fc-first';
16698 ret[6].cls += ' fc-last';
16701 var cal_cell = function(n) {
16704 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16709 cls: 'fc-day-number',
16713 cls: 'fc-day-content',
16717 style: 'position: relative;' // height: 17px;
16729 var cal_rows = function() {
16732 for (var r = 0; r < 6; r++) {
16739 for (var i =0; i < Date.dayNames.length; i++) {
16740 var d = Date.dayNames[i];
16741 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16744 row.cn[0].cls+=' fc-first';
16745 row.cn[0].cn[0].style = 'min-height:90px';
16746 row.cn[6].cls+=' fc-last';
16750 ret[0].cls += ' fc-first';
16751 ret[4].cls += ' fc-prev-last';
16752 ret[5].cls += ' fc-last';
16759 cls: 'fc-border-separate',
16760 style : 'width:100%',
16768 cls : 'fc-first fc-last',
16786 cls : 'fc-content',
16787 style : "position: relative;",
16790 cls : 'fc-view fc-view-month fc-grid',
16791 style : 'position: relative',
16792 unselectable : 'on',
16795 cls : 'fc-event-container',
16796 style : 'position:absolute;z-index:8;top:0;left:0;'
16814 initEvents : function()
16817 throw "can not find store for calendar";
16823 style: "text-align:center",
16827 style: "background-color:white;width:50%;margin:250 auto",
16831 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16842 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16844 var size = this.el.select('.fc-content', true).first().getSize();
16845 this.maskEl.setSize(size.width, size.height);
16846 this.maskEl.enableDisplayMode("block");
16847 if(!this.loadMask){
16848 this.maskEl.hide();
16851 this.store = Roo.factory(this.store, Roo.data);
16852 this.store.on('load', this.onLoad, this);
16853 this.store.on('beforeload', this.onBeforeLoad, this);
16857 this.cells = this.el.select('.fc-day',true);
16858 //Roo.log(this.cells);
16859 this.textNodes = this.el.query('.fc-day-number');
16860 this.cells.addClassOnOver('fc-state-hover');
16862 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16863 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16864 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16865 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16867 this.on('monthchange', this.onMonthChange, this);
16869 this.update(new Date().clearTime());
16872 resize : function() {
16873 var sz = this.el.getSize();
16875 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16876 this.el.select('.fc-day-content div',true).setHeight(34);
16881 showPrevMonth : function(e){
16882 this.update(this.activeDate.add("mo", -1));
16884 showToday : function(e){
16885 this.update(new Date().clearTime());
16888 showNextMonth : function(e){
16889 this.update(this.activeDate.add("mo", 1));
16893 showPrevYear : function(){
16894 this.update(this.activeDate.add("y", -1));
16898 showNextYear : function(){
16899 this.update(this.activeDate.add("y", 1));
16904 update : function(date)
16906 var vd = this.activeDate;
16907 this.activeDate = date;
16908 // if(vd && this.el){
16909 // var t = date.getTime();
16910 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16911 // Roo.log('using add remove');
16913 // this.fireEvent('monthchange', this, date);
16915 // this.cells.removeClass("fc-state-highlight");
16916 // this.cells.each(function(c){
16917 // if(c.dateValue == t){
16918 // c.addClass("fc-state-highlight");
16919 // setTimeout(function(){
16920 // try{c.dom.firstChild.focus();}catch(e){}
16930 var days = date.getDaysInMonth();
16932 var firstOfMonth = date.getFirstDateOfMonth();
16933 var startingPos = firstOfMonth.getDay()-this.startDay;
16935 if(startingPos < this.startDay){
16939 var pm = date.add(Date.MONTH, -1);
16940 var prevStart = pm.getDaysInMonth()-startingPos;
16942 this.cells = this.el.select('.fc-day',true);
16943 this.textNodes = this.el.query('.fc-day-number');
16944 this.cells.addClassOnOver('fc-state-hover');
16946 var cells = this.cells.elements;
16947 var textEls = this.textNodes;
16949 Roo.each(cells, function(cell){
16950 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16953 days += startingPos;
16955 // convert everything to numbers so it's fast
16956 var day = 86400000;
16957 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16960 //Roo.log(prevStart);
16962 var today = new Date().clearTime().getTime();
16963 var sel = date.clearTime().getTime();
16964 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16965 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16966 var ddMatch = this.disabledDatesRE;
16967 var ddText = this.disabledDatesText;
16968 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16969 var ddaysText = this.disabledDaysText;
16970 var format = this.format;
16972 var setCellClass = function(cal, cell){
16976 //Roo.log('set Cell Class');
16978 var t = d.getTime();
16982 cell.dateValue = t;
16984 cell.className += " fc-today";
16985 cell.className += " fc-state-highlight";
16986 cell.title = cal.todayText;
16989 // disable highlight in other month..
16990 //cell.className += " fc-state-highlight";
16995 cell.className = " fc-state-disabled";
16996 cell.title = cal.minText;
17000 cell.className = " fc-state-disabled";
17001 cell.title = cal.maxText;
17005 if(ddays.indexOf(d.getDay()) != -1){
17006 cell.title = ddaysText;
17007 cell.className = " fc-state-disabled";
17010 if(ddMatch && format){
17011 var fvalue = d.dateFormat(format);
17012 if(ddMatch.test(fvalue)){
17013 cell.title = ddText.replace("%0", fvalue);
17014 cell.className = " fc-state-disabled";
17018 if (!cell.initialClassName) {
17019 cell.initialClassName = cell.dom.className;
17022 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17027 for(; i < startingPos; i++) {
17028 textEls[i].innerHTML = (++prevStart);
17029 d.setDate(d.getDate()+1);
17031 cells[i].className = "fc-past fc-other-month";
17032 setCellClass(this, cells[i]);
17037 for(; i < days; i++){
17038 intDay = i - startingPos + 1;
17039 textEls[i].innerHTML = (intDay);
17040 d.setDate(d.getDate()+1);
17042 cells[i].className = ''; // "x-date-active";
17043 setCellClass(this, cells[i]);
17047 for(; i < 42; i++) {
17048 textEls[i].innerHTML = (++extraDays);
17049 d.setDate(d.getDate()+1);
17051 cells[i].className = "fc-future fc-other-month";
17052 setCellClass(this, cells[i]);
17055 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17057 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17059 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17060 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17062 if(totalRows != 6){
17063 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17064 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17067 this.fireEvent('monthchange', this, date);
17071 if(!this.internalRender){
17072 var main = this.el.dom.firstChild;
17073 var w = main.offsetWidth;
17074 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17075 Roo.fly(main).setWidth(w);
17076 this.internalRender = true;
17077 // opera does not respect the auto grow header center column
17078 // then, after it gets a width opera refuses to recalculate
17079 // without a second pass
17080 if(Roo.isOpera && !this.secondPass){
17081 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17082 this.secondPass = true;
17083 this.update.defer(10, this, [date]);
17090 findCell : function(dt) {
17091 dt = dt.clearTime().getTime();
17093 this.cells.each(function(c){
17094 //Roo.log("check " +c.dateValue + '?=' + dt);
17095 if(c.dateValue == dt){
17105 findCells : function(ev) {
17106 var s = ev.start.clone().clearTime().getTime();
17108 var e= ev.end.clone().clearTime().getTime();
17111 this.cells.each(function(c){
17112 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17114 if(c.dateValue > e){
17117 if(c.dateValue < s){
17126 // findBestRow: function(cells)
17130 // for (var i =0 ; i < cells.length;i++) {
17131 // ret = Math.max(cells[i].rows || 0,ret);
17138 addItem : function(ev)
17140 // look for vertical location slot in
17141 var cells = this.findCells(ev);
17143 // ev.row = this.findBestRow(cells);
17145 // work out the location.
17149 for(var i =0; i < cells.length; i++) {
17151 cells[i].row = cells[0].row;
17154 cells[i].row = cells[i].row + 1;
17164 if (crow.start.getY() == cells[i].getY()) {
17166 crow.end = cells[i];
17183 cells[0].events.push(ev);
17185 this.calevents.push(ev);
17188 clearEvents: function() {
17190 if(!this.calevents){
17194 Roo.each(this.cells.elements, function(c){
17200 Roo.each(this.calevents, function(e) {
17201 Roo.each(e.els, function(el) {
17202 el.un('mouseenter' ,this.onEventEnter, this);
17203 el.un('mouseleave' ,this.onEventLeave, this);
17208 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17214 renderEvents: function()
17218 this.cells.each(function(c) {
17227 if(c.row != c.events.length){
17228 r = 4 - (4 - (c.row - c.events.length));
17231 c.events = ev.slice(0, r);
17232 c.more = ev.slice(r);
17234 if(c.more.length && c.more.length == 1){
17235 c.events.push(c.more.pop());
17238 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17242 this.cells.each(function(c) {
17244 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17247 for (var e = 0; e < c.events.length; e++){
17248 var ev = c.events[e];
17249 var rows = ev.rows;
17251 for(var i = 0; i < rows.length; i++) {
17253 // how many rows should it span..
17256 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17257 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17259 unselectable : "on",
17262 cls: 'fc-event-inner',
17266 // cls: 'fc-event-time',
17267 // html : cells.length > 1 ? '' : ev.time
17271 cls: 'fc-event-title',
17272 html : String.format('{0}', ev.title)
17279 cls: 'ui-resizable-handle ui-resizable-e',
17280 html : '  '
17287 cfg.cls += ' fc-event-start';
17289 if ((i+1) == rows.length) {
17290 cfg.cls += ' fc-event-end';
17293 var ctr = _this.el.select('.fc-event-container',true).first();
17294 var cg = ctr.createChild(cfg);
17296 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17297 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17299 var r = (c.more.length) ? 1 : 0;
17300 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17301 cg.setWidth(ebox.right - sbox.x -2);
17303 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17304 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17305 cg.on('click', _this.onEventClick, _this, ev);
17316 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17317 style : 'position: absolute',
17318 unselectable : "on",
17321 cls: 'fc-event-inner',
17325 cls: 'fc-event-title',
17333 cls: 'ui-resizable-handle ui-resizable-e',
17334 html : '  '
17340 var ctr = _this.el.select('.fc-event-container',true).first();
17341 var cg = ctr.createChild(cfg);
17343 var sbox = c.select('.fc-day-content',true).first().getBox();
17344 var ebox = c.select('.fc-day-content',true).first().getBox();
17346 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17347 cg.setWidth(ebox.right - sbox.x -2);
17349 cg.on('click', _this.onMoreEventClick, _this, c.more);
17359 onEventEnter: function (e, el,event,d) {
17360 this.fireEvent('evententer', this, el, event);
17363 onEventLeave: function (e, el,event,d) {
17364 this.fireEvent('eventleave', this, el, event);
17367 onEventClick: function (e, el,event,d) {
17368 this.fireEvent('eventclick', this, el, event);
17371 onMonthChange: function () {
17375 onMoreEventClick: function(e, el, more)
17379 this.calpopover.placement = 'right';
17380 this.calpopover.setTitle('More');
17382 this.calpopover.setContent('');
17384 var ctr = this.calpopover.el.select('.popover-content', true).first();
17386 Roo.each(more, function(m){
17388 cls : 'fc-event-hori fc-event-draggable',
17391 var cg = ctr.createChild(cfg);
17393 cg.on('click', _this.onEventClick, _this, m);
17396 this.calpopover.show(el);
17401 onLoad: function ()
17403 this.calevents = [];
17406 if(this.store.getCount() > 0){
17407 this.store.data.each(function(d){
17410 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17411 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17412 time : d.data.start_time,
17413 title : d.data.title,
17414 description : d.data.description,
17415 venue : d.data.venue
17420 this.renderEvents();
17422 if(this.calevents.length && this.loadMask){
17423 this.maskEl.hide();
17427 onBeforeLoad: function()
17429 this.clearEvents();
17431 this.maskEl.show();
17445 * @class Roo.bootstrap.Popover
17446 * @extends Roo.bootstrap.Component
17447 * Bootstrap Popover class
17448 * @cfg {String} html contents of the popover (or false to use children..)
17449 * @cfg {String} title of popover (or false to hide)
17450 * @cfg {String} placement how it is placed
17451 * @cfg {String} trigger click || hover (or false to trigger manually)
17452 * @cfg {String} over what (parent or false to trigger manually.)
17453 * @cfg {Number} delay - delay before showing
17456 * Create a new Popover
17457 * @param {Object} config The config object
17460 Roo.bootstrap.Popover = function(config){
17461 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17467 * After the popover show
17469 * @param {Roo.bootstrap.Popover} this
17474 * After the popover hide
17476 * @param {Roo.bootstrap.Popover} this
17482 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17484 title: 'Fill in a title',
17487 placement : 'right',
17488 trigger : 'hover', // hover
17494 can_build_overlaid : false,
17496 getChildContainer : function()
17498 return this.el.select('.popover-content',true).first();
17501 getAutoCreate : function(){
17504 cls : 'popover roo-dynamic',
17505 style: 'display:block',
17511 cls : 'popover-inner',
17515 cls: 'popover-title',
17519 cls : 'popover-content',
17530 setTitle: function(str)
17533 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17535 setContent: function(str)
17538 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17540 // as it get's added to the bottom of the page.
17541 onRender : function(ct, position)
17543 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17545 var cfg = Roo.apply({}, this.getAutoCreate());
17549 cfg.cls += ' ' + this.cls;
17552 cfg.style = this.style;
17554 //Roo.log("adding to ");
17555 this.el = Roo.get(document.body).createChild(cfg, position);
17556 // Roo.log(this.el);
17561 initEvents : function()
17563 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17564 this.el.enableDisplayMode('block');
17566 if (this.over === false) {
17569 if (this.triggers === false) {
17572 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17573 var triggers = this.trigger ? this.trigger.split(' ') : [];
17574 Roo.each(triggers, function(trigger) {
17576 if (trigger == 'click') {
17577 on_el.on('click', this.toggle, this);
17578 } else if (trigger != 'manual') {
17579 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17580 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17582 on_el.on(eventIn ,this.enter, this);
17583 on_el.on(eventOut, this.leave, this);
17594 toggle : function () {
17595 this.hoverState == 'in' ? this.leave() : this.enter();
17598 enter : function () {
17600 clearTimeout(this.timeout);
17602 this.hoverState = 'in';
17604 if (!this.delay || !this.delay.show) {
17609 this.timeout = setTimeout(function () {
17610 if (_t.hoverState == 'in') {
17613 }, this.delay.show)
17616 leave : function() {
17617 clearTimeout(this.timeout);
17619 this.hoverState = 'out';
17621 if (!this.delay || !this.delay.hide) {
17626 this.timeout = setTimeout(function () {
17627 if (_t.hoverState == 'out') {
17630 }, this.delay.hide)
17633 show : function (on_el)
17636 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17640 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17641 if (this.html !== false) {
17642 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17644 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17645 if (!this.title.length) {
17646 this.el.select('.popover-title',true).hide();
17649 var placement = typeof this.placement == 'function' ?
17650 this.placement.call(this, this.el, on_el) :
17653 var autoToken = /\s?auto?\s?/i;
17654 var autoPlace = autoToken.test(placement);
17656 placement = placement.replace(autoToken, '') || 'top';
17660 //this.el.setXY([0,0]);
17662 this.el.dom.style.display='block';
17663 this.el.addClass(placement);
17665 //this.el.appendTo(on_el);
17667 var p = this.getPosition();
17668 var box = this.el.getBox();
17673 var align = Roo.bootstrap.Popover.alignment[placement];
17676 this.el.alignTo(on_el, align[0],align[1]);
17677 //var arrow = this.el.select('.arrow',true).first();
17678 //arrow.set(align[2],
17680 this.el.addClass('in');
17683 if (this.el.hasClass('fade')) {
17687 this.hoverState = 'in';
17689 this.fireEvent('show', this);
17694 this.el.setXY([0,0]);
17695 this.el.removeClass('in');
17697 this.hoverState = null;
17699 this.fireEvent('hide', this);
17704 Roo.bootstrap.Popover.alignment = {
17705 'left' : ['r-l', [-10,0], 'right'],
17706 'right' : ['l-r', [10,0], 'left'],
17707 'bottom' : ['t-b', [0,10], 'top'],
17708 'top' : [ 'b-t', [0,-10], 'bottom']
17719 * @class Roo.bootstrap.Progress
17720 * @extends Roo.bootstrap.Component
17721 * Bootstrap Progress class
17722 * @cfg {Boolean} striped striped of the progress bar
17723 * @cfg {Boolean} active animated of the progress bar
17727 * Create a new Progress
17728 * @param {Object} config The config object
17731 Roo.bootstrap.Progress = function(config){
17732 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17735 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17740 getAutoCreate : function(){
17748 cfg.cls += ' progress-striped';
17752 cfg.cls += ' active';
17771 * @class Roo.bootstrap.ProgressBar
17772 * @extends Roo.bootstrap.Component
17773 * Bootstrap ProgressBar class
17774 * @cfg {Number} aria_valuenow aria-value now
17775 * @cfg {Number} aria_valuemin aria-value min
17776 * @cfg {Number} aria_valuemax aria-value max
17777 * @cfg {String} label label for the progress bar
17778 * @cfg {String} panel (success | info | warning | danger )
17779 * @cfg {String} role role of the progress bar
17780 * @cfg {String} sr_only text
17784 * Create a new ProgressBar
17785 * @param {Object} config The config object
17788 Roo.bootstrap.ProgressBar = function(config){
17789 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17792 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17796 aria_valuemax : 100,
17802 getAutoCreate : function()
17807 cls: 'progress-bar',
17808 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17820 cfg.role = this.role;
17823 if(this.aria_valuenow){
17824 cfg['aria-valuenow'] = this.aria_valuenow;
17827 if(this.aria_valuemin){
17828 cfg['aria-valuemin'] = this.aria_valuemin;
17831 if(this.aria_valuemax){
17832 cfg['aria-valuemax'] = this.aria_valuemax;
17835 if(this.label && !this.sr_only){
17836 cfg.html = this.label;
17840 cfg.cls += ' progress-bar-' + this.panel;
17846 update : function(aria_valuenow)
17848 this.aria_valuenow = aria_valuenow;
17850 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17865 * @class Roo.bootstrap.TabGroup
17866 * @extends Roo.bootstrap.Column
17867 * Bootstrap Column class
17868 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17869 * @cfg {Boolean} carousel true to make the group behave like a carousel
17870 * @cfg {Boolean} bullets show bullets for the panels
17871 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17872 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17873 * @cfg {Boolean} showarrow (true|false) show arrow default true
17876 * Create a new TabGroup
17877 * @param {Object} config The config object
17880 Roo.bootstrap.TabGroup = function(config){
17881 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17883 this.navId = Roo.id();
17886 Roo.bootstrap.TabGroup.register(this);
17890 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17893 transition : false,
17898 slideOnTouch : false,
17901 getAutoCreate : function()
17903 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17905 cfg.cls += ' tab-content';
17907 if (this.carousel) {
17908 cfg.cls += ' carousel slide';
17911 cls : 'carousel-inner',
17915 if(this.bullets && !Roo.isTouch){
17918 cls : 'carousel-bullets',
17922 if(this.bullets_cls){
17923 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17930 cfg.cn[0].cn.push(bullets);
17933 if(this.showarrow){
17934 cfg.cn[0].cn.push({
17936 class : 'carousel-arrow',
17940 class : 'carousel-prev',
17944 class : 'fa fa-chevron-left'
17950 class : 'carousel-next',
17954 class : 'fa fa-chevron-right'
17967 initEvents: function()
17969 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17970 // this.el.on("touchstart", this.onTouchStart, this);
17973 if(this.autoslide){
17976 this.slideFn = window.setInterval(function() {
17977 _this.showPanelNext();
17981 if(this.showarrow){
17982 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17983 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17989 // onTouchStart : function(e, el, o)
17991 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17995 // this.showPanelNext();
17999 getChildContainer : function()
18001 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18005 * register a Navigation item
18006 * @param {Roo.bootstrap.NavItem} the navitem to add
18008 register : function(item)
18010 this.tabs.push( item);
18011 item.navId = this.navId; // not really needed..
18016 getActivePanel : function()
18019 Roo.each(this.tabs, function(t) {
18029 getPanelByName : function(n)
18032 Roo.each(this.tabs, function(t) {
18033 if (t.tabId == n) {
18041 indexOfPanel : function(p)
18044 Roo.each(this.tabs, function(t,i) {
18045 if (t.tabId == p.tabId) {
18054 * show a specific panel
18055 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18056 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18058 showPanel : function (pan)
18060 if(this.transition || typeof(pan) == 'undefined'){
18061 Roo.log("waiting for the transitionend");
18065 if (typeof(pan) == 'number') {
18066 pan = this.tabs[pan];
18069 if (typeof(pan) == 'string') {
18070 pan = this.getPanelByName(pan);
18073 var cur = this.getActivePanel();
18076 Roo.log('pan or acitve pan is undefined');
18080 if (pan.tabId == this.getActivePanel().tabId) {
18084 if (false === cur.fireEvent('beforedeactivate')) {
18088 if(this.bullets > 0 && !Roo.isTouch){
18089 this.setActiveBullet(this.indexOfPanel(pan));
18092 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18094 this.transition = true;
18095 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18096 var lr = dir == 'next' ? 'left' : 'right';
18097 pan.el.addClass(dir); // or prev
18098 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18099 cur.el.addClass(lr); // or right
18100 pan.el.addClass(lr);
18103 cur.el.on('transitionend', function() {
18104 Roo.log("trans end?");
18106 pan.el.removeClass([lr,dir]);
18107 pan.setActive(true);
18109 cur.el.removeClass([lr]);
18110 cur.setActive(false);
18112 _this.transition = false;
18114 }, this, { single: true } );
18119 cur.setActive(false);
18120 pan.setActive(true);
18125 showPanelNext : function()
18127 var i = this.indexOfPanel(this.getActivePanel());
18129 if (i >= this.tabs.length - 1 && !this.autoslide) {
18133 if (i >= this.tabs.length - 1 && this.autoslide) {
18137 this.showPanel(this.tabs[i+1]);
18140 showPanelPrev : function()
18142 var i = this.indexOfPanel(this.getActivePanel());
18144 if (i < 1 && !this.autoslide) {
18148 if (i < 1 && this.autoslide) {
18149 i = this.tabs.length;
18152 this.showPanel(this.tabs[i-1]);
18156 addBullet: function()
18158 if(!this.bullets || Roo.isTouch){
18161 var ctr = this.el.select('.carousel-bullets',true).first();
18162 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18163 var bullet = ctr.createChild({
18164 cls : 'bullet bullet-' + i
18165 },ctr.dom.lastChild);
18170 bullet.on('click', (function(e, el, o, ii, t){
18172 e.preventDefault();
18174 this.showPanel(ii);
18176 if(this.autoslide && this.slideFn){
18177 clearInterval(this.slideFn);
18178 this.slideFn = window.setInterval(function() {
18179 _this.showPanelNext();
18183 }).createDelegate(this, [i, bullet], true));
18188 setActiveBullet : function(i)
18194 Roo.each(this.el.select('.bullet', true).elements, function(el){
18195 el.removeClass('selected');
18198 var bullet = this.el.select('.bullet-' + i, true).first();
18204 bullet.addClass('selected');
18215 Roo.apply(Roo.bootstrap.TabGroup, {
18219 * register a Navigation Group
18220 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18222 register : function(navgrp)
18224 this.groups[navgrp.navId] = navgrp;
18228 * fetch a Navigation Group based on the navigation ID
18229 * if one does not exist , it will get created.
18230 * @param {string} the navgroup to add
18231 * @returns {Roo.bootstrap.NavGroup} the navgroup
18233 get: function(navId) {
18234 if (typeof(this.groups[navId]) == 'undefined') {
18235 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18237 return this.groups[navId] ;
18252 * @class Roo.bootstrap.TabPanel
18253 * @extends Roo.bootstrap.Component
18254 * Bootstrap TabPanel class
18255 * @cfg {Boolean} active panel active
18256 * @cfg {String} html panel content
18257 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18258 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18259 * @cfg {String} href click to link..
18263 * Create a new TabPanel
18264 * @param {Object} config The config object
18267 Roo.bootstrap.TabPanel = function(config){
18268 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18272 * Fires when the active status changes
18273 * @param {Roo.bootstrap.TabPanel} this
18274 * @param {Boolean} state the new state
18279 * @event beforedeactivate
18280 * Fires before a tab is de-activated - can be used to do validation on a form.
18281 * @param {Roo.bootstrap.TabPanel} this
18282 * @return {Boolean} false if there is an error
18285 'beforedeactivate': true
18288 this.tabId = this.tabId || Roo.id();
18292 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18300 getAutoCreate : function(){
18303 // item is needed for carousel - not sure if it has any effect otherwise
18304 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18305 html: this.html || ''
18309 cfg.cls += ' active';
18313 cfg.tabId = this.tabId;
18320 initEvents: function()
18322 var p = this.parent();
18324 this.navId = this.navId || p.navId;
18326 if (typeof(this.navId) != 'undefined') {
18327 // not really needed.. but just in case.. parent should be a NavGroup.
18328 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18332 var i = tg.tabs.length - 1;
18334 if(this.active && tg.bullets > 0 && i < tg.bullets){
18335 tg.setActiveBullet(i);
18339 this.el.on('click', this.onClick, this);
18342 this.el.on("touchstart", this.onTouchStart, this);
18343 this.el.on("touchmove", this.onTouchMove, this);
18344 this.el.on("touchend", this.onTouchEnd, this);
18349 onRender : function(ct, position)
18351 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18354 setActive : function(state)
18356 Roo.log("panel - set active " + this.tabId + "=" + state);
18358 this.active = state;
18360 this.el.removeClass('active');
18362 } else if (!this.el.hasClass('active')) {
18363 this.el.addClass('active');
18366 this.fireEvent('changed', this, state);
18369 onClick : function(e)
18371 e.preventDefault();
18373 if(!this.href.length){
18377 window.location.href = this.href;
18386 onTouchStart : function(e)
18388 this.swiping = false;
18390 this.startX = e.browserEvent.touches[0].clientX;
18391 this.startY = e.browserEvent.touches[0].clientY;
18394 onTouchMove : function(e)
18396 this.swiping = true;
18398 this.endX = e.browserEvent.touches[0].clientX;
18399 this.endY = e.browserEvent.touches[0].clientY;
18402 onTouchEnd : function(e)
18409 var tabGroup = this.parent();
18411 if(this.endX > this.startX){ // swiping right
18412 tabGroup.showPanelPrev();
18416 if(this.startX > this.endX){ // swiping left
18417 tabGroup.showPanelNext();
18436 * @class Roo.bootstrap.DateField
18437 * @extends Roo.bootstrap.Input
18438 * Bootstrap DateField class
18439 * @cfg {Number} weekStart default 0
18440 * @cfg {String} viewMode default empty, (months|years)
18441 * @cfg {String} minViewMode default empty, (months|years)
18442 * @cfg {Number} startDate default -Infinity
18443 * @cfg {Number} endDate default Infinity
18444 * @cfg {Boolean} todayHighlight default false
18445 * @cfg {Boolean} todayBtn default false
18446 * @cfg {Boolean} calendarWeeks default false
18447 * @cfg {Object} daysOfWeekDisabled default empty
18448 * @cfg {Boolean} singleMode default false (true | false)
18450 * @cfg {Boolean} keyboardNavigation default true
18451 * @cfg {String} language default en
18454 * Create a new DateField
18455 * @param {Object} config The config object
18458 Roo.bootstrap.DateField = function(config){
18459 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18463 * Fires when this field show.
18464 * @param {Roo.bootstrap.DateField} this
18465 * @param {Mixed} date The date value
18470 * Fires when this field hide.
18471 * @param {Roo.bootstrap.DateField} this
18472 * @param {Mixed} date The date value
18477 * Fires when select a date.
18478 * @param {Roo.bootstrap.DateField} this
18479 * @param {Mixed} date The date value
18483 * @event beforeselect
18484 * Fires when before select a date.
18485 * @param {Roo.bootstrap.DateField} this
18486 * @param {Mixed} date The date value
18488 beforeselect : true
18492 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18495 * @cfg {String} format
18496 * The default date format string which can be overriden for localization support. The format must be
18497 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18501 * @cfg {String} altFormats
18502 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18503 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18505 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18513 todayHighlight : false,
18519 keyboardNavigation: true,
18521 calendarWeeks: false,
18523 startDate: -Infinity,
18527 daysOfWeekDisabled: [],
18531 singleMode : false,
18533 UTCDate: function()
18535 return new Date(Date.UTC.apply(Date, arguments));
18538 UTCToday: function()
18540 var today = new Date();
18541 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18544 getDate: function() {
18545 var d = this.getUTCDate();
18546 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18549 getUTCDate: function() {
18553 setDate: function(d) {
18554 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18557 setUTCDate: function(d) {
18559 this.setValue(this.formatDate(this.date));
18562 onRender: function(ct, position)
18565 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18567 this.language = this.language || 'en';
18568 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18569 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18571 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18572 this.format = this.format || 'm/d/y';
18573 this.isInline = false;
18574 this.isInput = true;
18575 this.component = this.el.select('.add-on', true).first() || false;
18576 this.component = (this.component && this.component.length === 0) ? false : this.component;
18577 this.hasInput = this.component && this.inputEl().length;
18579 if (typeof(this.minViewMode === 'string')) {
18580 switch (this.minViewMode) {
18582 this.minViewMode = 1;
18585 this.minViewMode = 2;
18588 this.minViewMode = 0;
18593 if (typeof(this.viewMode === 'string')) {
18594 switch (this.viewMode) {
18607 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18609 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18611 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18613 this.picker().on('mousedown', this.onMousedown, this);
18614 this.picker().on('click', this.onClick, this);
18616 this.picker().addClass('datepicker-dropdown');
18618 this.startViewMode = this.viewMode;
18620 if(this.singleMode){
18621 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18622 v.setVisibilityMode(Roo.Element.DISPLAY);
18626 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18627 v.setStyle('width', '189px');
18631 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18632 if(!this.calendarWeeks){
18637 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18638 v.attr('colspan', function(i, val){
18639 return parseInt(val) + 1;
18644 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18646 this.setStartDate(this.startDate);
18647 this.setEndDate(this.endDate);
18649 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18656 if(this.isInline) {
18661 picker : function()
18663 return this.pickerEl;
18664 // return this.el.select('.datepicker', true).first();
18667 fillDow: function()
18669 var dowCnt = this.weekStart;
18678 if(this.calendarWeeks){
18686 while (dowCnt < this.weekStart + 7) {
18690 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18694 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18697 fillMonths: function()
18700 var months = this.picker().select('>.datepicker-months td', true).first();
18702 months.dom.innerHTML = '';
18708 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18711 months.createChild(month);
18718 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;
18720 if (this.date < this.startDate) {
18721 this.viewDate = new Date(this.startDate);
18722 } else if (this.date > this.endDate) {
18723 this.viewDate = new Date(this.endDate);
18725 this.viewDate = new Date(this.date);
18733 var d = new Date(this.viewDate),
18734 year = d.getUTCFullYear(),
18735 month = d.getUTCMonth(),
18736 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18737 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18738 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18739 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18740 currentDate = this.date && this.date.valueOf(),
18741 today = this.UTCToday();
18743 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18745 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18747 // this.picker.select('>tfoot th.today').
18748 // .text(dates[this.language].today)
18749 // .toggle(this.todayBtn !== false);
18751 this.updateNavArrows();
18754 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18756 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18758 prevMonth.setUTCDate(day);
18760 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18762 var nextMonth = new Date(prevMonth);
18764 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18766 nextMonth = nextMonth.valueOf();
18768 var fillMonths = false;
18770 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18772 while(prevMonth.valueOf() <= nextMonth) {
18775 if (prevMonth.getUTCDay() === this.weekStart) {
18777 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18785 if(this.calendarWeeks){
18786 // ISO 8601: First week contains first thursday.
18787 // ISO also states week starts on Monday, but we can be more abstract here.
18789 // Start of current week: based on weekstart/current date
18790 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18791 // Thursday of this week
18792 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18793 // First Thursday of year, year from thursday
18794 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18795 // Calendar week: ms between thursdays, div ms per day, div 7 days
18796 calWeek = (th - yth) / 864e5 / 7 + 1;
18798 fillMonths.cn.push({
18806 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18808 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18811 if (this.todayHighlight &&
18812 prevMonth.getUTCFullYear() == today.getFullYear() &&
18813 prevMonth.getUTCMonth() == today.getMonth() &&
18814 prevMonth.getUTCDate() == today.getDate()) {
18815 clsName += ' today';
18818 if (currentDate && prevMonth.valueOf() === currentDate) {
18819 clsName += ' active';
18822 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18823 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18824 clsName += ' disabled';
18827 fillMonths.cn.push({
18829 cls: 'day ' + clsName,
18830 html: prevMonth.getDate()
18833 prevMonth.setDate(prevMonth.getDate()+1);
18836 var currentYear = this.date && this.date.getUTCFullYear();
18837 var currentMonth = this.date && this.date.getUTCMonth();
18839 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18841 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18842 v.removeClass('active');
18844 if(currentYear === year && k === currentMonth){
18845 v.addClass('active');
18848 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18849 v.addClass('disabled');
18855 year = parseInt(year/10, 10) * 10;
18857 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18859 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18862 for (var i = -1; i < 11; i++) {
18863 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18865 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18873 showMode: function(dir)
18876 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18879 Roo.each(this.picker().select('>div',true).elements, function(v){
18880 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18883 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18888 if(this.isInline) {
18892 this.picker().removeClass(['bottom', 'top']);
18894 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18896 * place to the top of element!
18900 this.picker().addClass('top');
18901 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18906 this.picker().addClass('bottom');
18908 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18911 parseDate : function(value)
18913 if(!value || value instanceof Date){
18916 var v = Date.parseDate(value, this.format);
18917 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18918 v = Date.parseDate(value, 'Y-m-d');
18920 if(!v && this.altFormats){
18921 if(!this.altFormatsArray){
18922 this.altFormatsArray = this.altFormats.split("|");
18924 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18925 v = Date.parseDate(value, this.altFormatsArray[i]);
18931 formatDate : function(date, fmt)
18933 return (!date || !(date instanceof Date)) ?
18934 date : date.dateFormat(fmt || this.format);
18937 onFocus : function()
18939 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18943 onBlur : function()
18945 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18947 var d = this.inputEl().getValue();
18954 showPopup : function()
18956 this.picker().show();
18960 this.fireEvent('showpopup', this, this.date);
18963 hidePopup : function()
18965 if(this.isInline) {
18968 this.picker().hide();
18969 this.viewMode = this.startViewMode;
18972 this.fireEvent('hidepopup', this, this.date);
18976 onMousedown: function(e)
18978 e.stopPropagation();
18979 e.preventDefault();
18984 Roo.bootstrap.DateField.superclass.keyup.call(this);
18988 setValue: function(v)
18990 if(this.fireEvent('beforeselect', this, v) !== false){
18991 var d = new Date(this.parseDate(v) ).clearTime();
18993 if(isNaN(d.getTime())){
18994 this.date = this.viewDate = '';
18995 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18999 v = this.formatDate(d);
19001 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19003 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19007 this.fireEvent('select', this, this.date);
19011 getValue: function()
19013 return this.formatDate(this.date);
19016 fireKey: function(e)
19018 if (!this.picker().isVisible()){
19019 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19025 var dateChanged = false,
19027 newDate, newViewDate;
19032 e.preventDefault();
19036 if (!this.keyboardNavigation) {
19039 dir = e.keyCode == 37 ? -1 : 1;
19042 newDate = this.moveYear(this.date, dir);
19043 newViewDate = this.moveYear(this.viewDate, dir);
19044 } else if (e.shiftKey){
19045 newDate = this.moveMonth(this.date, dir);
19046 newViewDate = this.moveMonth(this.viewDate, dir);
19048 newDate = new Date(this.date);
19049 newDate.setUTCDate(this.date.getUTCDate() + dir);
19050 newViewDate = new Date(this.viewDate);
19051 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19053 if (this.dateWithinRange(newDate)){
19054 this.date = newDate;
19055 this.viewDate = newViewDate;
19056 this.setValue(this.formatDate(this.date));
19058 e.preventDefault();
19059 dateChanged = true;
19064 if (!this.keyboardNavigation) {
19067 dir = e.keyCode == 38 ? -1 : 1;
19069 newDate = this.moveYear(this.date, dir);
19070 newViewDate = this.moveYear(this.viewDate, dir);
19071 } else if (e.shiftKey){
19072 newDate = this.moveMonth(this.date, dir);
19073 newViewDate = this.moveMonth(this.viewDate, dir);
19075 newDate = new Date(this.date);
19076 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19077 newViewDate = new Date(this.viewDate);
19078 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19080 if (this.dateWithinRange(newDate)){
19081 this.date = newDate;
19082 this.viewDate = newViewDate;
19083 this.setValue(this.formatDate(this.date));
19085 e.preventDefault();
19086 dateChanged = true;
19090 this.setValue(this.formatDate(this.date));
19092 e.preventDefault();
19095 this.setValue(this.formatDate(this.date));
19109 onClick: function(e)
19111 e.stopPropagation();
19112 e.preventDefault();
19114 var target = e.getTarget();
19116 if(target.nodeName.toLowerCase() === 'i'){
19117 target = Roo.get(target).dom.parentNode;
19120 var nodeName = target.nodeName;
19121 var className = target.className;
19122 var html = target.innerHTML;
19123 //Roo.log(nodeName);
19125 switch(nodeName.toLowerCase()) {
19127 switch(className) {
19133 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19134 switch(this.viewMode){
19136 this.viewDate = this.moveMonth(this.viewDate, dir);
19140 this.viewDate = this.moveYear(this.viewDate, dir);
19146 var date = new Date();
19147 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19149 this.setValue(this.formatDate(this.date));
19156 if (className.indexOf('disabled') < 0) {
19157 this.viewDate.setUTCDate(1);
19158 if (className.indexOf('month') > -1) {
19159 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19161 var year = parseInt(html, 10) || 0;
19162 this.viewDate.setUTCFullYear(year);
19166 if(this.singleMode){
19167 this.setValue(this.formatDate(this.viewDate));
19178 //Roo.log(className);
19179 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19180 var day = parseInt(html, 10) || 1;
19181 var year = this.viewDate.getUTCFullYear(),
19182 month = this.viewDate.getUTCMonth();
19184 if (className.indexOf('old') > -1) {
19191 } else if (className.indexOf('new') > -1) {
19199 //Roo.log([year,month,day]);
19200 this.date = this.UTCDate(year, month, day,0,0,0,0);
19201 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19203 //Roo.log(this.formatDate(this.date));
19204 this.setValue(this.formatDate(this.date));
19211 setStartDate: function(startDate)
19213 this.startDate = startDate || -Infinity;
19214 if (this.startDate !== -Infinity) {
19215 this.startDate = this.parseDate(this.startDate);
19218 this.updateNavArrows();
19221 setEndDate: function(endDate)
19223 this.endDate = endDate || Infinity;
19224 if (this.endDate !== Infinity) {
19225 this.endDate = this.parseDate(this.endDate);
19228 this.updateNavArrows();
19231 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19233 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19234 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19235 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19237 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19238 return parseInt(d, 10);
19241 this.updateNavArrows();
19244 updateNavArrows: function()
19246 if(this.singleMode){
19250 var d = new Date(this.viewDate),
19251 year = d.getUTCFullYear(),
19252 month = d.getUTCMonth();
19254 Roo.each(this.picker().select('.prev', true).elements, function(v){
19256 switch (this.viewMode) {
19259 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19265 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19272 Roo.each(this.picker().select('.next', true).elements, function(v){
19274 switch (this.viewMode) {
19277 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19283 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19291 moveMonth: function(date, dir)
19296 var new_date = new Date(date.valueOf()),
19297 day = new_date.getUTCDate(),
19298 month = new_date.getUTCMonth(),
19299 mag = Math.abs(dir),
19301 dir = dir > 0 ? 1 : -1;
19304 // If going back one month, make sure month is not current month
19305 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19307 return new_date.getUTCMonth() == month;
19309 // If going forward one month, make sure month is as expected
19310 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19312 return new_date.getUTCMonth() != new_month;
19314 new_month = month + dir;
19315 new_date.setUTCMonth(new_month);
19316 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19317 if (new_month < 0 || new_month > 11) {
19318 new_month = (new_month + 12) % 12;
19321 // For magnitudes >1, move one month at a time...
19322 for (var i=0; i<mag; i++) {
19323 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19324 new_date = this.moveMonth(new_date, dir);
19326 // ...then reset the day, keeping it in the new month
19327 new_month = new_date.getUTCMonth();
19328 new_date.setUTCDate(day);
19330 return new_month != new_date.getUTCMonth();
19333 // Common date-resetting loop -- if date is beyond end of month, make it
19336 new_date.setUTCDate(--day);
19337 new_date.setUTCMonth(new_month);
19342 moveYear: function(date, dir)
19344 return this.moveMonth(date, dir*12);
19347 dateWithinRange: function(date)
19349 return date >= this.startDate && date <= this.endDate;
19355 this.picker().remove();
19358 validateValue : function(value)
19360 if(this.getVisibilityEl().hasClass('hidden')){
19364 if(value.length < 1) {
19365 if(this.allowBlank){
19371 if(value.length < this.minLength){
19374 if(value.length > this.maxLength){
19378 var vt = Roo.form.VTypes;
19379 if(!vt[this.vtype](value, this)){
19383 if(typeof this.validator == "function"){
19384 var msg = this.validator(value);
19390 if(this.regex && !this.regex.test(value)){
19394 if(typeof(this.parseDate(value)) == 'undefined'){
19398 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19402 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19412 this.date = this.viewDate = '';
19414 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19419 Roo.apply(Roo.bootstrap.DateField, {
19430 html: '<i class="fa fa-arrow-left"/>'
19440 html: '<i class="fa fa-arrow-right"/>'
19482 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19483 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19484 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19485 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19486 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19499 navFnc: 'FullYear',
19504 navFnc: 'FullYear',
19509 Roo.apply(Roo.bootstrap.DateField, {
19513 cls: 'datepicker dropdown-menu roo-dynamic',
19517 cls: 'datepicker-days',
19521 cls: 'table-condensed',
19523 Roo.bootstrap.DateField.head,
19527 Roo.bootstrap.DateField.footer
19534 cls: 'datepicker-months',
19538 cls: 'table-condensed',
19540 Roo.bootstrap.DateField.head,
19541 Roo.bootstrap.DateField.content,
19542 Roo.bootstrap.DateField.footer
19549 cls: 'datepicker-years',
19553 cls: 'table-condensed',
19555 Roo.bootstrap.DateField.head,
19556 Roo.bootstrap.DateField.content,
19557 Roo.bootstrap.DateField.footer
19576 * @class Roo.bootstrap.TimeField
19577 * @extends Roo.bootstrap.Input
19578 * Bootstrap DateField class
19582 * Create a new TimeField
19583 * @param {Object} config The config object
19586 Roo.bootstrap.TimeField = function(config){
19587 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19591 * Fires when this field show.
19592 * @param {Roo.bootstrap.DateField} thisthis
19593 * @param {Mixed} date The date value
19598 * Fires when this field hide.
19599 * @param {Roo.bootstrap.DateField} this
19600 * @param {Mixed} date The date value
19605 * Fires when select a date.
19606 * @param {Roo.bootstrap.DateField} this
19607 * @param {Mixed} date The date value
19613 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19616 * @cfg {String} format
19617 * The default time format string which can be overriden for localization support. The format must be
19618 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19622 onRender: function(ct, position)
19625 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19627 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19629 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19631 this.pop = this.picker().select('>.datepicker-time',true).first();
19632 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19634 this.picker().on('mousedown', this.onMousedown, this);
19635 this.picker().on('click', this.onClick, this);
19637 this.picker().addClass('datepicker-dropdown');
19642 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19643 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19644 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19645 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19646 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19647 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19651 fireKey: function(e){
19652 if (!this.picker().isVisible()){
19653 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19659 e.preventDefault();
19667 this.onTogglePeriod();
19670 this.onIncrementMinutes();
19673 this.onDecrementMinutes();
19682 onClick: function(e) {
19683 e.stopPropagation();
19684 e.preventDefault();
19687 picker : function()
19689 return this.el.select('.datepicker', true).first();
19692 fillTime: function()
19694 var time = this.pop.select('tbody', true).first();
19696 time.dom.innerHTML = '';
19711 cls: 'hours-up glyphicon glyphicon-chevron-up'
19731 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19752 cls: 'timepicker-hour',
19767 cls: 'timepicker-minute',
19782 cls: 'btn btn-primary period',
19804 cls: 'hours-down glyphicon glyphicon-chevron-down'
19824 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19842 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19849 var hours = this.time.getHours();
19850 var minutes = this.time.getMinutes();
19863 hours = hours - 12;
19867 hours = '0' + hours;
19871 minutes = '0' + minutes;
19874 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19875 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19876 this.pop.select('button', true).first().dom.innerHTML = period;
19882 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19884 var cls = ['bottom'];
19886 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19893 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19898 this.picker().addClass(cls.join('-'));
19902 Roo.each(cls, function(c){
19904 _this.picker().setTop(_this.inputEl().getHeight());
19908 _this.picker().setTop(0 - _this.picker().getHeight());
19913 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19917 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19924 onFocus : function()
19926 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19930 onBlur : function()
19932 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19938 this.picker().show();
19943 this.fireEvent('show', this, this.date);
19948 this.picker().hide();
19951 this.fireEvent('hide', this, this.date);
19954 setTime : function()
19957 this.setValue(this.time.format(this.format));
19959 this.fireEvent('select', this, this.date);
19964 onMousedown: function(e){
19965 e.stopPropagation();
19966 e.preventDefault();
19969 onIncrementHours: function()
19971 Roo.log('onIncrementHours');
19972 this.time = this.time.add(Date.HOUR, 1);
19977 onDecrementHours: function()
19979 Roo.log('onDecrementHours');
19980 this.time = this.time.add(Date.HOUR, -1);
19984 onIncrementMinutes: function()
19986 Roo.log('onIncrementMinutes');
19987 this.time = this.time.add(Date.MINUTE, 1);
19991 onDecrementMinutes: function()
19993 Roo.log('onDecrementMinutes');
19994 this.time = this.time.add(Date.MINUTE, -1);
19998 onTogglePeriod: function()
20000 Roo.log('onTogglePeriod');
20001 this.time = this.time.add(Date.HOUR, 12);
20008 Roo.apply(Roo.bootstrap.TimeField, {
20038 cls: 'btn btn-info ok',
20050 Roo.apply(Roo.bootstrap.TimeField, {
20054 cls: 'datepicker dropdown-menu',
20058 cls: 'datepicker-time',
20062 cls: 'table-condensed',
20064 Roo.bootstrap.TimeField.content,
20065 Roo.bootstrap.TimeField.footer
20084 * @class Roo.bootstrap.MonthField
20085 * @extends Roo.bootstrap.Input
20086 * Bootstrap MonthField class
20088 * @cfg {String} language default en
20091 * Create a new MonthField
20092 * @param {Object} config The config object
20095 Roo.bootstrap.MonthField = function(config){
20096 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20101 * Fires when this field show.
20102 * @param {Roo.bootstrap.MonthField} this
20103 * @param {Mixed} date The date value
20108 * Fires when this field hide.
20109 * @param {Roo.bootstrap.MonthField} this
20110 * @param {Mixed} date The date value
20115 * Fires when select a date.
20116 * @param {Roo.bootstrap.MonthField} this
20117 * @param {String} oldvalue The old value
20118 * @param {String} newvalue The new value
20124 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20126 onRender: function(ct, position)
20129 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20131 this.language = this.language || 'en';
20132 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20133 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20135 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20136 this.isInline = false;
20137 this.isInput = true;
20138 this.component = this.el.select('.add-on', true).first() || false;
20139 this.component = (this.component && this.component.length === 0) ? false : this.component;
20140 this.hasInput = this.component && this.inputEL().length;
20142 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20144 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20146 this.picker().on('mousedown', this.onMousedown, this);
20147 this.picker().on('click', this.onClick, this);
20149 this.picker().addClass('datepicker-dropdown');
20151 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20152 v.setStyle('width', '189px');
20159 if(this.isInline) {
20165 setValue: function(v, suppressEvent)
20167 var o = this.getValue();
20169 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20173 if(suppressEvent !== true){
20174 this.fireEvent('select', this, o, v);
20179 getValue: function()
20184 onClick: function(e)
20186 e.stopPropagation();
20187 e.preventDefault();
20189 var target = e.getTarget();
20191 if(target.nodeName.toLowerCase() === 'i'){
20192 target = Roo.get(target).dom.parentNode;
20195 var nodeName = target.nodeName;
20196 var className = target.className;
20197 var html = target.innerHTML;
20199 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20203 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20205 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20211 picker : function()
20213 return this.pickerEl;
20216 fillMonths: function()
20219 var months = this.picker().select('>.datepicker-months td', true).first();
20221 months.dom.innerHTML = '';
20227 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20230 months.createChild(month);
20239 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20240 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20243 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20244 e.removeClass('active');
20246 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20247 e.addClass('active');
20254 if(this.isInline) {
20258 this.picker().removeClass(['bottom', 'top']);
20260 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20262 * place to the top of element!
20266 this.picker().addClass('top');
20267 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20272 this.picker().addClass('bottom');
20274 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20277 onFocus : function()
20279 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20283 onBlur : function()
20285 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20287 var d = this.inputEl().getValue();
20296 this.picker().show();
20297 this.picker().select('>.datepicker-months', true).first().show();
20301 this.fireEvent('show', this, this.date);
20306 if(this.isInline) {
20309 this.picker().hide();
20310 this.fireEvent('hide', this, this.date);
20314 onMousedown: function(e)
20316 e.stopPropagation();
20317 e.preventDefault();
20322 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20326 fireKey: function(e)
20328 if (!this.picker().isVisible()){
20329 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20340 e.preventDefault();
20344 dir = e.keyCode == 37 ? -1 : 1;
20346 this.vIndex = this.vIndex + dir;
20348 if(this.vIndex < 0){
20352 if(this.vIndex > 11){
20356 if(isNaN(this.vIndex)){
20360 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20366 dir = e.keyCode == 38 ? -1 : 1;
20368 this.vIndex = this.vIndex + dir * 4;
20370 if(this.vIndex < 0){
20374 if(this.vIndex > 11){
20378 if(isNaN(this.vIndex)){
20382 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20387 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20388 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20392 e.preventDefault();
20395 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20396 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20412 this.picker().remove();
20417 Roo.apply(Roo.bootstrap.MonthField, {
20436 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20437 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20442 Roo.apply(Roo.bootstrap.MonthField, {
20446 cls: 'datepicker dropdown-menu roo-dynamic',
20450 cls: 'datepicker-months',
20454 cls: 'table-condensed',
20456 Roo.bootstrap.DateField.content
20476 * @class Roo.bootstrap.CheckBox
20477 * @extends Roo.bootstrap.Input
20478 * Bootstrap CheckBox class
20480 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20481 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20482 * @cfg {String} boxLabel The text that appears beside the checkbox
20483 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20484 * @cfg {Boolean} checked initnal the element
20485 * @cfg {Boolean} inline inline the element (default false)
20486 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20487 * @cfg {String} tooltip label tooltip
20490 * Create a new CheckBox
20491 * @param {Object} config The config object
20494 Roo.bootstrap.CheckBox = function(config){
20495 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20500 * Fires when the element is checked or unchecked.
20501 * @param {Roo.bootstrap.CheckBox} this This input
20502 * @param {Boolean} checked The new checked value
20507 * Fires when the element is click.
20508 * @param {Roo.bootstrap.CheckBox} this This input
20515 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20517 inputType: 'checkbox',
20526 getAutoCreate : function()
20528 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20534 cfg.cls = 'form-group ' + this.inputType; //input-group
20537 cfg.cls += ' ' + this.inputType + '-inline';
20543 type : this.inputType,
20544 value : this.inputValue,
20545 cls : 'roo-' + this.inputType, //'form-box',
20546 placeholder : this.placeholder || ''
20550 if(this.inputType != 'radio'){
20554 cls : 'roo-hidden-value',
20555 value : this.checked ? this.inputValue : this.valueOff
20560 if (this.weight) { // Validity check?
20561 cfg.cls += " " + this.inputType + "-" + this.weight;
20564 if (this.disabled) {
20565 input.disabled=true;
20569 input.checked = this.checked;
20574 input.name = this.name;
20576 if(this.inputType != 'radio'){
20577 hidden.name = this.name;
20578 input.name = '_hidden_' + this.name;
20583 input.cls += ' input-' + this.size;
20588 ['xs','sm','md','lg'].map(function(size){
20589 if (settings[size]) {
20590 cfg.cls += ' col-' + size + '-' + settings[size];
20594 var inputblock = input;
20596 if (this.before || this.after) {
20599 cls : 'input-group',
20604 inputblock.cn.push({
20606 cls : 'input-group-addon',
20611 inputblock.cn.push(input);
20613 if(this.inputType != 'radio'){
20614 inputblock.cn.push(hidden);
20618 inputblock.cn.push({
20620 cls : 'input-group-addon',
20627 if (align ==='left' && this.fieldLabel.length) {
20628 // Roo.log("left and has label");
20633 cls : 'control-label',
20634 html : this.fieldLabel
20644 if(this.labelWidth > 12){
20645 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20648 if(this.labelWidth < 13 && this.labelmd == 0){
20649 this.labelmd = this.labelWidth;
20652 if(this.labellg > 0){
20653 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20654 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20657 if(this.labelmd > 0){
20658 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20659 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20662 if(this.labelsm > 0){
20663 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20664 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20667 if(this.labelxs > 0){
20668 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20669 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20672 } else if ( this.fieldLabel.length) {
20673 // Roo.log(" label");
20677 tag: this.boxLabel ? 'span' : 'label',
20679 cls: 'control-label box-input-label',
20680 //cls : 'input-group-addon',
20681 html : this.fieldLabel
20690 // Roo.log(" no label && no align");
20691 cfg.cn = [ inputblock ] ;
20697 var boxLabelCfg = {
20699 //'for': id, // box label is handled by onclick - so no for...
20701 html: this.boxLabel
20705 boxLabelCfg.tooltip = this.tooltip;
20708 cfg.cn.push(boxLabelCfg);
20711 if(this.inputType != 'radio'){
20712 cfg.cn.push(hidden);
20720 * return the real input element.
20722 inputEl: function ()
20724 return this.el.select('input.roo-' + this.inputType,true).first();
20726 hiddenEl: function ()
20728 return this.el.select('input.roo-hidden-value',true).first();
20731 labelEl: function()
20733 return this.el.select('label.control-label',true).first();
20735 /* depricated... */
20739 return this.labelEl();
20742 boxLabelEl: function()
20744 return this.el.select('label.box-label',true).first();
20747 initEvents : function()
20749 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20751 this.inputEl().on('click', this.onClick, this);
20753 if (this.boxLabel) {
20754 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20757 this.startValue = this.getValue();
20760 Roo.bootstrap.CheckBox.register(this);
20764 onClick : function(e)
20766 if(this.fireEvent('click', this, e) !== false){
20767 this.setChecked(!this.checked);
20772 setChecked : function(state,suppressEvent)
20774 this.startValue = this.getValue();
20776 if(this.inputType == 'radio'){
20778 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20779 e.dom.checked = false;
20782 this.inputEl().dom.checked = true;
20784 this.inputEl().dom.value = this.inputValue;
20786 if(suppressEvent !== true){
20787 this.fireEvent('check', this, true);
20795 this.checked = state;
20797 this.inputEl().dom.checked = state;
20800 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20802 if(suppressEvent !== true){
20803 this.fireEvent('check', this, state);
20809 getValue : function()
20811 if(this.inputType == 'radio'){
20812 return this.getGroupValue();
20815 return this.hiddenEl().dom.value;
20819 getGroupValue : function()
20821 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20825 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20828 setValue : function(v,suppressEvent)
20830 if(this.inputType == 'radio'){
20831 this.setGroupValue(v, suppressEvent);
20835 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20840 setGroupValue : function(v, suppressEvent)
20842 this.startValue = this.getValue();
20844 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20845 e.dom.checked = false;
20847 if(e.dom.value == v){
20848 e.dom.checked = true;
20852 if(suppressEvent !== true){
20853 this.fireEvent('check', this, true);
20861 validate : function()
20863 if(this.getVisibilityEl().hasClass('hidden')){
20869 (this.inputType == 'radio' && this.validateRadio()) ||
20870 (this.inputType == 'checkbox' && this.validateCheckbox())
20876 this.markInvalid();
20880 validateRadio : function()
20882 if(this.getVisibilityEl().hasClass('hidden')){
20886 if(this.allowBlank){
20892 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20893 if(!e.dom.checked){
20905 validateCheckbox : function()
20908 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20909 //return (this.getValue() == this.inputValue) ? true : false;
20912 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20920 for(var i in group){
20921 if(group[i].el.isVisible(true)){
20929 for(var i in group){
20934 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20941 * Mark this field as valid
20943 markValid : function()
20947 this.fireEvent('valid', this);
20949 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20952 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20959 if(this.inputType == 'radio'){
20960 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20961 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20962 e.findParent('.form-group', false, true).addClass(_this.validClass);
20969 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20970 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20974 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20980 for(var i in group){
20981 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20982 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20987 * Mark this field as invalid
20988 * @param {String} msg The validation message
20990 markInvalid : function(msg)
20992 if(this.allowBlank){
20998 this.fireEvent('invalid', this, msg);
21000 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21003 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21007 label.markInvalid();
21010 if(this.inputType == 'radio'){
21011 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21012 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21013 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21020 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21021 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21025 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21031 for(var i in group){
21032 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21033 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21038 clearInvalid : function()
21040 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21042 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21044 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21046 if (label && label.iconEl) {
21047 label.iconEl.removeClass(label.validClass);
21048 label.iconEl.removeClass(label.invalidClass);
21052 disable : function()
21054 if(this.inputType != 'radio'){
21055 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21062 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21063 _this.getActionEl().addClass(this.disabledClass);
21064 e.dom.disabled = true;
21068 this.disabled = true;
21069 this.fireEvent("disable", this);
21073 enable : function()
21075 if(this.inputType != 'radio'){
21076 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21083 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21084 _this.getActionEl().removeClass(this.disabledClass);
21085 e.dom.disabled = false;
21089 this.disabled = false;
21090 this.fireEvent("enable", this);
21094 setBoxLabel : function(v)
21099 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21105 Roo.apply(Roo.bootstrap.CheckBox, {
21110 * register a CheckBox Group
21111 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21113 register : function(checkbox)
21115 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21116 this.groups[checkbox.groupId] = {};
21119 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21123 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21127 * fetch a CheckBox Group based on the group ID
21128 * @param {string} the group ID
21129 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21131 get: function(groupId) {
21132 if (typeof(this.groups[groupId]) == 'undefined') {
21136 return this.groups[groupId] ;
21149 * @class Roo.bootstrap.Radio
21150 * @extends Roo.bootstrap.Component
21151 * Bootstrap Radio class
21152 * @cfg {String} boxLabel - the label associated
21153 * @cfg {String} value - the value of radio
21156 * Create a new Radio
21157 * @param {Object} config The config object
21159 Roo.bootstrap.Radio = function(config){
21160 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21164 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21170 getAutoCreate : function()
21174 cls : 'form-group radio',
21179 html : this.boxLabel
21187 initEvents : function()
21189 this.parent().register(this);
21191 this.el.on('click', this.onClick, this);
21195 onClick : function(e)
21197 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21198 this.setChecked(true);
21202 setChecked : function(state, suppressEvent)
21204 this.parent().setValue(this.value, suppressEvent);
21208 setBoxLabel : function(v)
21213 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21228 * @class Roo.bootstrap.SecurePass
21229 * @extends Roo.bootstrap.Input
21230 * Bootstrap SecurePass class
21234 * Create a new SecurePass
21235 * @param {Object} config The config object
21238 Roo.bootstrap.SecurePass = function (config) {
21239 // these go here, so the translation tool can replace them..
21241 PwdEmpty: "Please type a password, and then retype it to confirm.",
21242 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21243 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21244 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21245 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21246 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21247 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21248 TooWeak: "Your password is Too Weak."
21250 this.meterLabel = "Password strength:";
21251 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21252 this.meterClass = [
21253 "roo-password-meter-tooweak",
21254 "roo-password-meter-weak",
21255 "roo-password-meter-medium",
21256 "roo-password-meter-strong",
21257 "roo-password-meter-grey"
21262 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21265 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21267 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21269 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21270 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21271 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21272 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21273 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21274 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21275 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21285 * @cfg {String/Object} Label for the strength meter (defaults to
21286 * 'Password strength:')
21291 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21292 * ['Weak', 'Medium', 'Strong'])
21295 pwdStrengths: false,
21308 initEvents: function ()
21310 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21312 if (this.el.is('input[type=password]') && Roo.isSafari) {
21313 this.el.on('keydown', this.SafariOnKeyDown, this);
21316 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21319 onRender: function (ct, position)
21321 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21322 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21323 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21325 this.trigger.createChild({
21330 cls: 'roo-password-meter-grey col-xs-12',
21333 //width: this.meterWidth + 'px'
21337 cls: 'roo-password-meter-text'
21343 if (this.hideTrigger) {
21344 this.trigger.setDisplayed(false);
21346 this.setSize(this.width || '', this.height || '');
21349 onDestroy: function ()
21351 if (this.trigger) {
21352 this.trigger.removeAllListeners();
21353 this.trigger.remove();
21356 this.wrap.remove();
21358 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21361 checkStrength: function ()
21363 var pwd = this.inputEl().getValue();
21364 if (pwd == this._lastPwd) {
21369 if (this.ClientSideStrongPassword(pwd)) {
21371 } else if (this.ClientSideMediumPassword(pwd)) {
21373 } else if (this.ClientSideWeakPassword(pwd)) {
21379 Roo.log('strength1: ' + strength);
21381 //var pm = this.trigger.child('div/div/div').dom;
21382 var pm = this.trigger.child('div/div');
21383 pm.removeClass(this.meterClass);
21384 pm.addClass(this.meterClass[strength]);
21387 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21389 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21391 this._lastPwd = pwd;
21395 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21397 this._lastPwd = '';
21399 var pm = this.trigger.child('div/div');
21400 pm.removeClass(this.meterClass);
21401 pm.addClass('roo-password-meter-grey');
21404 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21407 this.inputEl().dom.type='password';
21410 validateValue: function (value)
21413 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21416 if (value.length == 0) {
21417 if (this.allowBlank) {
21418 this.clearInvalid();
21422 this.markInvalid(this.errors.PwdEmpty);
21423 this.errorMsg = this.errors.PwdEmpty;
21431 if ('[\x21-\x7e]*'.match(value)) {
21432 this.markInvalid(this.errors.PwdBadChar);
21433 this.errorMsg = this.errors.PwdBadChar;
21436 if (value.length < 6) {
21437 this.markInvalid(this.errors.PwdShort);
21438 this.errorMsg = this.errors.PwdShort;
21441 if (value.length > 16) {
21442 this.markInvalid(this.errors.PwdLong);
21443 this.errorMsg = this.errors.PwdLong;
21447 if (this.ClientSideStrongPassword(value)) {
21449 } else if (this.ClientSideMediumPassword(value)) {
21451 } else if (this.ClientSideWeakPassword(value)) {
21458 if (strength < 2) {
21459 //this.markInvalid(this.errors.TooWeak);
21460 this.errorMsg = this.errors.TooWeak;
21465 console.log('strength2: ' + strength);
21467 //var pm = this.trigger.child('div/div/div').dom;
21469 var pm = this.trigger.child('div/div');
21470 pm.removeClass(this.meterClass);
21471 pm.addClass(this.meterClass[strength]);
21473 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21475 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21477 this.errorMsg = '';
21481 CharacterSetChecks: function (type)
21484 this.fResult = false;
21487 isctype: function (character, type)
21490 case this.kCapitalLetter:
21491 if (character >= 'A' && character <= 'Z') {
21496 case this.kSmallLetter:
21497 if (character >= 'a' && character <= 'z') {
21503 if (character >= '0' && character <= '9') {
21508 case this.kPunctuation:
21509 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21520 IsLongEnough: function (pwd, size)
21522 return !(pwd == null || isNaN(size) || pwd.length < size);
21525 SpansEnoughCharacterSets: function (word, nb)
21527 if (!this.IsLongEnough(word, nb))
21532 var characterSetChecks = new Array(
21533 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21534 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21537 for (var index = 0; index < word.length; ++index) {
21538 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21539 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21540 characterSetChecks[nCharSet].fResult = true;
21547 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21548 if (characterSetChecks[nCharSet].fResult) {
21553 if (nCharSets < nb) {
21559 ClientSideStrongPassword: function (pwd)
21561 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21564 ClientSideMediumPassword: function (pwd)
21566 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21569 ClientSideWeakPassword: function (pwd)
21571 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21574 })//<script type="text/javascript">
21577 * Based Ext JS Library 1.1.1
21578 * Copyright(c) 2006-2007, Ext JS, LLC.
21584 * @class Roo.HtmlEditorCore
21585 * @extends Roo.Component
21586 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21588 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21591 Roo.HtmlEditorCore = function(config){
21594 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21599 * @event initialize
21600 * Fires when the editor is fully initialized (including the iframe)
21601 * @param {Roo.HtmlEditorCore} this
21606 * Fires when the editor is first receives the focus. Any insertion must wait
21607 * until after this event.
21608 * @param {Roo.HtmlEditorCore} this
21612 * @event beforesync
21613 * Fires before the textarea is updated with content from the editor iframe. Return false
21614 * to cancel the sync.
21615 * @param {Roo.HtmlEditorCore} this
21616 * @param {String} html
21620 * @event beforepush
21621 * Fires before the iframe editor is updated with content from the textarea. Return false
21622 * to cancel the push.
21623 * @param {Roo.HtmlEditorCore} this
21624 * @param {String} html
21629 * Fires when the textarea is updated with content from the editor iframe.
21630 * @param {Roo.HtmlEditorCore} this
21631 * @param {String} html
21636 * Fires when the iframe editor is updated with content from the textarea.
21637 * @param {Roo.HtmlEditorCore} this
21638 * @param {String} html
21643 * @event editorevent
21644 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21645 * @param {Roo.HtmlEditorCore} this
21651 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21653 // defaults : white / black...
21654 this.applyBlacklists();
21661 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21665 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21671 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21676 * @cfg {Number} height (in pixels)
21680 * @cfg {Number} width (in pixels)
21685 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21688 stylesheets: false,
21693 // private properties
21694 validationEvent : false,
21696 initialized : false,
21698 sourceEditMode : false,
21699 onFocus : Roo.emptyFn,
21701 hideMode:'offsets',
21705 // blacklist + whitelisted elements..
21712 * Protected method that will not generally be called directly. It
21713 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21714 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21716 getDocMarkup : function(){
21720 // inherit styels from page...??
21721 if (this.stylesheets === false) {
21723 Roo.get(document.head).select('style').each(function(node) {
21724 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21727 Roo.get(document.head).select('link').each(function(node) {
21728 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21731 } else if (!this.stylesheets.length) {
21733 st = '<style type="text/css">' +
21734 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21737 st = '<style type="text/css">' +
21742 st += '<style type="text/css">' +
21743 'IMG { cursor: pointer } ' +
21746 var cls = 'roo-htmleditor-body';
21748 if(this.bodyCls.length){
21749 cls += ' ' + this.bodyCls;
21752 return '<html><head>' + st +
21753 //<style type="text/css">' +
21754 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21756 ' </head><body class="' + cls + '"></body></html>';
21760 onRender : function(ct, position)
21763 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21764 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21767 this.el.dom.style.border = '0 none';
21768 this.el.dom.setAttribute('tabIndex', -1);
21769 this.el.addClass('x-hidden hide');
21773 if(Roo.isIE){ // fix IE 1px bogus margin
21774 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21778 this.frameId = Roo.id();
21782 var iframe = this.owner.wrap.createChild({
21784 cls: 'form-control', // bootstrap..
21786 name: this.frameId,
21787 frameBorder : 'no',
21788 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21793 this.iframe = iframe.dom;
21795 this.assignDocWin();
21797 this.doc.designMode = 'on';
21800 this.doc.write(this.getDocMarkup());
21804 var task = { // must defer to wait for browser to be ready
21806 //console.log("run task?" + this.doc.readyState);
21807 this.assignDocWin();
21808 if(this.doc.body || this.doc.readyState == 'complete'){
21810 this.doc.designMode="on";
21814 Roo.TaskMgr.stop(task);
21815 this.initEditor.defer(10, this);
21822 Roo.TaskMgr.start(task);
21827 onResize : function(w, h)
21829 Roo.log('resize: ' +w + ',' + h );
21830 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21834 if(typeof w == 'number'){
21836 this.iframe.style.width = w + 'px';
21838 if(typeof h == 'number'){
21840 this.iframe.style.height = h + 'px';
21842 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21849 * Toggles the editor between standard and source edit mode.
21850 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21852 toggleSourceEdit : function(sourceEditMode){
21854 this.sourceEditMode = sourceEditMode === true;
21856 if(this.sourceEditMode){
21858 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21861 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21862 //this.iframe.className = '';
21865 //this.setSize(this.owner.wrap.getSize());
21866 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21873 * Protected method that will not generally be called directly. If you need/want
21874 * custom HTML cleanup, this is the method you should override.
21875 * @param {String} html The HTML to be cleaned
21876 * return {String} The cleaned HTML
21878 cleanHtml : function(html){
21879 html = String(html);
21880 if(html.length > 5){
21881 if(Roo.isSafari){ // strip safari nonsense
21882 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21885 if(html == ' '){
21892 * HTML Editor -> Textarea
21893 * Protected method that will not generally be called directly. Syncs the contents
21894 * of the editor iframe with the textarea.
21896 syncValue : function(){
21897 if(this.initialized){
21898 var bd = (this.doc.body || this.doc.documentElement);
21899 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21900 var html = bd.innerHTML;
21902 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21903 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21905 html = '<div style="'+m[0]+'">' + html + '</div>';
21908 html = this.cleanHtml(html);
21909 // fix up the special chars.. normaly like back quotes in word...
21910 // however we do not want to do this with chinese..
21911 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21912 var cc = b.charCodeAt();
21914 (cc >= 0x4E00 && cc < 0xA000 ) ||
21915 (cc >= 0x3400 && cc < 0x4E00 ) ||
21916 (cc >= 0xf900 && cc < 0xfb00 )
21922 if(this.owner.fireEvent('beforesync', this, html) !== false){
21923 this.el.dom.value = html;
21924 this.owner.fireEvent('sync', this, html);
21930 * Protected method that will not generally be called directly. Pushes the value of the textarea
21931 * into the iframe editor.
21933 pushValue : function(){
21934 if(this.initialized){
21935 var v = this.el.dom.value.trim();
21937 // if(v.length < 1){
21941 if(this.owner.fireEvent('beforepush', this, v) !== false){
21942 var d = (this.doc.body || this.doc.documentElement);
21944 this.cleanUpPaste();
21945 this.el.dom.value = d.innerHTML;
21946 this.owner.fireEvent('push', this, v);
21952 deferFocus : function(){
21953 this.focus.defer(10, this);
21957 focus : function(){
21958 if(this.win && !this.sourceEditMode){
21965 assignDocWin: function()
21967 var iframe = this.iframe;
21970 this.doc = iframe.contentWindow.document;
21971 this.win = iframe.contentWindow;
21973 // if (!Roo.get(this.frameId)) {
21976 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21977 // this.win = Roo.get(this.frameId).dom.contentWindow;
21979 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21983 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21984 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21989 initEditor : function(){
21990 //console.log("INIT EDITOR");
21991 this.assignDocWin();
21995 this.doc.designMode="on";
21997 this.doc.write(this.getDocMarkup());
22000 var dbody = (this.doc.body || this.doc.documentElement);
22001 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22002 // this copies styles from the containing element into thsi one..
22003 // not sure why we need all of this..
22004 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22006 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22007 //ss['background-attachment'] = 'fixed'; // w3c
22008 dbody.bgProperties = 'fixed'; // ie
22009 //Roo.DomHelper.applyStyles(dbody, ss);
22010 Roo.EventManager.on(this.doc, {
22011 //'mousedown': this.onEditorEvent,
22012 'mouseup': this.onEditorEvent,
22013 'dblclick': this.onEditorEvent,
22014 'click': this.onEditorEvent,
22015 'keyup': this.onEditorEvent,
22020 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22022 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22023 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22025 this.initialized = true;
22027 this.owner.fireEvent('initialize', this);
22032 onDestroy : function(){
22038 //for (var i =0; i < this.toolbars.length;i++) {
22039 // // fixme - ask toolbars for heights?
22040 // this.toolbars[i].onDestroy();
22043 //this.wrap.dom.innerHTML = '';
22044 //this.wrap.remove();
22049 onFirstFocus : function(){
22051 this.assignDocWin();
22054 this.activated = true;
22057 if(Roo.isGecko){ // prevent silly gecko errors
22059 var s = this.win.getSelection();
22060 if(!s.focusNode || s.focusNode.nodeType != 3){
22061 var r = s.getRangeAt(0);
22062 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22067 this.execCmd('useCSS', true);
22068 this.execCmd('styleWithCSS', false);
22071 this.owner.fireEvent('activate', this);
22075 adjustFont: function(btn){
22076 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22077 //if(Roo.isSafari){ // safari
22080 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22081 if(Roo.isSafari){ // safari
22082 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22083 v = (v < 10) ? 10 : v;
22084 v = (v > 48) ? 48 : v;
22085 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22090 v = Math.max(1, v+adjust);
22092 this.execCmd('FontSize', v );
22095 onEditorEvent : function(e)
22097 this.owner.fireEvent('editorevent', this, e);
22098 // this.updateToolbar();
22099 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22102 insertTag : function(tg)
22104 // could be a bit smarter... -> wrap the current selected tRoo..
22105 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22107 range = this.createRange(this.getSelection());
22108 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22109 wrappingNode.appendChild(range.extractContents());
22110 range.insertNode(wrappingNode);
22117 this.execCmd("formatblock", tg);
22121 insertText : function(txt)
22125 var range = this.createRange();
22126 range.deleteContents();
22127 //alert(Sender.getAttribute('label'));
22129 range.insertNode(this.doc.createTextNode(txt));
22135 * Executes a Midas editor command on the editor document and performs necessary focus and
22136 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22137 * @param {String} cmd The Midas command
22138 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22140 relayCmd : function(cmd, value){
22142 this.execCmd(cmd, value);
22143 this.owner.fireEvent('editorevent', this);
22144 //this.updateToolbar();
22145 this.owner.deferFocus();
22149 * Executes a Midas editor command directly on the editor document.
22150 * For visual commands, you should use {@link #relayCmd} instead.
22151 * <b>This should only be called after the editor is initialized.</b>
22152 * @param {String} cmd The Midas command
22153 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22155 execCmd : function(cmd, value){
22156 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22163 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22165 * @param {String} text | dom node..
22167 insertAtCursor : function(text)
22170 if(!this.activated){
22176 var r = this.doc.selection.createRange();
22187 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22191 // from jquery ui (MIT licenced)
22193 var win = this.win;
22195 if (win.getSelection && win.getSelection().getRangeAt) {
22196 range = win.getSelection().getRangeAt(0);
22197 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22198 range.insertNode(node);
22199 } else if (win.document.selection && win.document.selection.createRange) {
22200 // no firefox support
22201 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22202 win.document.selection.createRange().pasteHTML(txt);
22204 // no firefox support
22205 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22206 this.execCmd('InsertHTML', txt);
22215 mozKeyPress : function(e){
22217 var c = e.getCharCode(), cmd;
22220 c = String.fromCharCode(c).toLowerCase();
22234 this.cleanUpPaste.defer(100, this);
22242 e.preventDefault();
22250 fixKeys : function(){ // load time branching for fastest keydown performance
22252 return function(e){
22253 var k = e.getKey(), r;
22256 r = this.doc.selection.createRange();
22259 r.pasteHTML('    ');
22266 r = this.doc.selection.createRange();
22268 var target = r.parentElement();
22269 if(!target || target.tagName.toLowerCase() != 'li'){
22271 r.pasteHTML('<br />');
22277 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22278 this.cleanUpPaste.defer(100, this);
22284 }else if(Roo.isOpera){
22285 return function(e){
22286 var k = e.getKey();
22290 this.execCmd('InsertHTML','    ');
22293 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22294 this.cleanUpPaste.defer(100, this);
22299 }else if(Roo.isSafari){
22300 return function(e){
22301 var k = e.getKey();
22305 this.execCmd('InsertText','\t');
22309 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22310 this.cleanUpPaste.defer(100, this);
22318 getAllAncestors: function()
22320 var p = this.getSelectedNode();
22323 a.push(p); // push blank onto stack..
22324 p = this.getParentElement();
22328 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22332 a.push(this.doc.body);
22336 lastSelNode : false,
22339 getSelection : function()
22341 this.assignDocWin();
22342 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22345 getSelectedNode: function()
22347 // this may only work on Gecko!!!
22349 // should we cache this!!!!
22354 var range = this.createRange(this.getSelection()).cloneRange();
22357 var parent = range.parentElement();
22359 var testRange = range.duplicate();
22360 testRange.moveToElementText(parent);
22361 if (testRange.inRange(range)) {
22364 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22367 parent = parent.parentElement;
22372 // is ancestor a text element.
22373 var ac = range.commonAncestorContainer;
22374 if (ac.nodeType == 3) {
22375 ac = ac.parentNode;
22378 var ar = ac.childNodes;
22381 var other_nodes = [];
22382 var has_other_nodes = false;
22383 for (var i=0;i<ar.length;i++) {
22384 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22387 // fullly contained node.
22389 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22394 // probably selected..
22395 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22396 other_nodes.push(ar[i]);
22400 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22405 has_other_nodes = true;
22407 if (!nodes.length && other_nodes.length) {
22408 nodes= other_nodes;
22410 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22416 createRange: function(sel)
22418 // this has strange effects when using with
22419 // top toolbar - not sure if it's a great idea.
22420 //this.editor.contentWindow.focus();
22421 if (typeof sel != "undefined") {
22423 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22425 return this.doc.createRange();
22428 return this.doc.createRange();
22431 getParentElement: function()
22434 this.assignDocWin();
22435 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22437 var range = this.createRange(sel);
22440 var p = range.commonAncestorContainer;
22441 while (p.nodeType == 3) { // text node
22452 * Range intersection.. the hard stuff...
22456 * [ -- selected range --- ]
22460 * if end is before start or hits it. fail.
22461 * if start is after end or hits it fail.
22463 * if either hits (but other is outside. - then it's not
22469 // @see http://www.thismuchiknow.co.uk/?p=64.
22470 rangeIntersectsNode : function(range, node)
22472 var nodeRange = node.ownerDocument.createRange();
22474 nodeRange.selectNode(node);
22476 nodeRange.selectNodeContents(node);
22479 var rangeStartRange = range.cloneRange();
22480 rangeStartRange.collapse(true);
22482 var rangeEndRange = range.cloneRange();
22483 rangeEndRange.collapse(false);
22485 var nodeStartRange = nodeRange.cloneRange();
22486 nodeStartRange.collapse(true);
22488 var nodeEndRange = nodeRange.cloneRange();
22489 nodeEndRange.collapse(false);
22491 return rangeStartRange.compareBoundaryPoints(
22492 Range.START_TO_START, nodeEndRange) == -1 &&
22493 rangeEndRange.compareBoundaryPoints(
22494 Range.START_TO_START, nodeStartRange) == 1;
22498 rangeCompareNode : function(range, node)
22500 var nodeRange = node.ownerDocument.createRange();
22502 nodeRange.selectNode(node);
22504 nodeRange.selectNodeContents(node);
22508 range.collapse(true);
22510 nodeRange.collapse(true);
22512 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22513 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22515 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22517 var nodeIsBefore = ss == 1;
22518 var nodeIsAfter = ee == -1;
22520 if (nodeIsBefore && nodeIsAfter) {
22523 if (!nodeIsBefore && nodeIsAfter) {
22524 return 1; //right trailed.
22527 if (nodeIsBefore && !nodeIsAfter) {
22528 return 2; // left trailed.
22534 // private? - in a new class?
22535 cleanUpPaste : function()
22537 // cleans up the whole document..
22538 Roo.log('cleanuppaste');
22540 this.cleanUpChildren(this.doc.body);
22541 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22542 if (clean != this.doc.body.innerHTML) {
22543 this.doc.body.innerHTML = clean;
22548 cleanWordChars : function(input) {// change the chars to hex code
22549 var he = Roo.HtmlEditorCore;
22551 var output = input;
22552 Roo.each(he.swapCodes, function(sw) {
22553 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22555 output = output.replace(swapper, sw[1]);
22562 cleanUpChildren : function (n)
22564 if (!n.childNodes.length) {
22567 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22568 this.cleanUpChild(n.childNodes[i]);
22575 cleanUpChild : function (node)
22578 //console.log(node);
22579 if (node.nodeName == "#text") {
22580 // clean up silly Windows -- stuff?
22583 if (node.nodeName == "#comment") {
22584 node.parentNode.removeChild(node);
22585 // clean up silly Windows -- stuff?
22588 var lcname = node.tagName.toLowerCase();
22589 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22590 // whitelist of tags..
22592 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22594 node.parentNode.removeChild(node);
22599 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22601 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22602 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22604 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22605 // remove_keep_children = true;
22608 if (remove_keep_children) {
22609 this.cleanUpChildren(node);
22610 // inserts everything just before this node...
22611 while (node.childNodes.length) {
22612 var cn = node.childNodes[0];
22613 node.removeChild(cn);
22614 node.parentNode.insertBefore(cn, node);
22616 node.parentNode.removeChild(node);
22620 if (!node.attributes || !node.attributes.length) {
22621 this.cleanUpChildren(node);
22625 function cleanAttr(n,v)
22628 if (v.match(/^\./) || v.match(/^\//)) {
22631 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22634 if (v.match(/^#/)) {
22637 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22638 node.removeAttribute(n);
22642 var cwhite = this.cwhite;
22643 var cblack = this.cblack;
22645 function cleanStyle(n,v)
22647 if (v.match(/expression/)) { //XSS?? should we even bother..
22648 node.removeAttribute(n);
22652 var parts = v.split(/;/);
22655 Roo.each(parts, function(p) {
22656 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22660 var l = p.split(':').shift().replace(/\s+/g,'');
22661 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22663 if ( cwhite.length && cblack.indexOf(l) > -1) {
22664 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22665 //node.removeAttribute(n);
22669 // only allow 'c whitelisted system attributes'
22670 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22671 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22672 //node.removeAttribute(n);
22682 if (clean.length) {
22683 node.setAttribute(n, clean.join(';'));
22685 node.removeAttribute(n);
22691 for (var i = node.attributes.length-1; i > -1 ; i--) {
22692 var a = node.attributes[i];
22695 if (a.name.toLowerCase().substr(0,2)=='on') {
22696 node.removeAttribute(a.name);
22699 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22700 node.removeAttribute(a.name);
22703 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22704 cleanAttr(a.name,a.value); // fixme..
22707 if (a.name == 'style') {
22708 cleanStyle(a.name,a.value);
22711 /// clean up MS crap..
22712 // tecnically this should be a list of valid class'es..
22715 if (a.name == 'class') {
22716 if (a.value.match(/^Mso/)) {
22717 node.className = '';
22720 if (a.value.match(/^body$/)) {
22721 node.className = '';
22732 this.cleanUpChildren(node);
22738 * Clean up MS wordisms...
22740 cleanWord : function(node)
22745 this.cleanWord(this.doc.body);
22748 if (node.nodeName == "#text") {
22749 // clean up silly Windows -- stuff?
22752 if (node.nodeName == "#comment") {
22753 node.parentNode.removeChild(node);
22754 // clean up silly Windows -- stuff?
22758 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22759 node.parentNode.removeChild(node);
22763 // remove - but keep children..
22764 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22765 while (node.childNodes.length) {
22766 var cn = node.childNodes[0];
22767 node.removeChild(cn);
22768 node.parentNode.insertBefore(cn, node);
22770 node.parentNode.removeChild(node);
22771 this.iterateChildren(node, this.cleanWord);
22775 if (node.className.length) {
22777 var cn = node.className.split(/\W+/);
22779 Roo.each(cn, function(cls) {
22780 if (cls.match(/Mso[a-zA-Z]+/)) {
22785 node.className = cna.length ? cna.join(' ') : '';
22787 node.removeAttribute("class");
22791 if (node.hasAttribute("lang")) {
22792 node.removeAttribute("lang");
22795 if (node.hasAttribute("style")) {
22797 var styles = node.getAttribute("style").split(";");
22799 Roo.each(styles, function(s) {
22800 if (!s.match(/:/)) {
22803 var kv = s.split(":");
22804 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22807 // what ever is left... we allow.
22810 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22811 if (!nstyle.length) {
22812 node.removeAttribute('style');
22815 this.iterateChildren(node, this.cleanWord);
22821 * iterateChildren of a Node, calling fn each time, using this as the scole..
22822 * @param {DomNode} node node to iterate children of.
22823 * @param {Function} fn method of this class to call on each item.
22825 iterateChildren : function(node, fn)
22827 if (!node.childNodes.length) {
22830 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22831 fn.call(this, node.childNodes[i])
22837 * cleanTableWidths.
22839 * Quite often pasting from word etc.. results in tables with column and widths.
22840 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22843 cleanTableWidths : function(node)
22848 this.cleanTableWidths(this.doc.body);
22853 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22856 Roo.log(node.tagName);
22857 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22858 this.iterateChildren(node, this.cleanTableWidths);
22861 if (node.hasAttribute('width')) {
22862 node.removeAttribute('width');
22866 if (node.hasAttribute("style")) {
22869 var styles = node.getAttribute("style").split(";");
22871 Roo.each(styles, function(s) {
22872 if (!s.match(/:/)) {
22875 var kv = s.split(":");
22876 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22879 // what ever is left... we allow.
22882 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22883 if (!nstyle.length) {
22884 node.removeAttribute('style');
22888 this.iterateChildren(node, this.cleanTableWidths);
22896 domToHTML : function(currentElement, depth, nopadtext) {
22898 depth = depth || 0;
22899 nopadtext = nopadtext || false;
22901 if (!currentElement) {
22902 return this.domToHTML(this.doc.body);
22905 //Roo.log(currentElement);
22907 var allText = false;
22908 var nodeName = currentElement.nodeName;
22909 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22911 if (nodeName == '#text') {
22913 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22918 if (nodeName != 'BODY') {
22921 // Prints the node tagName, such as <A>, <IMG>, etc
22924 for(i = 0; i < currentElement.attributes.length;i++) {
22926 var aname = currentElement.attributes.item(i).name;
22927 if (!currentElement.attributes.item(i).value.length) {
22930 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22933 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22942 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22945 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22950 // Traverse the tree
22952 var currentElementChild = currentElement.childNodes.item(i);
22953 var allText = true;
22954 var innerHTML = '';
22956 while (currentElementChild) {
22957 // Formatting code (indent the tree so it looks nice on the screen)
22958 var nopad = nopadtext;
22959 if (lastnode == 'SPAN') {
22963 if (currentElementChild.nodeName == '#text') {
22964 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22965 toadd = nopadtext ? toadd : toadd.trim();
22966 if (!nopad && toadd.length > 80) {
22967 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22969 innerHTML += toadd;
22972 currentElementChild = currentElement.childNodes.item(i);
22978 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22980 // Recursively traverse the tree structure of the child node
22981 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22982 lastnode = currentElementChild.nodeName;
22984 currentElementChild=currentElement.childNodes.item(i);
22990 // The remaining code is mostly for formatting the tree
22991 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22996 ret+= "</"+tagName+">";
23002 applyBlacklists : function()
23004 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23005 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23009 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23010 if (b.indexOf(tag) > -1) {
23013 this.white.push(tag);
23017 Roo.each(w, function(tag) {
23018 if (b.indexOf(tag) > -1) {
23021 if (this.white.indexOf(tag) > -1) {
23024 this.white.push(tag);
23029 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23030 if (w.indexOf(tag) > -1) {
23033 this.black.push(tag);
23037 Roo.each(b, function(tag) {
23038 if (w.indexOf(tag) > -1) {
23041 if (this.black.indexOf(tag) > -1) {
23044 this.black.push(tag);
23049 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23050 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23054 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23055 if (b.indexOf(tag) > -1) {
23058 this.cwhite.push(tag);
23062 Roo.each(w, function(tag) {
23063 if (b.indexOf(tag) > -1) {
23066 if (this.cwhite.indexOf(tag) > -1) {
23069 this.cwhite.push(tag);
23074 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23075 if (w.indexOf(tag) > -1) {
23078 this.cblack.push(tag);
23082 Roo.each(b, function(tag) {
23083 if (w.indexOf(tag) > -1) {
23086 if (this.cblack.indexOf(tag) > -1) {
23089 this.cblack.push(tag);
23094 setStylesheets : function(stylesheets)
23096 if(typeof(stylesheets) == 'string'){
23097 Roo.get(this.iframe.contentDocument.head).createChild({
23099 rel : 'stylesheet',
23108 Roo.each(stylesheets, function(s) {
23113 Roo.get(_this.iframe.contentDocument.head).createChild({
23115 rel : 'stylesheet',
23124 removeStylesheets : function()
23128 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23133 setStyle : function(style)
23135 Roo.get(this.iframe.contentDocument.head).createChild({
23144 // hide stuff that is not compatible
23158 * @event specialkey
23162 * @cfg {String} fieldClass @hide
23165 * @cfg {String} focusClass @hide
23168 * @cfg {String} autoCreate @hide
23171 * @cfg {String} inputType @hide
23174 * @cfg {String} invalidClass @hide
23177 * @cfg {String} invalidText @hide
23180 * @cfg {String} msgFx @hide
23183 * @cfg {String} validateOnBlur @hide
23187 Roo.HtmlEditorCore.white = [
23188 'area', 'br', 'img', 'input', 'hr', 'wbr',
23190 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23191 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23192 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23193 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23194 'table', 'ul', 'xmp',
23196 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23199 'dir', 'menu', 'ol', 'ul', 'dl',
23205 Roo.HtmlEditorCore.black = [
23206 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23208 'base', 'basefont', 'bgsound', 'blink', 'body',
23209 'frame', 'frameset', 'head', 'html', 'ilayer',
23210 'iframe', 'layer', 'link', 'meta', 'object',
23211 'script', 'style' ,'title', 'xml' // clean later..
23213 Roo.HtmlEditorCore.clean = [
23214 'script', 'style', 'title', 'xml'
23216 Roo.HtmlEditorCore.remove = [
23221 Roo.HtmlEditorCore.ablack = [
23225 Roo.HtmlEditorCore.aclean = [
23226 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23230 Roo.HtmlEditorCore.pwhite= [
23231 'http', 'https', 'mailto'
23234 // white listed style attributes.
23235 Roo.HtmlEditorCore.cwhite= [
23236 // 'text-align', /// default is to allow most things..
23242 // black listed style attributes.
23243 Roo.HtmlEditorCore.cblack= [
23244 // 'font-size' -- this can be set by the project
23248 Roo.HtmlEditorCore.swapCodes =[
23267 * @class Roo.bootstrap.HtmlEditor
23268 * @extends Roo.bootstrap.TextArea
23269 * Bootstrap HtmlEditor class
23272 * Create a new HtmlEditor
23273 * @param {Object} config The config object
23276 Roo.bootstrap.HtmlEditor = function(config){
23277 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23278 if (!this.toolbars) {
23279 this.toolbars = [];
23282 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23285 * @event initialize
23286 * Fires when the editor is fully initialized (including the iframe)
23287 * @param {HtmlEditor} this
23292 * Fires when the editor is first receives the focus. Any insertion must wait
23293 * until after this event.
23294 * @param {HtmlEditor} this
23298 * @event beforesync
23299 * Fires before the textarea is updated with content from the editor iframe. Return false
23300 * to cancel the sync.
23301 * @param {HtmlEditor} this
23302 * @param {String} html
23306 * @event beforepush
23307 * Fires before the iframe editor is updated with content from the textarea. Return false
23308 * to cancel the push.
23309 * @param {HtmlEditor} this
23310 * @param {String} html
23315 * Fires when the textarea is updated with content from the editor iframe.
23316 * @param {HtmlEditor} this
23317 * @param {String} html
23322 * Fires when the iframe editor is updated with content from the textarea.
23323 * @param {HtmlEditor} this
23324 * @param {String} html
23328 * @event editmodechange
23329 * Fires when the editor switches edit modes
23330 * @param {HtmlEditor} this
23331 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23333 editmodechange: true,
23335 * @event editorevent
23336 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23337 * @param {HtmlEditor} this
23341 * @event firstfocus
23342 * Fires when on first focus - needed by toolbars..
23343 * @param {HtmlEditor} this
23348 * Auto save the htmlEditor value as a file into Events
23349 * @param {HtmlEditor} this
23353 * @event savedpreview
23354 * preview the saved version of htmlEditor
23355 * @param {HtmlEditor} this
23362 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23366 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23371 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23376 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23381 * @cfg {Number} height (in pixels)
23385 * @cfg {Number} width (in pixels)
23390 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23393 stylesheets: false,
23398 // private properties
23399 validationEvent : false,
23401 initialized : false,
23404 onFocus : Roo.emptyFn,
23406 hideMode:'offsets',
23408 tbContainer : false,
23412 toolbarContainer :function() {
23413 return this.wrap.select('.x-html-editor-tb',true).first();
23417 * Protected method that will not generally be called directly. It
23418 * is called when the editor creates its toolbar. Override this method if you need to
23419 * add custom toolbar buttons.
23420 * @param {HtmlEditor} editor
23422 createToolbar : function(){
23423 Roo.log('renewing');
23424 Roo.log("create toolbars");
23426 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23427 this.toolbars[0].render(this.toolbarContainer());
23431 // if (!editor.toolbars || !editor.toolbars.length) {
23432 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23435 // for (var i =0 ; i < editor.toolbars.length;i++) {
23436 // editor.toolbars[i] = Roo.factory(
23437 // typeof(editor.toolbars[i]) == 'string' ?
23438 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23439 // Roo.bootstrap.HtmlEditor);
23440 // editor.toolbars[i].init(editor);
23446 onRender : function(ct, position)
23448 // Roo.log("Call onRender: " + this.xtype);
23450 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23452 this.wrap = this.inputEl().wrap({
23453 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23456 this.editorcore.onRender(ct, position);
23458 if (this.resizable) {
23459 this.resizeEl = new Roo.Resizable(this.wrap, {
23463 minHeight : this.height,
23464 height: this.height,
23465 handles : this.resizable,
23468 resize : function(r, w, h) {
23469 _t.onResize(w,h); // -something
23475 this.createToolbar(this);
23478 if(!this.width && this.resizable){
23479 this.setSize(this.wrap.getSize());
23481 if (this.resizeEl) {
23482 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23483 // should trigger onReize..
23489 onResize : function(w, h)
23491 Roo.log('resize: ' +w + ',' + h );
23492 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23496 if(this.inputEl() ){
23497 if(typeof w == 'number'){
23498 var aw = w - this.wrap.getFrameWidth('lr');
23499 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23502 if(typeof h == 'number'){
23503 var tbh = -11; // fixme it needs to tool bar size!
23504 for (var i =0; i < this.toolbars.length;i++) {
23505 // fixme - ask toolbars for heights?
23506 tbh += this.toolbars[i].el.getHeight();
23507 //if (this.toolbars[i].footer) {
23508 // tbh += this.toolbars[i].footer.el.getHeight();
23516 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23517 ah -= 5; // knock a few pixes off for look..
23518 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23522 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23523 this.editorcore.onResize(ew,eh);
23528 * Toggles the editor between standard and source edit mode.
23529 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23531 toggleSourceEdit : function(sourceEditMode)
23533 this.editorcore.toggleSourceEdit(sourceEditMode);
23535 if(this.editorcore.sourceEditMode){
23536 Roo.log('editor - showing textarea');
23539 // Roo.log(this.syncValue());
23541 this.inputEl().removeClass(['hide', 'x-hidden']);
23542 this.inputEl().dom.removeAttribute('tabIndex');
23543 this.inputEl().focus();
23545 Roo.log('editor - hiding textarea');
23547 // Roo.log(this.pushValue());
23550 this.inputEl().addClass(['hide', 'x-hidden']);
23551 this.inputEl().dom.setAttribute('tabIndex', -1);
23552 //this.deferFocus();
23555 if(this.resizable){
23556 this.setSize(this.wrap.getSize());
23559 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23562 // private (for BoxComponent)
23563 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23565 // private (for BoxComponent)
23566 getResizeEl : function(){
23570 // private (for BoxComponent)
23571 getPositionEl : function(){
23576 initEvents : function(){
23577 this.originalValue = this.getValue();
23581 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23584 // markInvalid : Roo.emptyFn,
23586 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23589 // clearInvalid : Roo.emptyFn,
23591 setValue : function(v){
23592 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23593 this.editorcore.pushValue();
23598 deferFocus : function(){
23599 this.focus.defer(10, this);
23603 focus : function(){
23604 this.editorcore.focus();
23610 onDestroy : function(){
23616 for (var i =0; i < this.toolbars.length;i++) {
23617 // fixme - ask toolbars for heights?
23618 this.toolbars[i].onDestroy();
23621 this.wrap.dom.innerHTML = '';
23622 this.wrap.remove();
23627 onFirstFocus : function(){
23628 //Roo.log("onFirstFocus");
23629 this.editorcore.onFirstFocus();
23630 for (var i =0; i < this.toolbars.length;i++) {
23631 this.toolbars[i].onFirstFocus();
23637 syncValue : function()
23639 this.editorcore.syncValue();
23642 pushValue : function()
23644 this.editorcore.pushValue();
23648 // hide stuff that is not compatible
23662 * @event specialkey
23666 * @cfg {String} fieldClass @hide
23669 * @cfg {String} focusClass @hide
23672 * @cfg {String} autoCreate @hide
23675 * @cfg {String} inputType @hide
23678 * @cfg {String} invalidClass @hide
23681 * @cfg {String} invalidText @hide
23684 * @cfg {String} msgFx @hide
23687 * @cfg {String} validateOnBlur @hide
23696 Roo.namespace('Roo.bootstrap.htmleditor');
23698 * @class Roo.bootstrap.HtmlEditorToolbar1
23703 new Roo.bootstrap.HtmlEditor({
23706 new Roo.bootstrap.HtmlEditorToolbar1({
23707 disable : { fonts: 1 , format: 1, ..., ... , ...],
23713 * @cfg {Object} disable List of elements to disable..
23714 * @cfg {Array} btns List of additional buttons.
23718 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23721 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23724 Roo.apply(this, config);
23726 // default disabled, based on 'good practice'..
23727 this.disable = this.disable || {};
23728 Roo.applyIf(this.disable, {
23731 specialElements : true
23733 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23735 this.editor = config.editor;
23736 this.editorcore = config.editor.editorcore;
23738 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23740 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23741 // dont call parent... till later.
23743 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23748 editorcore : false,
23753 "h1","h2","h3","h4","h5","h6",
23755 "abbr", "acronym", "address", "cite", "samp", "var",
23759 onRender : function(ct, position)
23761 // Roo.log("Call onRender: " + this.xtype);
23763 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23765 this.el.dom.style.marginBottom = '0';
23767 var editorcore = this.editorcore;
23768 var editor= this.editor;
23771 var btn = function(id,cmd , toggle, handler, html){
23773 var event = toggle ? 'toggle' : 'click';
23778 xns: Roo.bootstrap,
23781 enableToggle:toggle !== false,
23783 pressed : toggle ? false : null,
23786 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23787 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23793 // var cb_box = function...
23798 xns: Roo.bootstrap,
23799 glyphicon : 'font',
23803 xns: Roo.bootstrap,
23807 Roo.each(this.formats, function(f) {
23808 style.menu.items.push({
23810 xns: Roo.bootstrap,
23811 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23816 editorcore.insertTag(this.tagname);
23823 children.push(style);
23825 btn('bold',false,true);
23826 btn('italic',false,true);
23827 btn('align-left', 'justifyleft',true);
23828 btn('align-center', 'justifycenter',true);
23829 btn('align-right' , 'justifyright',true);
23830 btn('link', false, false, function(btn) {
23831 //Roo.log("create link?");
23832 var url = prompt(this.createLinkText, this.defaultLinkValue);
23833 if(url && url != 'http:/'+'/'){
23834 this.editorcore.relayCmd('createlink', url);
23837 btn('list','insertunorderedlist',true);
23838 btn('pencil', false,true, function(btn){
23840 this.toggleSourceEdit(btn.pressed);
23843 if (this.editor.btns.length > 0) {
23844 for (var i = 0; i<this.editor.btns.length; i++) {
23845 children.push(this.editor.btns[i]);
23853 xns: Roo.bootstrap,
23858 xns: Roo.bootstrap,
23863 cog.menu.items.push({
23865 xns: Roo.bootstrap,
23866 html : Clean styles,
23871 editorcore.insertTag(this.tagname);
23880 this.xtype = 'NavSimplebar';
23882 for(var i=0;i< children.length;i++) {
23884 this.buttons.add(this.addxtypeChild(children[i]));
23888 editor.on('editorevent', this.updateToolbar, this);
23890 onBtnClick : function(id)
23892 this.editorcore.relayCmd(id);
23893 this.editorcore.focus();
23897 * Protected method that will not generally be called directly. It triggers
23898 * a toolbar update by reading the markup state of the current selection in the editor.
23900 updateToolbar: function(){
23902 if(!this.editorcore.activated){
23903 this.editor.onFirstFocus(); // is this neeed?
23907 var btns = this.buttons;
23908 var doc = this.editorcore.doc;
23909 btns.get('bold').setActive(doc.queryCommandState('bold'));
23910 btns.get('italic').setActive(doc.queryCommandState('italic'));
23911 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23913 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23914 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23915 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23917 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23918 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23921 var ans = this.editorcore.getAllAncestors();
23922 if (this.formatCombo) {
23925 var store = this.formatCombo.store;
23926 this.formatCombo.setValue("");
23927 for (var i =0; i < ans.length;i++) {
23928 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23930 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23938 // hides menus... - so this cant be on a menu...
23939 Roo.bootstrap.MenuMgr.hideAll();
23941 Roo.bootstrap.MenuMgr.hideAll();
23942 //this.editorsyncValue();
23944 onFirstFocus: function() {
23945 this.buttons.each(function(item){
23949 toggleSourceEdit : function(sourceEditMode){
23952 if(sourceEditMode){
23953 Roo.log("disabling buttons");
23954 this.buttons.each( function(item){
23955 if(item.cmd != 'pencil'){
23961 Roo.log("enabling buttons");
23962 if(this.editorcore.initialized){
23963 this.buttons.each( function(item){
23969 Roo.log("calling toggole on editor");
23970 // tell the editor that it's been pressed..
23971 this.editor.toggleSourceEdit(sourceEditMode);
23981 * @class Roo.bootstrap.Table.AbstractSelectionModel
23982 * @extends Roo.util.Observable
23983 * Abstract base class for grid SelectionModels. It provides the interface that should be
23984 * implemented by descendant classes. This class should not be directly instantiated.
23987 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23988 this.locked = false;
23989 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23993 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23994 /** @ignore Called by the grid automatically. Do not call directly. */
23995 init : function(grid){
24001 * Locks the selections.
24004 this.locked = true;
24008 * Unlocks the selections.
24010 unlock : function(){
24011 this.locked = false;
24015 * Returns true if the selections are locked.
24016 * @return {Boolean}
24018 isLocked : function(){
24019 return this.locked;
24023 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24024 * @class Roo.bootstrap.Table.RowSelectionModel
24025 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24026 * It supports multiple selections and keyboard selection/navigation.
24028 * @param {Object} config
24031 Roo.bootstrap.Table.RowSelectionModel = function(config){
24032 Roo.apply(this, config);
24033 this.selections = new Roo.util.MixedCollection(false, function(o){
24038 this.lastActive = false;
24042 * @event selectionchange
24043 * Fires when the selection changes
24044 * @param {SelectionModel} this
24046 "selectionchange" : true,
24048 * @event afterselectionchange
24049 * Fires after the selection changes (eg. by key press or clicking)
24050 * @param {SelectionModel} this
24052 "afterselectionchange" : true,
24054 * @event beforerowselect
24055 * Fires when a row is selected being selected, return false to cancel.
24056 * @param {SelectionModel} this
24057 * @param {Number} rowIndex The selected index
24058 * @param {Boolean} keepExisting False if other selections will be cleared
24060 "beforerowselect" : true,
24063 * Fires when a row is selected.
24064 * @param {SelectionModel} this
24065 * @param {Number} rowIndex The selected index
24066 * @param {Roo.data.Record} r The record
24068 "rowselect" : true,
24070 * @event rowdeselect
24071 * Fires when a row is deselected.
24072 * @param {SelectionModel} this
24073 * @param {Number} rowIndex The selected index
24075 "rowdeselect" : true
24077 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24078 this.locked = false;
24081 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24083 * @cfg {Boolean} singleSelect
24084 * True to allow selection of only one row at a time (defaults to false)
24086 singleSelect : false,
24089 initEvents : function()
24092 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24093 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24094 //}else{ // allow click to work like normal
24095 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24097 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24098 this.grid.on("rowclick", this.handleMouseDown, this);
24100 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24101 "up" : function(e){
24103 this.selectPrevious(e.shiftKey);
24104 }else if(this.last !== false && this.lastActive !== false){
24105 var last = this.last;
24106 this.selectRange(this.last, this.lastActive-1);
24107 this.grid.getView().focusRow(this.lastActive);
24108 if(last !== false){
24112 this.selectFirstRow();
24114 this.fireEvent("afterselectionchange", this);
24116 "down" : function(e){
24118 this.selectNext(e.shiftKey);
24119 }else if(this.last !== false && this.lastActive !== false){
24120 var last = this.last;
24121 this.selectRange(this.last, this.lastActive+1);
24122 this.grid.getView().focusRow(this.lastActive);
24123 if(last !== false){
24127 this.selectFirstRow();
24129 this.fireEvent("afterselectionchange", this);
24133 this.grid.store.on('load', function(){
24134 this.selections.clear();
24137 var view = this.grid.view;
24138 view.on("refresh", this.onRefresh, this);
24139 view.on("rowupdated", this.onRowUpdated, this);
24140 view.on("rowremoved", this.onRemove, this);
24145 onRefresh : function()
24147 var ds = this.grid.store, i, v = this.grid.view;
24148 var s = this.selections;
24149 s.each(function(r){
24150 if((i = ds.indexOfId(r.id)) != -1){
24159 onRemove : function(v, index, r){
24160 this.selections.remove(r);
24164 onRowUpdated : function(v, index, r){
24165 if(this.isSelected(r)){
24166 v.onRowSelect(index);
24172 * @param {Array} records The records to select
24173 * @param {Boolean} keepExisting (optional) True to keep existing selections
24175 selectRecords : function(records, keepExisting)
24178 this.clearSelections();
24180 var ds = this.grid.store;
24181 for(var i = 0, len = records.length; i < len; i++){
24182 this.selectRow(ds.indexOf(records[i]), true);
24187 * Gets the number of selected rows.
24190 getCount : function(){
24191 return this.selections.length;
24195 * Selects the first row in the grid.
24197 selectFirstRow : function(){
24202 * Select the last row.
24203 * @param {Boolean} keepExisting (optional) True to keep existing selections
24205 selectLastRow : function(keepExisting){
24206 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24207 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24211 * Selects the row immediately following the last selected row.
24212 * @param {Boolean} keepExisting (optional) True to keep existing selections
24214 selectNext : function(keepExisting)
24216 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24217 this.selectRow(this.last+1, keepExisting);
24218 this.grid.getView().focusRow(this.last);
24223 * Selects the row that precedes the last selected row.
24224 * @param {Boolean} keepExisting (optional) True to keep existing selections
24226 selectPrevious : function(keepExisting){
24228 this.selectRow(this.last-1, keepExisting);
24229 this.grid.getView().focusRow(this.last);
24234 * Returns the selected records
24235 * @return {Array} Array of selected records
24237 getSelections : function(){
24238 return [].concat(this.selections.items);
24242 * Returns the first selected record.
24245 getSelected : function(){
24246 return this.selections.itemAt(0);
24251 * Clears all selections.
24253 clearSelections : function(fast)
24259 var ds = this.grid.store;
24260 var s = this.selections;
24261 s.each(function(r){
24262 this.deselectRow(ds.indexOfId(r.id));
24266 this.selections.clear();
24273 * Selects all rows.
24275 selectAll : function(){
24279 this.selections.clear();
24280 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24281 this.selectRow(i, true);
24286 * Returns True if there is a selection.
24287 * @return {Boolean}
24289 hasSelection : function(){
24290 return this.selections.length > 0;
24294 * Returns True if the specified row is selected.
24295 * @param {Number/Record} record The record or index of the record to check
24296 * @return {Boolean}
24298 isSelected : function(index){
24299 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24300 return (r && this.selections.key(r.id) ? true : false);
24304 * Returns True if the specified record id is selected.
24305 * @param {String} id The id of record to check
24306 * @return {Boolean}
24308 isIdSelected : function(id){
24309 return (this.selections.key(id) ? true : false);
24314 handleMouseDBClick : function(e, t){
24318 handleMouseDown : function(e, t)
24320 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24321 if(this.isLocked() || rowIndex < 0 ){
24324 if(e.shiftKey && this.last !== false){
24325 var last = this.last;
24326 this.selectRange(last, rowIndex, e.ctrlKey);
24327 this.last = last; // reset the last
24331 var isSelected = this.isSelected(rowIndex);
24332 //Roo.log("select row:" + rowIndex);
24334 this.deselectRow(rowIndex);
24336 this.selectRow(rowIndex, true);
24340 if(e.button !== 0 && isSelected){
24341 alert('rowIndex 2: ' + rowIndex);
24342 view.focusRow(rowIndex);
24343 }else if(e.ctrlKey && isSelected){
24344 this.deselectRow(rowIndex);
24345 }else if(!isSelected){
24346 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24347 view.focusRow(rowIndex);
24351 this.fireEvent("afterselectionchange", this);
24354 handleDragableRowClick : function(grid, rowIndex, e)
24356 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24357 this.selectRow(rowIndex, false);
24358 grid.view.focusRow(rowIndex);
24359 this.fireEvent("afterselectionchange", this);
24364 * Selects multiple rows.
24365 * @param {Array} rows Array of the indexes of the row to select
24366 * @param {Boolean} keepExisting (optional) True to keep existing selections
24368 selectRows : function(rows, keepExisting){
24370 this.clearSelections();
24372 for(var i = 0, len = rows.length; i < len; i++){
24373 this.selectRow(rows[i], true);
24378 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24379 * @param {Number} startRow The index of the first row in the range
24380 * @param {Number} endRow The index of the last row in the range
24381 * @param {Boolean} keepExisting (optional) True to retain existing selections
24383 selectRange : function(startRow, endRow, keepExisting){
24388 this.clearSelections();
24390 if(startRow <= endRow){
24391 for(var i = startRow; i <= endRow; i++){
24392 this.selectRow(i, true);
24395 for(var i = startRow; i >= endRow; i--){
24396 this.selectRow(i, true);
24402 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24403 * @param {Number} startRow The index of the first row in the range
24404 * @param {Number} endRow The index of the last row in the range
24406 deselectRange : function(startRow, endRow, preventViewNotify){
24410 for(var i = startRow; i <= endRow; i++){
24411 this.deselectRow(i, preventViewNotify);
24417 * @param {Number} row The index of the row to select
24418 * @param {Boolean} keepExisting (optional) True to keep existing selections
24420 selectRow : function(index, keepExisting, preventViewNotify)
24422 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24425 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24426 if(!keepExisting || this.singleSelect){
24427 this.clearSelections();
24430 var r = this.grid.store.getAt(index);
24431 //console.log('selectRow - record id :' + r.id);
24433 this.selections.add(r);
24434 this.last = this.lastActive = index;
24435 if(!preventViewNotify){
24436 var proxy = new Roo.Element(
24437 this.grid.getRowDom(index)
24439 proxy.addClass('bg-info info');
24441 this.fireEvent("rowselect", this, index, r);
24442 this.fireEvent("selectionchange", this);
24448 * @param {Number} row The index of the row to deselect
24450 deselectRow : function(index, preventViewNotify)
24455 if(this.last == index){
24458 if(this.lastActive == index){
24459 this.lastActive = false;
24462 var r = this.grid.store.getAt(index);
24467 this.selections.remove(r);
24468 //.console.log('deselectRow - record id :' + r.id);
24469 if(!preventViewNotify){
24471 var proxy = new Roo.Element(
24472 this.grid.getRowDom(index)
24474 proxy.removeClass('bg-info info');
24476 this.fireEvent("rowdeselect", this, index);
24477 this.fireEvent("selectionchange", this);
24481 restoreLast : function(){
24483 this.last = this._last;
24488 acceptsNav : function(row, col, cm){
24489 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24493 onEditorKey : function(field, e){
24494 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24499 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24501 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24503 }else if(k == e.ENTER && !e.ctrlKey){
24507 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24509 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24511 }else if(k == e.ESC){
24515 g.startEditing(newCell[0], newCell[1]);
24521 * Ext JS Library 1.1.1
24522 * Copyright(c) 2006-2007, Ext JS, LLC.
24524 * Originally Released Under LGPL - original licence link has changed is not relivant.
24527 * <script type="text/javascript">
24531 * @class Roo.bootstrap.PagingToolbar
24532 * @extends Roo.bootstrap.NavSimplebar
24533 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24535 * Create a new PagingToolbar
24536 * @param {Object} config The config object
24537 * @param {Roo.data.Store} store
24539 Roo.bootstrap.PagingToolbar = function(config)
24541 // old args format still supported... - xtype is prefered..
24542 // created from xtype...
24544 this.ds = config.dataSource;
24546 if (config.store && !this.ds) {
24547 this.store= Roo.factory(config.store, Roo.data);
24548 this.ds = this.store;
24549 this.ds.xmodule = this.xmodule || false;
24552 this.toolbarItems = [];
24553 if (config.items) {
24554 this.toolbarItems = config.items;
24557 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24562 this.bind(this.ds);
24565 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24569 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24571 * @cfg {Roo.data.Store} dataSource
24572 * The underlying data store providing the paged data
24575 * @cfg {String/HTMLElement/Element} container
24576 * container The id or element that will contain the toolbar
24579 * @cfg {Boolean} displayInfo
24580 * True to display the displayMsg (defaults to false)
24583 * @cfg {Number} pageSize
24584 * The number of records to display per page (defaults to 20)
24588 * @cfg {String} displayMsg
24589 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24591 displayMsg : 'Displaying {0} - {1} of {2}',
24593 * @cfg {String} emptyMsg
24594 * The message to display when no records are found (defaults to "No data to display")
24596 emptyMsg : 'No data to display',
24598 * Customizable piece of the default paging text (defaults to "Page")
24601 beforePageText : "Page",
24603 * Customizable piece of the default paging text (defaults to "of %0")
24606 afterPageText : "of {0}",
24608 * Customizable piece of the default paging text (defaults to "First Page")
24611 firstText : "First Page",
24613 * Customizable piece of the default paging text (defaults to "Previous Page")
24616 prevText : "Previous Page",
24618 * Customizable piece of the default paging text (defaults to "Next Page")
24621 nextText : "Next Page",
24623 * Customizable piece of the default paging text (defaults to "Last Page")
24626 lastText : "Last Page",
24628 * Customizable piece of the default paging text (defaults to "Refresh")
24631 refreshText : "Refresh",
24635 onRender : function(ct, position)
24637 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24638 this.navgroup.parentId = this.id;
24639 this.navgroup.onRender(this.el, null);
24640 // add the buttons to the navgroup
24642 if(this.displayInfo){
24643 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24644 this.displayEl = this.el.select('.x-paging-info', true).first();
24645 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24646 // this.displayEl = navel.el.select('span',true).first();
24652 Roo.each(_this.buttons, function(e){ // this might need to use render????
24653 Roo.factory(e).render(_this.el);
24657 Roo.each(_this.toolbarItems, function(e) {
24658 _this.navgroup.addItem(e);
24662 this.first = this.navgroup.addItem({
24663 tooltip: this.firstText,
24665 icon : 'fa fa-backward',
24667 preventDefault: true,
24668 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24671 this.prev = this.navgroup.addItem({
24672 tooltip: this.prevText,
24674 icon : 'fa fa-step-backward',
24676 preventDefault: true,
24677 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24679 //this.addSeparator();
24682 var field = this.navgroup.addItem( {
24684 cls : 'x-paging-position',
24686 html : this.beforePageText +
24687 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24688 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24691 this.field = field.el.select('input', true).first();
24692 this.field.on("keydown", this.onPagingKeydown, this);
24693 this.field.on("focus", function(){this.dom.select();});
24696 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24697 //this.field.setHeight(18);
24698 //this.addSeparator();
24699 this.next = this.navgroup.addItem({
24700 tooltip: this.nextText,
24702 html : ' <i class="fa fa-step-forward">',
24704 preventDefault: true,
24705 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24707 this.last = this.navgroup.addItem({
24708 tooltip: this.lastText,
24709 icon : 'fa fa-forward',
24712 preventDefault: true,
24713 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24715 //this.addSeparator();
24716 this.loading = this.navgroup.addItem({
24717 tooltip: this.refreshText,
24718 icon: 'fa fa-refresh',
24719 preventDefault: true,
24720 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24726 updateInfo : function(){
24727 if(this.displayEl){
24728 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24729 var msg = count == 0 ?
24733 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24735 this.displayEl.update(msg);
24740 onLoad : function(ds, r, o)
24742 this.cursor = o.params.start ? o.params.start : 0;
24744 var d = this.getPageData(),
24749 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24750 this.field.dom.value = ap;
24751 this.first.setDisabled(ap == 1);
24752 this.prev.setDisabled(ap == 1);
24753 this.next.setDisabled(ap == ps);
24754 this.last.setDisabled(ap == ps);
24755 this.loading.enable();
24760 getPageData : function(){
24761 var total = this.ds.getTotalCount();
24764 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24765 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24770 onLoadError : function(){
24771 this.loading.enable();
24775 onPagingKeydown : function(e){
24776 var k = e.getKey();
24777 var d = this.getPageData();
24779 var v = this.field.dom.value, pageNum;
24780 if(!v || isNaN(pageNum = parseInt(v, 10))){
24781 this.field.dom.value = d.activePage;
24784 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24785 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24788 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))
24790 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24791 this.field.dom.value = pageNum;
24792 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24795 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24797 var v = this.field.dom.value, pageNum;
24798 var increment = (e.shiftKey) ? 10 : 1;
24799 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24802 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24803 this.field.dom.value = d.activePage;
24806 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24808 this.field.dom.value = parseInt(v, 10) + increment;
24809 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24810 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24817 beforeLoad : function(){
24819 this.loading.disable();
24824 onClick : function(which){
24833 ds.load({params:{start: 0, limit: this.pageSize}});
24836 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24839 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24842 var total = ds.getTotalCount();
24843 var extra = total % this.pageSize;
24844 var lastStart = extra ? (total - extra) : total-this.pageSize;
24845 ds.load({params:{start: lastStart, limit: this.pageSize}});
24848 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24854 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24855 * @param {Roo.data.Store} store The data store to unbind
24857 unbind : function(ds){
24858 ds.un("beforeload", this.beforeLoad, this);
24859 ds.un("load", this.onLoad, this);
24860 ds.un("loadexception", this.onLoadError, this);
24861 ds.un("remove", this.updateInfo, this);
24862 ds.un("add", this.updateInfo, this);
24863 this.ds = undefined;
24867 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24868 * @param {Roo.data.Store} store The data store to bind
24870 bind : function(ds){
24871 ds.on("beforeload", this.beforeLoad, this);
24872 ds.on("load", this.onLoad, this);
24873 ds.on("loadexception", this.onLoadError, this);
24874 ds.on("remove", this.updateInfo, this);
24875 ds.on("add", this.updateInfo, this);
24886 * @class Roo.bootstrap.MessageBar
24887 * @extends Roo.bootstrap.Component
24888 * Bootstrap MessageBar class
24889 * @cfg {String} html contents of the MessageBar
24890 * @cfg {String} weight (info | success | warning | danger) default info
24891 * @cfg {String} beforeClass insert the bar before the given class
24892 * @cfg {Boolean} closable (true | false) default false
24893 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24896 * Create a new Element
24897 * @param {Object} config The config object
24900 Roo.bootstrap.MessageBar = function(config){
24901 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24904 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24910 beforeClass: 'bootstrap-sticky-wrap',
24912 getAutoCreate : function(){
24916 cls: 'alert alert-dismissable alert-' + this.weight,
24921 html: this.html || ''
24927 cfg.cls += ' alert-messages-fixed';
24941 onRender : function(ct, position)
24943 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24946 var cfg = Roo.apply({}, this.getAutoCreate());
24950 cfg.cls += ' ' + this.cls;
24953 cfg.style = this.style;
24955 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24957 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24960 this.el.select('>button.close').on('click', this.hide, this);
24966 if (!this.rendered) {
24972 this.fireEvent('show', this);
24978 if (!this.rendered) {
24984 this.fireEvent('hide', this);
24987 update : function()
24989 // var e = this.el.dom.firstChild;
24991 // if(this.closable){
24992 // e = e.nextSibling;
24995 // e.data = this.html || '';
24997 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25013 * @class Roo.bootstrap.Graph
25014 * @extends Roo.bootstrap.Component
25015 * Bootstrap Graph class
25019 @cfg {String} graphtype bar | vbar | pie
25020 @cfg {number} g_x coodinator | centre x (pie)
25021 @cfg {number} g_y coodinator | centre y (pie)
25022 @cfg {number} g_r radius (pie)
25023 @cfg {number} g_height height of the chart (respected by all elements in the set)
25024 @cfg {number} g_width width of the chart (respected by all elements in the set)
25025 @cfg {Object} title The title of the chart
25028 -opts (object) options for the chart
25030 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25031 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25033 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.
25034 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25036 o stretch (boolean)
25038 -opts (object) options for the pie
25041 o startAngle (number)
25042 o endAngle (number)
25046 * Create a new Input
25047 * @param {Object} config The config object
25050 Roo.bootstrap.Graph = function(config){
25051 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25057 * The img click event for the img.
25058 * @param {Roo.EventObject} e
25064 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25075 //g_colors: this.colors,
25082 getAutoCreate : function(){
25093 onRender : function(ct,position){
25096 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25098 if (typeof(Raphael) == 'undefined') {
25099 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25103 this.raphael = Raphael(this.el.dom);
25105 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25106 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25107 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25108 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25110 r.text(160, 10, "Single Series Chart").attr(txtattr);
25111 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25112 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25113 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25115 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25116 r.barchart(330, 10, 300, 220, data1);
25117 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25118 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25121 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25122 // r.barchart(30, 30, 560, 250, xdata, {
25123 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25124 // axis : "0 0 1 1",
25125 // axisxlabels : xdata
25126 // //yvalues : cols,
25129 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25131 // this.load(null,xdata,{
25132 // axis : "0 0 1 1",
25133 // axisxlabels : xdata
25138 load : function(graphtype,xdata,opts)
25140 this.raphael.clear();
25142 graphtype = this.graphtype;
25147 var r = this.raphael,
25148 fin = function () {
25149 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25151 fout = function () {
25152 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25154 pfin = function() {
25155 this.sector.stop();
25156 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25159 this.label[0].stop();
25160 this.label[0].attr({ r: 7.5 });
25161 this.label[1].attr({ "font-weight": 800 });
25164 pfout = function() {
25165 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25168 this.label[0].animate({ r: 5 }, 500, "bounce");
25169 this.label[1].attr({ "font-weight": 400 });
25175 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25178 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25181 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25182 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25184 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25191 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25196 setTitle: function(o)
25201 initEvents: function() {
25204 this.el.on('click', this.onClick, this);
25208 onClick : function(e)
25210 Roo.log('img onclick');
25211 this.fireEvent('click', this, e);
25223 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25226 * @class Roo.bootstrap.dash.NumberBox
25227 * @extends Roo.bootstrap.Component
25228 * Bootstrap NumberBox class
25229 * @cfg {String} headline Box headline
25230 * @cfg {String} content Box content
25231 * @cfg {String} icon Box icon
25232 * @cfg {String} footer Footer text
25233 * @cfg {String} fhref Footer href
25236 * Create a new NumberBox
25237 * @param {Object} config The config object
25241 Roo.bootstrap.dash.NumberBox = function(config){
25242 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25246 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25255 getAutoCreate : function(){
25259 cls : 'small-box ',
25267 cls : 'roo-headline',
25268 html : this.headline
25272 cls : 'roo-content',
25273 html : this.content
25287 cls : 'ion ' + this.icon
25296 cls : 'small-box-footer',
25297 href : this.fhref || '#',
25301 cfg.cn.push(footer);
25308 onRender : function(ct,position){
25309 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25316 setHeadline: function (value)
25318 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25321 setFooter: function (value, href)
25323 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25326 this.el.select('a.small-box-footer',true).first().attr('href', href);
25331 setContent: function (value)
25333 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25336 initEvents: function()
25350 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25353 * @class Roo.bootstrap.dash.TabBox
25354 * @extends Roo.bootstrap.Component
25355 * Bootstrap TabBox class
25356 * @cfg {String} title Title of the TabBox
25357 * @cfg {String} icon Icon of the TabBox
25358 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25359 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25362 * Create a new TabBox
25363 * @param {Object} config The config object
25367 Roo.bootstrap.dash.TabBox = function(config){
25368 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25373 * When a pane is added
25374 * @param {Roo.bootstrap.dash.TabPane} pane
25378 * @event activatepane
25379 * When a pane is activated
25380 * @param {Roo.bootstrap.dash.TabPane} pane
25382 "activatepane" : true
25390 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25395 tabScrollable : false,
25397 getChildContainer : function()
25399 return this.el.select('.tab-content', true).first();
25402 getAutoCreate : function(){
25406 cls: 'pull-left header',
25414 cls: 'fa ' + this.icon
25420 cls: 'nav nav-tabs pull-right',
25426 if(this.tabScrollable){
25433 cls: 'nav nav-tabs pull-right',
25444 cls: 'nav-tabs-custom',
25449 cls: 'tab-content no-padding',
25457 initEvents : function()
25459 //Roo.log('add add pane handler');
25460 this.on('addpane', this.onAddPane, this);
25463 * Updates the box title
25464 * @param {String} html to set the title to.
25466 setTitle : function(value)
25468 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25470 onAddPane : function(pane)
25472 this.panes.push(pane);
25473 //Roo.log('addpane');
25475 // tabs are rendere left to right..
25476 if(!this.showtabs){
25480 var ctr = this.el.select('.nav-tabs', true).first();
25483 var existing = ctr.select('.nav-tab',true);
25484 var qty = existing.getCount();;
25487 var tab = ctr.createChild({
25489 cls : 'nav-tab' + (qty ? '' : ' active'),
25497 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25500 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25502 pane.el.addClass('active');
25507 onTabClick : function(ev,un,ob,pane)
25509 //Roo.log('tab - prev default');
25510 ev.preventDefault();
25513 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25514 pane.tab.addClass('active');
25515 //Roo.log(pane.title);
25516 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25517 // technically we should have a deactivate event.. but maybe add later.
25518 // and it should not de-activate the selected tab...
25519 this.fireEvent('activatepane', pane);
25520 pane.el.addClass('active');
25521 pane.fireEvent('activate');
25526 getActivePane : function()
25529 Roo.each(this.panes, function(p) {
25530 if(p.el.hasClass('active')){
25551 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25553 * @class Roo.bootstrap.TabPane
25554 * @extends Roo.bootstrap.Component
25555 * Bootstrap TabPane class
25556 * @cfg {Boolean} active (false | true) Default false
25557 * @cfg {String} title title of panel
25561 * Create a new TabPane
25562 * @param {Object} config The config object
25565 Roo.bootstrap.dash.TabPane = function(config){
25566 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25572 * When a pane is activated
25573 * @param {Roo.bootstrap.dash.TabPane} pane
25580 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25585 // the tabBox that this is attached to.
25588 getAutoCreate : function()
25596 cfg.cls += ' active';
25601 initEvents : function()
25603 //Roo.log('trigger add pane handler');
25604 this.parent().fireEvent('addpane', this)
25608 * Updates the tab title
25609 * @param {String} html to set the title to.
25611 setTitle: function(str)
25617 this.tab.select('a', true).first().dom.innerHTML = str;
25634 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25637 * @class Roo.bootstrap.menu.Menu
25638 * @extends Roo.bootstrap.Component
25639 * Bootstrap Menu class - container for Menu
25640 * @cfg {String} html Text of the menu
25641 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25642 * @cfg {String} icon Font awesome icon
25643 * @cfg {String} pos Menu align to (top | bottom) default bottom
25647 * Create a new Menu
25648 * @param {Object} config The config object
25652 Roo.bootstrap.menu.Menu = function(config){
25653 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25657 * @event beforeshow
25658 * Fires before this menu is displayed
25659 * @param {Roo.bootstrap.menu.Menu} this
25663 * @event beforehide
25664 * Fires before this menu is hidden
25665 * @param {Roo.bootstrap.menu.Menu} this
25670 * Fires after this menu is displayed
25671 * @param {Roo.bootstrap.menu.Menu} this
25676 * Fires after this menu is hidden
25677 * @param {Roo.bootstrap.menu.Menu} this
25682 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25683 * @param {Roo.bootstrap.menu.Menu} this
25684 * @param {Roo.EventObject} e
25691 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25695 weight : 'default',
25700 getChildContainer : function() {
25701 if(this.isSubMenu){
25705 return this.el.select('ul.dropdown-menu', true).first();
25708 getAutoCreate : function()
25713 cls : 'roo-menu-text',
25721 cls : 'fa ' + this.icon
25732 cls : 'dropdown-button btn btn-' + this.weight,
25737 cls : 'dropdown-toggle btn btn-' + this.weight,
25747 cls : 'dropdown-menu'
25753 if(this.pos == 'top'){
25754 cfg.cls += ' dropup';
25757 if(this.isSubMenu){
25760 cls : 'dropdown-menu'
25767 onRender : function(ct, position)
25769 this.isSubMenu = ct.hasClass('dropdown-submenu');
25771 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25774 initEvents : function()
25776 if(this.isSubMenu){
25780 this.hidden = true;
25782 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25783 this.triggerEl.on('click', this.onTriggerPress, this);
25785 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25786 this.buttonEl.on('click', this.onClick, this);
25792 if(this.isSubMenu){
25796 return this.el.select('ul.dropdown-menu', true).first();
25799 onClick : function(e)
25801 this.fireEvent("click", this, e);
25804 onTriggerPress : function(e)
25806 if (this.isVisible()) {
25813 isVisible : function(){
25814 return !this.hidden;
25819 this.fireEvent("beforeshow", this);
25821 this.hidden = false;
25822 this.el.addClass('open');
25824 Roo.get(document).on("mouseup", this.onMouseUp, this);
25826 this.fireEvent("show", this);
25833 this.fireEvent("beforehide", this);
25835 this.hidden = true;
25836 this.el.removeClass('open');
25838 Roo.get(document).un("mouseup", this.onMouseUp);
25840 this.fireEvent("hide", this);
25843 onMouseUp : function()
25857 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25860 * @class Roo.bootstrap.menu.Item
25861 * @extends Roo.bootstrap.Component
25862 * Bootstrap MenuItem class
25863 * @cfg {Boolean} submenu (true | false) default false
25864 * @cfg {String} html text of the item
25865 * @cfg {String} href the link
25866 * @cfg {Boolean} disable (true | false) default false
25867 * @cfg {Boolean} preventDefault (true | false) default true
25868 * @cfg {String} icon Font awesome icon
25869 * @cfg {String} pos Submenu align to (left | right) default right
25873 * Create a new Item
25874 * @param {Object} config The config object
25878 Roo.bootstrap.menu.Item = function(config){
25879 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25883 * Fires when the mouse is hovering over this menu
25884 * @param {Roo.bootstrap.menu.Item} this
25885 * @param {Roo.EventObject} e
25890 * Fires when the mouse exits this menu
25891 * @param {Roo.bootstrap.menu.Item} this
25892 * @param {Roo.EventObject} e
25898 * The raw click event for the entire grid.
25899 * @param {Roo.EventObject} e
25905 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25910 preventDefault: true,
25915 getAutoCreate : function()
25920 cls : 'roo-menu-item-text',
25928 cls : 'fa ' + this.icon
25937 href : this.href || '#',
25944 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25948 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25950 if(this.pos == 'left'){
25951 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25958 initEvents : function()
25960 this.el.on('mouseover', this.onMouseOver, this);
25961 this.el.on('mouseout', this.onMouseOut, this);
25963 this.el.select('a', true).first().on('click', this.onClick, this);
25967 onClick : function(e)
25969 if(this.preventDefault){
25970 e.preventDefault();
25973 this.fireEvent("click", this, e);
25976 onMouseOver : function(e)
25978 if(this.submenu && this.pos == 'left'){
25979 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25982 this.fireEvent("mouseover", this, e);
25985 onMouseOut : function(e)
25987 this.fireEvent("mouseout", this, e);
25999 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26002 * @class Roo.bootstrap.menu.Separator
26003 * @extends Roo.bootstrap.Component
26004 * Bootstrap Separator class
26007 * Create a new Separator
26008 * @param {Object} config The config object
26012 Roo.bootstrap.menu.Separator = function(config){
26013 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26016 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26018 getAutoCreate : function(){
26039 * @class Roo.bootstrap.Tooltip
26040 * Bootstrap Tooltip class
26041 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26042 * to determine which dom element triggers the tooltip.
26044 * It needs to add support for additional attributes like tooltip-position
26047 * Create a new Toolti
26048 * @param {Object} config The config object
26051 Roo.bootstrap.Tooltip = function(config){
26052 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26054 this.alignment = Roo.bootstrap.Tooltip.alignment;
26056 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26057 this.alignment = config.alignment;
26062 Roo.apply(Roo.bootstrap.Tooltip, {
26064 * @function init initialize tooltip monitoring.
26068 currentTip : false,
26069 currentRegion : false,
26075 Roo.get(document).on('mouseover', this.enter ,this);
26076 Roo.get(document).on('mouseout', this.leave, this);
26079 this.currentTip = new Roo.bootstrap.Tooltip();
26082 enter : function(ev)
26084 var dom = ev.getTarget();
26086 //Roo.log(['enter',dom]);
26087 var el = Roo.fly(dom);
26088 if (this.currentEl) {
26090 //Roo.log(this.currentEl);
26091 //Roo.log(this.currentEl.contains(dom));
26092 if (this.currentEl == el) {
26095 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26101 if (this.currentTip.el) {
26102 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26106 if(!el || el.dom == document){
26112 // you can not look for children, as if el is the body.. then everythign is the child..
26113 if (!el.attr('tooltip')) { //
26114 if (!el.select("[tooltip]").elements.length) {
26117 // is the mouse over this child...?
26118 bindEl = el.select("[tooltip]").first();
26119 var xy = ev.getXY();
26120 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26121 //Roo.log("not in region.");
26124 //Roo.log("child element over..");
26127 this.currentEl = bindEl;
26128 this.currentTip.bind(bindEl);
26129 this.currentRegion = Roo.lib.Region.getRegion(dom);
26130 this.currentTip.enter();
26133 leave : function(ev)
26135 var dom = ev.getTarget();
26136 //Roo.log(['leave',dom]);
26137 if (!this.currentEl) {
26142 if (dom != this.currentEl.dom) {
26145 var xy = ev.getXY();
26146 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26149 // only activate leave if mouse cursor is outside... bounding box..
26154 if (this.currentTip) {
26155 this.currentTip.leave();
26157 //Roo.log('clear currentEl');
26158 this.currentEl = false;
26163 'left' : ['r-l', [-2,0], 'right'],
26164 'right' : ['l-r', [2,0], 'left'],
26165 'bottom' : ['t-b', [0,2], 'top'],
26166 'top' : [ 'b-t', [0,-2], 'bottom']
26172 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26177 delay : null, // can be { show : 300 , hide: 500}
26181 hoverState : null, //???
26183 placement : 'bottom',
26187 getAutoCreate : function(){
26194 cls : 'tooltip-arrow'
26197 cls : 'tooltip-inner'
26204 bind : function(el)
26210 enter : function () {
26212 if (this.timeout != null) {
26213 clearTimeout(this.timeout);
26216 this.hoverState = 'in';
26217 //Roo.log("enter - show");
26218 if (!this.delay || !this.delay.show) {
26223 this.timeout = setTimeout(function () {
26224 if (_t.hoverState == 'in') {
26227 }, this.delay.show);
26231 clearTimeout(this.timeout);
26233 this.hoverState = 'out';
26234 if (!this.delay || !this.delay.hide) {
26240 this.timeout = setTimeout(function () {
26241 //Roo.log("leave - timeout");
26243 if (_t.hoverState == 'out') {
26245 Roo.bootstrap.Tooltip.currentEl = false;
26250 show : function (msg)
26253 this.render(document.body);
26256 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26258 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26260 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26262 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26264 var placement = typeof this.placement == 'function' ?
26265 this.placement.call(this, this.el, on_el) :
26268 var autoToken = /\s?auto?\s?/i;
26269 var autoPlace = autoToken.test(placement);
26271 placement = placement.replace(autoToken, '') || 'top';
26275 //this.el.setXY([0,0]);
26277 //this.el.dom.style.display='block';
26279 //this.el.appendTo(on_el);
26281 var p = this.getPosition();
26282 var box = this.el.getBox();
26288 var align = this.alignment[placement];
26290 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26292 if(placement == 'top' || placement == 'bottom'){
26294 placement = 'right';
26297 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26298 placement = 'left';
26301 var scroll = Roo.select('body', true).first().getScroll();
26303 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26307 align = this.alignment[placement];
26310 this.el.alignTo(this.bindEl, align[0],align[1]);
26311 //var arrow = this.el.select('.arrow',true).first();
26312 //arrow.set(align[2],
26314 this.el.addClass(placement);
26316 this.el.addClass('in fade');
26318 this.hoverState = null;
26320 if (this.el.hasClass('fade')) {
26331 //this.el.setXY([0,0]);
26332 this.el.removeClass('in');
26348 * @class Roo.bootstrap.LocationPicker
26349 * @extends Roo.bootstrap.Component
26350 * Bootstrap LocationPicker class
26351 * @cfg {Number} latitude Position when init default 0
26352 * @cfg {Number} longitude Position when init default 0
26353 * @cfg {Number} zoom default 15
26354 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26355 * @cfg {Boolean} mapTypeControl default false
26356 * @cfg {Boolean} disableDoubleClickZoom default false
26357 * @cfg {Boolean} scrollwheel default true
26358 * @cfg {Boolean} streetViewControl default false
26359 * @cfg {Number} radius default 0
26360 * @cfg {String} locationName
26361 * @cfg {Boolean} draggable default true
26362 * @cfg {Boolean} enableAutocomplete default false
26363 * @cfg {Boolean} enableReverseGeocode default true
26364 * @cfg {String} markerTitle
26367 * Create a new LocationPicker
26368 * @param {Object} config The config object
26372 Roo.bootstrap.LocationPicker = function(config){
26374 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26379 * Fires when the picker initialized.
26380 * @param {Roo.bootstrap.LocationPicker} this
26381 * @param {Google Location} location
26385 * @event positionchanged
26386 * Fires when the picker position changed.
26387 * @param {Roo.bootstrap.LocationPicker} this
26388 * @param {Google Location} location
26390 positionchanged : true,
26393 * Fires when the map resize.
26394 * @param {Roo.bootstrap.LocationPicker} this
26399 * Fires when the map show.
26400 * @param {Roo.bootstrap.LocationPicker} this
26405 * Fires when the map hide.
26406 * @param {Roo.bootstrap.LocationPicker} this
26411 * Fires when click the map.
26412 * @param {Roo.bootstrap.LocationPicker} this
26413 * @param {Map event} e
26417 * @event mapRightClick
26418 * Fires when right click the map.
26419 * @param {Roo.bootstrap.LocationPicker} this
26420 * @param {Map event} e
26422 mapRightClick : true,
26424 * @event markerClick
26425 * Fires when click the marker.
26426 * @param {Roo.bootstrap.LocationPicker} this
26427 * @param {Map event} e
26429 markerClick : true,
26431 * @event markerRightClick
26432 * Fires when right click the marker.
26433 * @param {Roo.bootstrap.LocationPicker} this
26434 * @param {Map event} e
26436 markerRightClick : true,
26438 * @event OverlayViewDraw
26439 * Fires when OverlayView Draw
26440 * @param {Roo.bootstrap.LocationPicker} this
26442 OverlayViewDraw : true,
26444 * @event OverlayViewOnAdd
26445 * Fires when OverlayView Draw
26446 * @param {Roo.bootstrap.LocationPicker} this
26448 OverlayViewOnAdd : true,
26450 * @event OverlayViewOnRemove
26451 * Fires when OverlayView Draw
26452 * @param {Roo.bootstrap.LocationPicker} this
26454 OverlayViewOnRemove : true,
26456 * @event OverlayViewShow
26457 * Fires when OverlayView Draw
26458 * @param {Roo.bootstrap.LocationPicker} this
26459 * @param {Pixel} cpx
26461 OverlayViewShow : true,
26463 * @event OverlayViewHide
26464 * Fires when OverlayView Draw
26465 * @param {Roo.bootstrap.LocationPicker} this
26467 OverlayViewHide : true,
26469 * @event loadexception
26470 * Fires when load google lib failed.
26471 * @param {Roo.bootstrap.LocationPicker} this
26473 loadexception : true
26478 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26480 gMapContext: false,
26486 mapTypeControl: false,
26487 disableDoubleClickZoom: false,
26489 streetViewControl: false,
26493 enableAutocomplete: false,
26494 enableReverseGeocode: true,
26497 getAutoCreate: function()
26502 cls: 'roo-location-picker'
26508 initEvents: function(ct, position)
26510 if(!this.el.getWidth() || this.isApplied()){
26514 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26519 initial: function()
26521 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26522 this.fireEvent('loadexception', this);
26526 if(!this.mapTypeId){
26527 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26530 this.gMapContext = this.GMapContext();
26532 this.initOverlayView();
26534 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26538 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26539 _this.setPosition(_this.gMapContext.marker.position);
26542 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26543 _this.fireEvent('mapClick', this, event);
26547 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26548 _this.fireEvent('mapRightClick', this, event);
26552 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26553 _this.fireEvent('markerClick', this, event);
26557 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26558 _this.fireEvent('markerRightClick', this, event);
26562 this.setPosition(this.gMapContext.location);
26564 this.fireEvent('initial', this, this.gMapContext.location);
26567 initOverlayView: function()
26571 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26575 _this.fireEvent('OverlayViewDraw', _this);
26580 _this.fireEvent('OverlayViewOnAdd', _this);
26583 onRemove: function()
26585 _this.fireEvent('OverlayViewOnRemove', _this);
26588 show: function(cpx)
26590 _this.fireEvent('OverlayViewShow', _this, cpx);
26595 _this.fireEvent('OverlayViewHide', _this);
26601 fromLatLngToContainerPixel: function(event)
26603 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26606 isApplied: function()
26608 return this.getGmapContext() == false ? false : true;
26611 getGmapContext: function()
26613 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26616 GMapContext: function()
26618 var position = new google.maps.LatLng(this.latitude, this.longitude);
26620 var _map = new google.maps.Map(this.el.dom, {
26623 mapTypeId: this.mapTypeId,
26624 mapTypeControl: this.mapTypeControl,
26625 disableDoubleClickZoom: this.disableDoubleClickZoom,
26626 scrollwheel: this.scrollwheel,
26627 streetViewControl: this.streetViewControl,
26628 locationName: this.locationName,
26629 draggable: this.draggable,
26630 enableAutocomplete: this.enableAutocomplete,
26631 enableReverseGeocode: this.enableReverseGeocode
26634 var _marker = new google.maps.Marker({
26635 position: position,
26637 title: this.markerTitle,
26638 draggable: this.draggable
26645 location: position,
26646 radius: this.radius,
26647 locationName: this.locationName,
26648 addressComponents: {
26649 formatted_address: null,
26650 addressLine1: null,
26651 addressLine2: null,
26653 streetNumber: null,
26657 stateOrProvince: null
26660 domContainer: this.el.dom,
26661 geodecoder: new google.maps.Geocoder()
26665 drawCircle: function(center, radius, options)
26667 if (this.gMapContext.circle != null) {
26668 this.gMapContext.circle.setMap(null);
26672 options = Roo.apply({}, options, {
26673 strokeColor: "#0000FF",
26674 strokeOpacity: .35,
26676 fillColor: "#0000FF",
26680 options.map = this.gMapContext.map;
26681 options.radius = radius;
26682 options.center = center;
26683 this.gMapContext.circle = new google.maps.Circle(options);
26684 return this.gMapContext.circle;
26690 setPosition: function(location)
26692 this.gMapContext.location = location;
26693 this.gMapContext.marker.setPosition(location);
26694 this.gMapContext.map.panTo(location);
26695 this.drawCircle(location, this.gMapContext.radius, {});
26699 if (this.gMapContext.settings.enableReverseGeocode) {
26700 this.gMapContext.geodecoder.geocode({
26701 latLng: this.gMapContext.location
26702 }, function(results, status) {
26704 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26705 _this.gMapContext.locationName = results[0].formatted_address;
26706 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26708 _this.fireEvent('positionchanged', this, location);
26715 this.fireEvent('positionchanged', this, location);
26720 google.maps.event.trigger(this.gMapContext.map, "resize");
26722 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26724 this.fireEvent('resize', this);
26727 setPositionByLatLng: function(latitude, longitude)
26729 this.setPosition(new google.maps.LatLng(latitude, longitude));
26732 getCurrentPosition: function()
26735 latitude: this.gMapContext.location.lat(),
26736 longitude: this.gMapContext.location.lng()
26740 getAddressName: function()
26742 return this.gMapContext.locationName;
26745 getAddressComponents: function()
26747 return this.gMapContext.addressComponents;
26750 address_component_from_google_geocode: function(address_components)
26754 for (var i = 0; i < address_components.length; i++) {
26755 var component = address_components[i];
26756 if (component.types.indexOf("postal_code") >= 0) {
26757 result.postalCode = component.short_name;
26758 } else if (component.types.indexOf("street_number") >= 0) {
26759 result.streetNumber = component.short_name;
26760 } else if (component.types.indexOf("route") >= 0) {
26761 result.streetName = component.short_name;
26762 } else if (component.types.indexOf("neighborhood") >= 0) {
26763 result.city = component.short_name;
26764 } else if (component.types.indexOf("locality") >= 0) {
26765 result.city = component.short_name;
26766 } else if (component.types.indexOf("sublocality") >= 0) {
26767 result.district = component.short_name;
26768 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26769 result.stateOrProvince = component.short_name;
26770 } else if (component.types.indexOf("country") >= 0) {
26771 result.country = component.short_name;
26775 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26776 result.addressLine2 = "";
26780 setZoomLevel: function(zoom)
26782 this.gMapContext.map.setZoom(zoom);
26795 this.fireEvent('show', this);
26806 this.fireEvent('hide', this);
26811 Roo.apply(Roo.bootstrap.LocationPicker, {
26813 OverlayView : function(map, options)
26815 options = options || {};
26829 * @class Roo.bootstrap.Alert
26830 * @extends Roo.bootstrap.Component
26831 * Bootstrap Alert class
26832 * @cfg {String} title The title of alert
26833 * @cfg {String} html The content of alert
26834 * @cfg {String} weight ( success | info | warning | danger )
26835 * @cfg {String} faicon font-awesomeicon
26838 * Create a new alert
26839 * @param {Object} config The config object
26843 Roo.bootstrap.Alert = function(config){
26844 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26848 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26855 getAutoCreate : function()
26864 cls : 'roo-alert-icon'
26869 cls : 'roo-alert-title',
26874 cls : 'roo-alert-text',
26881 cfg.cn[0].cls += ' fa ' + this.faicon;
26885 cfg.cls += ' alert-' + this.weight;
26891 initEvents: function()
26893 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26896 setTitle : function(str)
26898 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26901 setText : function(str)
26903 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26906 setWeight : function(weight)
26909 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26912 this.weight = weight;
26914 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26917 setIcon : function(icon)
26920 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26923 this.faicon = icon;
26925 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26946 * @class Roo.bootstrap.UploadCropbox
26947 * @extends Roo.bootstrap.Component
26948 * Bootstrap UploadCropbox class
26949 * @cfg {String} emptyText show when image has been loaded
26950 * @cfg {String} rotateNotify show when image too small to rotate
26951 * @cfg {Number} errorTimeout default 3000
26952 * @cfg {Number} minWidth default 300
26953 * @cfg {Number} minHeight default 300
26954 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26955 * @cfg {Boolean} isDocument (true|false) default false
26956 * @cfg {String} url action url
26957 * @cfg {String} paramName default 'imageUpload'
26958 * @cfg {String} method default POST
26959 * @cfg {Boolean} loadMask (true|false) default true
26960 * @cfg {Boolean} loadingText default 'Loading...'
26963 * Create a new UploadCropbox
26964 * @param {Object} config The config object
26967 Roo.bootstrap.UploadCropbox = function(config){
26968 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26972 * @event beforeselectfile
26973 * Fire before select file
26974 * @param {Roo.bootstrap.UploadCropbox} this
26976 "beforeselectfile" : true,
26979 * Fire after initEvent
26980 * @param {Roo.bootstrap.UploadCropbox} this
26985 * Fire after initEvent
26986 * @param {Roo.bootstrap.UploadCropbox} this
26987 * @param {String} data
26992 * Fire when preparing the file data
26993 * @param {Roo.bootstrap.UploadCropbox} this
26994 * @param {Object} file
26999 * Fire when get exception
27000 * @param {Roo.bootstrap.UploadCropbox} this
27001 * @param {XMLHttpRequest} xhr
27003 "exception" : true,
27005 * @event beforeloadcanvas
27006 * Fire before load the canvas
27007 * @param {Roo.bootstrap.UploadCropbox} this
27008 * @param {String} src
27010 "beforeloadcanvas" : true,
27013 * Fire when trash image
27014 * @param {Roo.bootstrap.UploadCropbox} this
27019 * Fire when download the image
27020 * @param {Roo.bootstrap.UploadCropbox} this
27024 * @event footerbuttonclick
27025 * Fire when footerbuttonclick
27026 * @param {Roo.bootstrap.UploadCropbox} this
27027 * @param {String} type
27029 "footerbuttonclick" : true,
27033 * @param {Roo.bootstrap.UploadCropbox} this
27038 * Fire when rotate the image
27039 * @param {Roo.bootstrap.UploadCropbox} this
27040 * @param {String} pos
27045 * Fire when inspect the file
27046 * @param {Roo.bootstrap.UploadCropbox} this
27047 * @param {Object} file
27052 * Fire when xhr upload the file
27053 * @param {Roo.bootstrap.UploadCropbox} this
27054 * @param {Object} data
27059 * Fire when arrange the file data
27060 * @param {Roo.bootstrap.UploadCropbox} this
27061 * @param {Object} formData
27066 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27069 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27071 emptyText : 'Click to upload image',
27072 rotateNotify : 'Image is too small to rotate',
27073 errorTimeout : 3000,
27087 cropType : 'image/jpeg',
27089 canvasLoaded : false,
27090 isDocument : false,
27092 paramName : 'imageUpload',
27094 loadingText : 'Loading...',
27097 getAutoCreate : function()
27101 cls : 'roo-upload-cropbox',
27105 cls : 'roo-upload-cropbox-selector',
27110 cls : 'roo-upload-cropbox-body',
27111 style : 'cursor:pointer',
27115 cls : 'roo-upload-cropbox-preview'
27119 cls : 'roo-upload-cropbox-thumb'
27123 cls : 'roo-upload-cropbox-empty-notify',
27124 html : this.emptyText
27128 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27129 html : this.rotateNotify
27135 cls : 'roo-upload-cropbox-footer',
27138 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27148 onRender : function(ct, position)
27150 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27152 if (this.buttons.length) {
27154 Roo.each(this.buttons, function(bb) {
27156 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27158 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27164 this.maskEl = this.el;
27168 initEvents : function()
27170 this.urlAPI = (window.createObjectURL && window) ||
27171 (window.URL && URL.revokeObjectURL && URL) ||
27172 (window.webkitURL && webkitURL);
27174 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27175 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27177 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27178 this.selectorEl.hide();
27180 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27181 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27183 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27184 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27185 this.thumbEl.hide();
27187 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27188 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27190 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27191 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27192 this.errorEl.hide();
27194 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27195 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27196 this.footerEl.hide();
27198 this.setThumbBoxSize();
27204 this.fireEvent('initial', this);
27211 window.addEventListener("resize", function() { _this.resize(); } );
27213 this.bodyEl.on('click', this.beforeSelectFile, this);
27216 this.bodyEl.on('touchstart', this.onTouchStart, this);
27217 this.bodyEl.on('touchmove', this.onTouchMove, this);
27218 this.bodyEl.on('touchend', this.onTouchEnd, this);
27222 this.bodyEl.on('mousedown', this.onMouseDown, this);
27223 this.bodyEl.on('mousemove', this.onMouseMove, this);
27224 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27225 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27226 Roo.get(document).on('mouseup', this.onMouseUp, this);
27229 this.selectorEl.on('change', this.onFileSelected, this);
27235 this.baseScale = 1;
27237 this.baseRotate = 1;
27238 this.dragable = false;
27239 this.pinching = false;
27242 this.cropData = false;
27243 this.notifyEl.dom.innerHTML = this.emptyText;
27245 this.selectorEl.dom.value = '';
27249 resize : function()
27251 if(this.fireEvent('resize', this) != false){
27252 this.setThumbBoxPosition();
27253 this.setCanvasPosition();
27257 onFooterButtonClick : function(e, el, o, type)
27260 case 'rotate-left' :
27261 this.onRotateLeft(e);
27263 case 'rotate-right' :
27264 this.onRotateRight(e);
27267 this.beforeSelectFile(e);
27282 this.fireEvent('footerbuttonclick', this, type);
27285 beforeSelectFile : function(e)
27287 e.preventDefault();
27289 if(this.fireEvent('beforeselectfile', this) != false){
27290 this.selectorEl.dom.click();
27294 onFileSelected : function(e)
27296 e.preventDefault();
27298 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27302 var file = this.selectorEl.dom.files[0];
27304 if(this.fireEvent('inspect', this, file) != false){
27305 this.prepare(file);
27310 trash : function(e)
27312 this.fireEvent('trash', this);
27315 download : function(e)
27317 this.fireEvent('download', this);
27320 loadCanvas : function(src)
27322 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27326 this.imageEl = document.createElement('img');
27330 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27332 this.imageEl.src = src;
27336 onLoadCanvas : function()
27338 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27339 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27341 this.bodyEl.un('click', this.beforeSelectFile, this);
27343 this.notifyEl.hide();
27344 this.thumbEl.show();
27345 this.footerEl.show();
27347 this.baseRotateLevel();
27349 if(this.isDocument){
27350 this.setThumbBoxSize();
27353 this.setThumbBoxPosition();
27355 this.baseScaleLevel();
27361 this.canvasLoaded = true;
27364 this.maskEl.unmask();
27369 setCanvasPosition : function()
27371 if(!this.canvasEl){
27375 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27376 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27378 this.previewEl.setLeft(pw);
27379 this.previewEl.setTop(ph);
27383 onMouseDown : function(e)
27387 this.dragable = true;
27388 this.pinching = false;
27390 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27391 this.dragable = false;
27395 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27396 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27400 onMouseMove : function(e)
27404 if(!this.canvasLoaded){
27408 if (!this.dragable){
27412 var minX = Math.ceil(this.thumbEl.getLeft(true));
27413 var minY = Math.ceil(this.thumbEl.getTop(true));
27415 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27416 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27418 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27419 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27421 x = x - this.mouseX;
27422 y = y - this.mouseY;
27424 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27425 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27427 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27428 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27430 this.previewEl.setLeft(bgX);
27431 this.previewEl.setTop(bgY);
27433 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27434 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27437 onMouseUp : function(e)
27441 this.dragable = false;
27444 onMouseWheel : function(e)
27448 this.startScale = this.scale;
27450 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27452 if(!this.zoomable()){
27453 this.scale = this.startScale;
27462 zoomable : function()
27464 var minScale = this.thumbEl.getWidth() / this.minWidth;
27466 if(this.minWidth < this.minHeight){
27467 minScale = this.thumbEl.getHeight() / this.minHeight;
27470 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27471 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27475 (this.rotate == 0 || this.rotate == 180) &&
27477 width > this.imageEl.OriginWidth ||
27478 height > this.imageEl.OriginHeight ||
27479 (width < this.minWidth && height < this.minHeight)
27487 (this.rotate == 90 || this.rotate == 270) &&
27489 width > this.imageEl.OriginWidth ||
27490 height > this.imageEl.OriginHeight ||
27491 (width < this.minHeight && height < this.minWidth)
27498 !this.isDocument &&
27499 (this.rotate == 0 || this.rotate == 180) &&
27501 width < this.minWidth ||
27502 width > this.imageEl.OriginWidth ||
27503 height < this.minHeight ||
27504 height > this.imageEl.OriginHeight
27511 !this.isDocument &&
27512 (this.rotate == 90 || this.rotate == 270) &&
27514 width < this.minHeight ||
27515 width > this.imageEl.OriginWidth ||
27516 height < this.minWidth ||
27517 height > this.imageEl.OriginHeight
27527 onRotateLeft : function(e)
27529 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27531 var minScale = this.thumbEl.getWidth() / this.minWidth;
27533 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27534 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27536 this.startScale = this.scale;
27538 while (this.getScaleLevel() < minScale){
27540 this.scale = this.scale + 1;
27542 if(!this.zoomable()){
27547 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27548 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27553 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27560 this.scale = this.startScale;
27562 this.onRotateFail();
27567 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27569 if(this.isDocument){
27570 this.setThumbBoxSize();
27571 this.setThumbBoxPosition();
27572 this.setCanvasPosition();
27577 this.fireEvent('rotate', this, 'left');
27581 onRotateRight : function(e)
27583 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27585 var minScale = this.thumbEl.getWidth() / this.minWidth;
27587 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27588 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27590 this.startScale = this.scale;
27592 while (this.getScaleLevel() < minScale){
27594 this.scale = this.scale + 1;
27596 if(!this.zoomable()){
27601 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27602 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27607 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27614 this.scale = this.startScale;
27616 this.onRotateFail();
27621 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27623 if(this.isDocument){
27624 this.setThumbBoxSize();
27625 this.setThumbBoxPosition();
27626 this.setCanvasPosition();
27631 this.fireEvent('rotate', this, 'right');
27634 onRotateFail : function()
27636 this.errorEl.show(true);
27640 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27645 this.previewEl.dom.innerHTML = '';
27647 var canvasEl = document.createElement("canvas");
27649 var contextEl = canvasEl.getContext("2d");
27651 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27652 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27653 var center = this.imageEl.OriginWidth / 2;
27655 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27656 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27657 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27658 center = this.imageEl.OriginHeight / 2;
27661 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27663 contextEl.translate(center, center);
27664 contextEl.rotate(this.rotate * Math.PI / 180);
27666 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27668 this.canvasEl = document.createElement("canvas");
27670 this.contextEl = this.canvasEl.getContext("2d");
27672 switch (this.rotate) {
27675 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27676 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27678 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27683 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27684 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27686 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27687 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);
27691 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27696 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27697 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27699 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27700 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);
27704 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);
27709 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27710 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27712 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27713 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27717 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);
27724 this.previewEl.appendChild(this.canvasEl);
27726 this.setCanvasPosition();
27731 if(!this.canvasLoaded){
27735 var imageCanvas = document.createElement("canvas");
27737 var imageContext = imageCanvas.getContext("2d");
27739 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27740 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27742 var center = imageCanvas.width / 2;
27744 imageContext.translate(center, center);
27746 imageContext.rotate(this.rotate * Math.PI / 180);
27748 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27750 var canvas = document.createElement("canvas");
27752 var context = canvas.getContext("2d");
27754 canvas.width = this.minWidth;
27755 canvas.height = this.minHeight;
27757 switch (this.rotate) {
27760 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27761 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27763 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27764 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27766 var targetWidth = this.minWidth - 2 * x;
27767 var targetHeight = this.minHeight - 2 * y;
27771 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27772 scale = targetWidth / width;
27775 if(x > 0 && y == 0){
27776 scale = targetHeight / height;
27779 if(x > 0 && y > 0){
27780 scale = targetWidth / width;
27782 if(width < height){
27783 scale = targetHeight / height;
27787 context.scale(scale, scale);
27789 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27790 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27792 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27793 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27795 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27800 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27801 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27803 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27804 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27806 var targetWidth = this.minWidth - 2 * x;
27807 var targetHeight = this.minHeight - 2 * y;
27811 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27812 scale = targetWidth / width;
27815 if(x > 0 && y == 0){
27816 scale = targetHeight / height;
27819 if(x > 0 && y > 0){
27820 scale = targetWidth / width;
27822 if(width < height){
27823 scale = targetHeight / height;
27827 context.scale(scale, scale);
27829 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27830 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27832 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27833 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27835 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27837 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27842 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27843 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27845 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27846 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27848 var targetWidth = this.minWidth - 2 * x;
27849 var targetHeight = this.minHeight - 2 * y;
27853 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27854 scale = targetWidth / width;
27857 if(x > 0 && y == 0){
27858 scale = targetHeight / height;
27861 if(x > 0 && y > 0){
27862 scale = targetWidth / width;
27864 if(width < height){
27865 scale = targetHeight / height;
27869 context.scale(scale, scale);
27871 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27872 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27874 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27875 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27877 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27878 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27880 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27885 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27886 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27888 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27889 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27891 var targetWidth = this.minWidth - 2 * x;
27892 var targetHeight = this.minHeight - 2 * y;
27896 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27897 scale = targetWidth / width;
27900 if(x > 0 && y == 0){
27901 scale = targetHeight / height;
27904 if(x > 0 && y > 0){
27905 scale = targetWidth / width;
27907 if(width < height){
27908 scale = targetHeight / height;
27912 context.scale(scale, scale);
27914 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27915 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27917 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27918 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27920 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27922 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27929 this.cropData = canvas.toDataURL(this.cropType);
27931 if(this.fireEvent('crop', this, this.cropData) !== false){
27932 this.process(this.file, this.cropData);
27939 setThumbBoxSize : function()
27943 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27944 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27945 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27947 this.minWidth = width;
27948 this.minHeight = height;
27950 if(this.rotate == 90 || this.rotate == 270){
27951 this.minWidth = height;
27952 this.minHeight = width;
27957 width = Math.ceil(this.minWidth * height / this.minHeight);
27959 if(this.minWidth > this.minHeight){
27961 height = Math.ceil(this.minHeight * width / this.minWidth);
27964 this.thumbEl.setStyle({
27965 width : width + 'px',
27966 height : height + 'px'
27973 setThumbBoxPosition : function()
27975 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27976 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27978 this.thumbEl.setLeft(x);
27979 this.thumbEl.setTop(y);
27983 baseRotateLevel : function()
27985 this.baseRotate = 1;
27988 typeof(this.exif) != 'undefined' &&
27989 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27990 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27992 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27995 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27999 baseScaleLevel : function()
28003 if(this.isDocument){
28005 if(this.baseRotate == 6 || this.baseRotate == 8){
28007 height = this.thumbEl.getHeight();
28008 this.baseScale = height / this.imageEl.OriginWidth;
28010 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28011 width = this.thumbEl.getWidth();
28012 this.baseScale = width / this.imageEl.OriginHeight;
28018 height = this.thumbEl.getHeight();
28019 this.baseScale = height / this.imageEl.OriginHeight;
28021 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28022 width = this.thumbEl.getWidth();
28023 this.baseScale = width / this.imageEl.OriginWidth;
28029 if(this.baseRotate == 6 || this.baseRotate == 8){
28031 width = this.thumbEl.getHeight();
28032 this.baseScale = width / this.imageEl.OriginHeight;
28034 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28035 height = this.thumbEl.getWidth();
28036 this.baseScale = height / this.imageEl.OriginHeight;
28039 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28040 height = this.thumbEl.getWidth();
28041 this.baseScale = height / this.imageEl.OriginHeight;
28043 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28044 width = this.thumbEl.getHeight();
28045 this.baseScale = width / this.imageEl.OriginWidth;
28052 width = this.thumbEl.getWidth();
28053 this.baseScale = width / this.imageEl.OriginWidth;
28055 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28056 height = this.thumbEl.getHeight();
28057 this.baseScale = height / this.imageEl.OriginHeight;
28060 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28062 height = this.thumbEl.getHeight();
28063 this.baseScale = height / this.imageEl.OriginHeight;
28065 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28066 width = this.thumbEl.getWidth();
28067 this.baseScale = width / this.imageEl.OriginWidth;
28075 getScaleLevel : function()
28077 return this.baseScale * Math.pow(1.1, this.scale);
28080 onTouchStart : function(e)
28082 if(!this.canvasLoaded){
28083 this.beforeSelectFile(e);
28087 var touches = e.browserEvent.touches;
28093 if(touches.length == 1){
28094 this.onMouseDown(e);
28098 if(touches.length != 2){
28104 for(var i = 0, finger; finger = touches[i]; i++){
28105 coords.push(finger.pageX, finger.pageY);
28108 var x = Math.pow(coords[0] - coords[2], 2);
28109 var y = Math.pow(coords[1] - coords[3], 2);
28111 this.startDistance = Math.sqrt(x + y);
28113 this.startScale = this.scale;
28115 this.pinching = true;
28116 this.dragable = false;
28120 onTouchMove : function(e)
28122 if(!this.pinching && !this.dragable){
28126 var touches = e.browserEvent.touches;
28133 this.onMouseMove(e);
28139 for(var i = 0, finger; finger = touches[i]; i++){
28140 coords.push(finger.pageX, finger.pageY);
28143 var x = Math.pow(coords[0] - coords[2], 2);
28144 var y = Math.pow(coords[1] - coords[3], 2);
28146 this.endDistance = Math.sqrt(x + y);
28148 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28150 if(!this.zoomable()){
28151 this.scale = this.startScale;
28159 onTouchEnd : function(e)
28161 this.pinching = false;
28162 this.dragable = false;
28166 process : function(file, crop)
28169 this.maskEl.mask(this.loadingText);
28172 this.xhr = new XMLHttpRequest();
28174 file.xhr = this.xhr;
28176 this.xhr.open(this.method, this.url, true);
28179 "Accept": "application/json",
28180 "Cache-Control": "no-cache",
28181 "X-Requested-With": "XMLHttpRequest"
28184 for (var headerName in headers) {
28185 var headerValue = headers[headerName];
28187 this.xhr.setRequestHeader(headerName, headerValue);
28193 this.xhr.onload = function()
28195 _this.xhrOnLoad(_this.xhr);
28198 this.xhr.onerror = function()
28200 _this.xhrOnError(_this.xhr);
28203 var formData = new FormData();
28205 formData.append('returnHTML', 'NO');
28208 formData.append('crop', crop);
28211 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28212 formData.append(this.paramName, file, file.name);
28215 if(typeof(file.filename) != 'undefined'){
28216 formData.append('filename', file.filename);
28219 if(typeof(file.mimetype) != 'undefined'){
28220 formData.append('mimetype', file.mimetype);
28223 if(this.fireEvent('arrange', this, formData) != false){
28224 this.xhr.send(formData);
28228 xhrOnLoad : function(xhr)
28231 this.maskEl.unmask();
28234 if (xhr.readyState !== 4) {
28235 this.fireEvent('exception', this, xhr);
28239 var response = Roo.decode(xhr.responseText);
28241 if(!response.success){
28242 this.fireEvent('exception', this, xhr);
28246 var response = Roo.decode(xhr.responseText);
28248 this.fireEvent('upload', this, response);
28252 xhrOnError : function()
28255 this.maskEl.unmask();
28258 Roo.log('xhr on error');
28260 var response = Roo.decode(xhr.responseText);
28266 prepare : function(file)
28269 this.maskEl.mask(this.loadingText);
28275 if(typeof(file) === 'string'){
28276 this.loadCanvas(file);
28280 if(!file || !this.urlAPI){
28285 this.cropType = file.type;
28289 if(this.fireEvent('prepare', this, this.file) != false){
28291 var reader = new FileReader();
28293 reader.onload = function (e) {
28294 if (e.target.error) {
28295 Roo.log(e.target.error);
28299 var buffer = e.target.result,
28300 dataView = new DataView(buffer),
28302 maxOffset = dataView.byteLength - 4,
28306 if (dataView.getUint16(0) === 0xffd8) {
28307 while (offset < maxOffset) {
28308 markerBytes = dataView.getUint16(offset);
28310 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28311 markerLength = dataView.getUint16(offset + 2) + 2;
28312 if (offset + markerLength > dataView.byteLength) {
28313 Roo.log('Invalid meta data: Invalid segment size.');
28317 if(markerBytes == 0xffe1){
28318 _this.parseExifData(
28325 offset += markerLength;
28335 var url = _this.urlAPI.createObjectURL(_this.file);
28337 _this.loadCanvas(url);
28342 reader.readAsArrayBuffer(this.file);
28348 parseExifData : function(dataView, offset, length)
28350 var tiffOffset = offset + 10,
28354 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28355 // No Exif data, might be XMP data instead
28359 // Check for the ASCII code for "Exif" (0x45786966):
28360 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28361 // No Exif data, might be XMP data instead
28364 if (tiffOffset + 8 > dataView.byteLength) {
28365 Roo.log('Invalid Exif data: Invalid segment size.');
28368 // Check for the two null bytes:
28369 if (dataView.getUint16(offset + 8) !== 0x0000) {
28370 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28373 // Check the byte alignment:
28374 switch (dataView.getUint16(tiffOffset)) {
28376 littleEndian = true;
28379 littleEndian = false;
28382 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28385 // Check for the TIFF tag marker (0x002A):
28386 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28387 Roo.log('Invalid Exif data: Missing TIFF marker.');
28390 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28391 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28393 this.parseExifTags(
28396 tiffOffset + dirOffset,
28401 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28406 if (dirOffset + 6 > dataView.byteLength) {
28407 Roo.log('Invalid Exif data: Invalid directory offset.');
28410 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28411 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28412 if (dirEndOffset + 4 > dataView.byteLength) {
28413 Roo.log('Invalid Exif data: Invalid directory size.');
28416 for (i = 0; i < tagsNumber; i += 1) {
28420 dirOffset + 2 + 12 * i, // tag offset
28424 // Return the offset to the next directory:
28425 return dataView.getUint32(dirEndOffset, littleEndian);
28428 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28430 var tag = dataView.getUint16(offset, littleEndian);
28432 this.exif[tag] = this.getExifValue(
28436 dataView.getUint16(offset + 2, littleEndian), // tag type
28437 dataView.getUint32(offset + 4, littleEndian), // tag length
28442 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28444 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28453 Roo.log('Invalid Exif data: Invalid tag type.');
28457 tagSize = tagType.size * length;
28458 // Determine if the value is contained in the dataOffset bytes,
28459 // or if the value at the dataOffset is a pointer to the actual data:
28460 dataOffset = tagSize > 4 ?
28461 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28462 if (dataOffset + tagSize > dataView.byteLength) {
28463 Roo.log('Invalid Exif data: Invalid data offset.');
28466 if (length === 1) {
28467 return tagType.getValue(dataView, dataOffset, littleEndian);
28470 for (i = 0; i < length; i += 1) {
28471 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28474 if (tagType.ascii) {
28476 // Concatenate the chars:
28477 for (i = 0; i < values.length; i += 1) {
28479 // Ignore the terminating NULL byte(s):
28480 if (c === '\u0000') {
28492 Roo.apply(Roo.bootstrap.UploadCropbox, {
28494 'Orientation': 0x0112
28498 1: 0, //'top-left',
28500 3: 180, //'bottom-right',
28501 // 4: 'bottom-left',
28503 6: 90, //'right-top',
28504 // 7: 'right-bottom',
28505 8: 270 //'left-bottom'
28509 // byte, 8-bit unsigned int:
28511 getValue: function (dataView, dataOffset) {
28512 return dataView.getUint8(dataOffset);
28516 // ascii, 8-bit byte:
28518 getValue: function (dataView, dataOffset) {
28519 return String.fromCharCode(dataView.getUint8(dataOffset));
28524 // short, 16 bit int:
28526 getValue: function (dataView, dataOffset, littleEndian) {
28527 return dataView.getUint16(dataOffset, littleEndian);
28531 // long, 32 bit int:
28533 getValue: function (dataView, dataOffset, littleEndian) {
28534 return dataView.getUint32(dataOffset, littleEndian);
28538 // rational = two long values, first is numerator, second is denominator:
28540 getValue: function (dataView, dataOffset, littleEndian) {
28541 return dataView.getUint32(dataOffset, littleEndian) /
28542 dataView.getUint32(dataOffset + 4, littleEndian);
28546 // slong, 32 bit signed int:
28548 getValue: function (dataView, dataOffset, littleEndian) {
28549 return dataView.getInt32(dataOffset, littleEndian);
28553 // srational, two slongs, first is numerator, second is denominator:
28555 getValue: function (dataView, dataOffset, littleEndian) {
28556 return dataView.getInt32(dataOffset, littleEndian) /
28557 dataView.getInt32(dataOffset + 4, littleEndian);
28567 cls : 'btn-group roo-upload-cropbox-rotate-left',
28568 action : 'rotate-left',
28572 cls : 'btn btn-default',
28573 html : '<i class="fa fa-undo"></i>'
28579 cls : 'btn-group roo-upload-cropbox-picture',
28580 action : 'picture',
28584 cls : 'btn btn-default',
28585 html : '<i class="fa fa-picture-o"></i>'
28591 cls : 'btn-group roo-upload-cropbox-rotate-right',
28592 action : 'rotate-right',
28596 cls : 'btn btn-default',
28597 html : '<i class="fa fa-repeat"></i>'
28605 cls : 'btn-group roo-upload-cropbox-rotate-left',
28606 action : 'rotate-left',
28610 cls : 'btn btn-default',
28611 html : '<i class="fa fa-undo"></i>'
28617 cls : 'btn-group roo-upload-cropbox-download',
28618 action : 'download',
28622 cls : 'btn btn-default',
28623 html : '<i class="fa fa-download"></i>'
28629 cls : 'btn-group roo-upload-cropbox-crop',
28634 cls : 'btn btn-default',
28635 html : '<i class="fa fa-crop"></i>'
28641 cls : 'btn-group roo-upload-cropbox-trash',
28646 cls : 'btn btn-default',
28647 html : '<i class="fa fa-trash"></i>'
28653 cls : 'btn-group roo-upload-cropbox-rotate-right',
28654 action : 'rotate-right',
28658 cls : 'btn btn-default',
28659 html : '<i class="fa fa-repeat"></i>'
28667 cls : 'btn-group roo-upload-cropbox-rotate-left',
28668 action : 'rotate-left',
28672 cls : 'btn btn-default',
28673 html : '<i class="fa fa-undo"></i>'
28679 cls : 'btn-group roo-upload-cropbox-rotate-right',
28680 action : 'rotate-right',
28684 cls : 'btn btn-default',
28685 html : '<i class="fa fa-repeat"></i>'
28698 * @class Roo.bootstrap.DocumentManager
28699 * @extends Roo.bootstrap.Component
28700 * Bootstrap DocumentManager class
28701 * @cfg {String} paramName default 'imageUpload'
28702 * @cfg {String} toolTipName default 'filename'
28703 * @cfg {String} method default POST
28704 * @cfg {String} url action url
28705 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28706 * @cfg {Boolean} multiple multiple upload default true
28707 * @cfg {Number} thumbSize default 300
28708 * @cfg {String} fieldLabel
28709 * @cfg {Number} labelWidth default 4
28710 * @cfg {String} labelAlign (left|top) default left
28711 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28712 * @cfg {Number} labellg set the width of label (1-12)
28713 * @cfg {Number} labelmd set the width of label (1-12)
28714 * @cfg {Number} labelsm set the width of label (1-12)
28715 * @cfg {Number} labelxs set the width of label (1-12)
28718 * Create a new DocumentManager
28719 * @param {Object} config The config object
28722 Roo.bootstrap.DocumentManager = function(config){
28723 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28726 this.delegates = [];
28731 * Fire when initial the DocumentManager
28732 * @param {Roo.bootstrap.DocumentManager} this
28737 * inspect selected file
28738 * @param {Roo.bootstrap.DocumentManager} this
28739 * @param {File} file
28744 * Fire when xhr load exception
28745 * @param {Roo.bootstrap.DocumentManager} this
28746 * @param {XMLHttpRequest} xhr
28748 "exception" : true,
28750 * @event afterupload
28751 * Fire when xhr load exception
28752 * @param {Roo.bootstrap.DocumentManager} this
28753 * @param {XMLHttpRequest} xhr
28755 "afterupload" : true,
28758 * prepare the form data
28759 * @param {Roo.bootstrap.DocumentManager} this
28760 * @param {Object} formData
28765 * Fire when remove the file
28766 * @param {Roo.bootstrap.DocumentManager} this
28767 * @param {Object} file
28772 * Fire after refresh the file
28773 * @param {Roo.bootstrap.DocumentManager} this
28778 * Fire after click the image
28779 * @param {Roo.bootstrap.DocumentManager} this
28780 * @param {Object} file
28785 * Fire when upload a image and editable set to true
28786 * @param {Roo.bootstrap.DocumentManager} this
28787 * @param {Object} file
28791 * @event beforeselectfile
28792 * Fire before select file
28793 * @param {Roo.bootstrap.DocumentManager} this
28795 "beforeselectfile" : true,
28798 * Fire before process file
28799 * @param {Roo.bootstrap.DocumentManager} this
28800 * @param {Object} file
28804 * @event previewrendered
28805 * Fire when preview rendered
28806 * @param {Roo.bootstrap.DocumentManager} this
28807 * @param {Object} file
28809 "previewrendered" : true,
28812 "previewResize" : true
28817 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28826 paramName : 'imageUpload',
28827 toolTipName : 'filename',
28830 labelAlign : 'left',
28840 getAutoCreate : function()
28842 var managerWidget = {
28844 cls : 'roo-document-manager',
28848 cls : 'roo-document-manager-selector',
28853 cls : 'roo-document-manager-uploader',
28857 cls : 'roo-document-manager-upload-btn',
28858 html : '<i class="fa fa-plus"></i>'
28869 cls : 'column col-md-12',
28874 if(this.fieldLabel.length){
28879 cls : 'column col-md-12',
28880 html : this.fieldLabel
28884 cls : 'column col-md-12',
28889 if(this.labelAlign == 'left'){
28894 html : this.fieldLabel
28903 if(this.labelWidth > 12){
28904 content[0].style = "width: " + this.labelWidth + 'px';
28907 if(this.labelWidth < 13 && this.labelmd == 0){
28908 this.labelmd = this.labelWidth;
28911 if(this.labellg > 0){
28912 content[0].cls += ' col-lg-' + this.labellg;
28913 content[1].cls += ' col-lg-' + (12 - this.labellg);
28916 if(this.labelmd > 0){
28917 content[0].cls += ' col-md-' + this.labelmd;
28918 content[1].cls += ' col-md-' + (12 - this.labelmd);
28921 if(this.labelsm > 0){
28922 content[0].cls += ' col-sm-' + this.labelsm;
28923 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28926 if(this.labelxs > 0){
28927 content[0].cls += ' col-xs-' + this.labelxs;
28928 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28936 cls : 'row clearfix',
28944 initEvents : function()
28946 this.managerEl = this.el.select('.roo-document-manager', true).first();
28947 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28949 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28950 this.selectorEl.hide();
28953 this.selectorEl.attr('multiple', 'multiple');
28956 this.selectorEl.on('change', this.onFileSelected, this);
28958 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28959 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28961 this.uploader.on('click', this.onUploaderClick, this);
28963 this.renderProgressDialog();
28967 window.addEventListener("resize", function() { _this.refresh(); } );
28969 this.fireEvent('initial', this);
28972 renderProgressDialog : function()
28976 this.progressDialog = new Roo.bootstrap.Modal({
28977 cls : 'roo-document-manager-progress-dialog',
28978 allow_close : false,
28988 btnclick : function() {
28989 _this.uploadCancel();
28995 this.progressDialog.render(Roo.get(document.body));
28997 this.progress = new Roo.bootstrap.Progress({
28998 cls : 'roo-document-manager-progress',
29003 this.progress.render(this.progressDialog.getChildContainer());
29005 this.progressBar = new Roo.bootstrap.ProgressBar({
29006 cls : 'roo-document-manager-progress-bar',
29009 aria_valuemax : 12,
29013 this.progressBar.render(this.progress.getChildContainer());
29016 onUploaderClick : function(e)
29018 e.preventDefault();
29020 if(this.fireEvent('beforeselectfile', this) != false){
29021 this.selectorEl.dom.click();
29026 onFileSelected : function(e)
29028 e.preventDefault();
29030 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29034 Roo.each(this.selectorEl.dom.files, function(file){
29035 if(this.fireEvent('inspect', this, file) != false){
29036 this.files.push(file);
29046 this.selectorEl.dom.value = '';
29048 if(!this.files || !this.files.length){
29052 if(this.boxes > 0 && this.files.length > this.boxes){
29053 this.files = this.files.slice(0, this.boxes);
29056 this.uploader.show();
29058 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29059 this.uploader.hide();
29068 Roo.each(this.files, function(file){
29070 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29071 var f = this.renderPreview(file);
29076 if(file.type.indexOf('image') != -1){
29077 this.delegates.push(
29079 _this.process(file);
29080 }).createDelegate(this)
29088 _this.process(file);
29089 }).createDelegate(this)
29094 this.files = files;
29096 this.delegates = this.delegates.concat(docs);
29098 if(!this.delegates.length){
29103 this.progressBar.aria_valuemax = this.delegates.length;
29110 arrange : function()
29112 if(!this.delegates.length){
29113 this.progressDialog.hide();
29118 var delegate = this.delegates.shift();
29120 this.progressDialog.show();
29122 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29124 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29129 refresh : function()
29131 this.uploader.show();
29133 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29134 this.uploader.hide();
29137 Roo.isTouch ? this.closable(false) : this.closable(true);
29139 this.fireEvent('refresh', this);
29142 onRemove : function(e, el, o)
29144 e.preventDefault();
29146 this.fireEvent('remove', this, o);
29150 remove : function(o)
29154 Roo.each(this.files, function(file){
29155 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29164 this.files = files;
29171 Roo.each(this.files, function(file){
29176 file.target.remove();
29185 onClick : function(e, el, o)
29187 e.preventDefault();
29189 this.fireEvent('click', this, o);
29193 closable : function(closable)
29195 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29197 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29209 xhrOnLoad : function(xhr)
29211 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29215 if (xhr.readyState !== 4) {
29217 this.fireEvent('exception', this, xhr);
29221 var response = Roo.decode(xhr.responseText);
29223 if(!response.success){
29225 this.fireEvent('exception', this, xhr);
29229 var file = this.renderPreview(response.data);
29231 this.files.push(file);
29235 this.fireEvent('afterupload', this, xhr);
29239 xhrOnError : function(xhr)
29241 Roo.log('xhr on error');
29243 var response = Roo.decode(xhr.responseText);
29250 process : function(file)
29252 if(this.fireEvent('process', this, file) !== false){
29253 if(this.editable && file.type.indexOf('image') != -1){
29254 this.fireEvent('edit', this, file);
29258 this.uploadStart(file, false);
29265 uploadStart : function(file, crop)
29267 this.xhr = new XMLHttpRequest();
29269 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29274 file.xhr = this.xhr;
29276 this.managerEl.createChild({
29278 cls : 'roo-document-manager-loading',
29282 tooltip : file.name,
29283 cls : 'roo-document-manager-thumb',
29284 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29290 this.xhr.open(this.method, this.url, true);
29293 "Accept": "application/json",
29294 "Cache-Control": "no-cache",
29295 "X-Requested-With": "XMLHttpRequest"
29298 for (var headerName in headers) {
29299 var headerValue = headers[headerName];
29301 this.xhr.setRequestHeader(headerName, headerValue);
29307 this.xhr.onload = function()
29309 _this.xhrOnLoad(_this.xhr);
29312 this.xhr.onerror = function()
29314 _this.xhrOnError(_this.xhr);
29317 var formData = new FormData();
29319 formData.append('returnHTML', 'NO');
29322 formData.append('crop', crop);
29325 formData.append(this.paramName, file, file.name);
29332 if(this.fireEvent('prepare', this, formData, options) != false){
29334 if(options.manually){
29338 this.xhr.send(formData);
29342 this.uploadCancel();
29345 uploadCancel : function()
29351 this.delegates = [];
29353 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29360 renderPreview : function(file)
29362 if(typeof(file.target) != 'undefined' && file.target){
29366 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29368 var previewEl = this.managerEl.createChild({
29370 cls : 'roo-document-manager-preview',
29374 tooltip : file[this.toolTipName],
29375 cls : 'roo-document-manager-thumb',
29376 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29381 html : '<i class="fa fa-times-circle"></i>'
29386 var close = previewEl.select('button.close', true).first();
29388 close.on('click', this.onRemove, this, file);
29390 file.target = previewEl;
29392 var image = previewEl.select('img', true).first();
29396 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29398 image.on('click', this.onClick, this, file);
29400 this.fireEvent('previewrendered', this, file);
29406 onPreviewLoad : function(file, image)
29408 if(typeof(file.target) == 'undefined' || !file.target){
29412 var width = image.dom.naturalWidth || image.dom.width;
29413 var height = image.dom.naturalHeight || image.dom.height;
29415 if(!this.previewResize) {
29419 if(width > height){
29420 file.target.addClass('wide');
29424 file.target.addClass('tall');
29429 uploadFromSource : function(file, crop)
29431 this.xhr = new XMLHttpRequest();
29433 this.managerEl.createChild({
29435 cls : 'roo-document-manager-loading',
29439 tooltip : file.name,
29440 cls : 'roo-document-manager-thumb',
29441 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29447 this.xhr.open(this.method, this.url, true);
29450 "Accept": "application/json",
29451 "Cache-Control": "no-cache",
29452 "X-Requested-With": "XMLHttpRequest"
29455 for (var headerName in headers) {
29456 var headerValue = headers[headerName];
29458 this.xhr.setRequestHeader(headerName, headerValue);
29464 this.xhr.onload = function()
29466 _this.xhrOnLoad(_this.xhr);
29469 this.xhr.onerror = function()
29471 _this.xhrOnError(_this.xhr);
29474 var formData = new FormData();
29476 formData.append('returnHTML', 'NO');
29478 formData.append('crop', crop);
29480 if(typeof(file.filename) != 'undefined'){
29481 formData.append('filename', file.filename);
29484 if(typeof(file.mimetype) != 'undefined'){
29485 formData.append('mimetype', file.mimetype);
29490 if(this.fireEvent('prepare', this, formData) != false){
29491 this.xhr.send(formData);
29501 * @class Roo.bootstrap.DocumentViewer
29502 * @extends Roo.bootstrap.Component
29503 * Bootstrap DocumentViewer class
29504 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29505 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29508 * Create a new DocumentViewer
29509 * @param {Object} config The config object
29512 Roo.bootstrap.DocumentViewer = function(config){
29513 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29518 * Fire after initEvent
29519 * @param {Roo.bootstrap.DocumentViewer} this
29525 * @param {Roo.bootstrap.DocumentViewer} this
29530 * Fire after download button
29531 * @param {Roo.bootstrap.DocumentViewer} this
29536 * Fire after trash button
29537 * @param {Roo.bootstrap.DocumentViewer} this
29544 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29546 showDownload : true,
29550 getAutoCreate : function()
29554 cls : 'roo-document-viewer',
29558 cls : 'roo-document-viewer-body',
29562 cls : 'roo-document-viewer-thumb',
29566 cls : 'roo-document-viewer-image'
29574 cls : 'roo-document-viewer-footer',
29577 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29581 cls : 'btn-group roo-document-viewer-download',
29585 cls : 'btn btn-default',
29586 html : '<i class="fa fa-download"></i>'
29592 cls : 'btn-group roo-document-viewer-trash',
29596 cls : 'btn btn-default',
29597 html : '<i class="fa fa-trash"></i>'
29610 initEvents : function()
29612 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29613 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29615 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29616 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29618 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29619 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29621 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29622 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29624 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29625 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29627 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29628 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29630 this.bodyEl.on('click', this.onClick, this);
29631 this.downloadBtn.on('click', this.onDownload, this);
29632 this.trashBtn.on('click', this.onTrash, this);
29634 this.downloadBtn.hide();
29635 this.trashBtn.hide();
29637 if(this.showDownload){
29638 this.downloadBtn.show();
29641 if(this.showTrash){
29642 this.trashBtn.show();
29645 if(!this.showDownload && !this.showTrash) {
29646 this.footerEl.hide();
29651 initial : function()
29653 this.fireEvent('initial', this);
29657 onClick : function(e)
29659 e.preventDefault();
29661 this.fireEvent('click', this);
29664 onDownload : function(e)
29666 e.preventDefault();
29668 this.fireEvent('download', this);
29671 onTrash : function(e)
29673 e.preventDefault();
29675 this.fireEvent('trash', this);
29687 * @class Roo.bootstrap.NavProgressBar
29688 * @extends Roo.bootstrap.Component
29689 * Bootstrap NavProgressBar class
29692 * Create a new nav progress bar
29693 * @param {Object} config The config object
29696 Roo.bootstrap.NavProgressBar = function(config){
29697 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29699 this.bullets = this.bullets || [];
29701 // Roo.bootstrap.NavProgressBar.register(this);
29705 * Fires when the active item changes
29706 * @param {Roo.bootstrap.NavProgressBar} this
29707 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29708 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29715 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29720 getAutoCreate : function()
29722 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29726 cls : 'roo-navigation-bar-group',
29730 cls : 'roo-navigation-top-bar'
29734 cls : 'roo-navigation-bullets-bar',
29738 cls : 'roo-navigation-bar'
29745 cls : 'roo-navigation-bottom-bar'
29755 initEvents: function()
29760 onRender : function(ct, position)
29762 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29764 if(this.bullets.length){
29765 Roo.each(this.bullets, function(b){
29774 addItem : function(cfg)
29776 var item = new Roo.bootstrap.NavProgressItem(cfg);
29778 item.parentId = this.id;
29779 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29782 var top = new Roo.bootstrap.Element({
29784 cls : 'roo-navigation-bar-text'
29787 var bottom = new Roo.bootstrap.Element({
29789 cls : 'roo-navigation-bar-text'
29792 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29793 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29795 var topText = new Roo.bootstrap.Element({
29797 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29800 var bottomText = new Roo.bootstrap.Element({
29802 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29805 topText.onRender(top.el, null);
29806 bottomText.onRender(bottom.el, null);
29809 item.bottomEl = bottom;
29812 this.barItems.push(item);
29817 getActive : function()
29819 var active = false;
29821 Roo.each(this.barItems, function(v){
29823 if (!v.isActive()) {
29835 setActiveItem : function(item)
29839 Roo.each(this.barItems, function(v){
29840 if (v.rid == item.rid) {
29844 if (v.isActive()) {
29845 v.setActive(false);
29850 item.setActive(true);
29852 this.fireEvent('changed', this, item, prev);
29855 getBarItem: function(rid)
29859 Roo.each(this.barItems, function(e) {
29860 if (e.rid != rid) {
29871 indexOfItem : function(item)
29875 Roo.each(this.barItems, function(v, i){
29877 if (v.rid != item.rid) {
29888 setActiveNext : function()
29890 var i = this.indexOfItem(this.getActive());
29892 if (i > this.barItems.length) {
29896 this.setActiveItem(this.barItems[i+1]);
29899 setActivePrev : function()
29901 var i = this.indexOfItem(this.getActive());
29907 this.setActiveItem(this.barItems[i-1]);
29910 format : function()
29912 if(!this.barItems.length){
29916 var width = 100 / this.barItems.length;
29918 Roo.each(this.barItems, function(i){
29919 i.el.setStyle('width', width + '%');
29920 i.topEl.el.setStyle('width', width + '%');
29921 i.bottomEl.el.setStyle('width', width + '%');
29930 * Nav Progress Item
29935 * @class Roo.bootstrap.NavProgressItem
29936 * @extends Roo.bootstrap.Component
29937 * Bootstrap NavProgressItem class
29938 * @cfg {String} rid the reference id
29939 * @cfg {Boolean} active (true|false) Is item active default false
29940 * @cfg {Boolean} disabled (true|false) Is item active default false
29941 * @cfg {String} html
29942 * @cfg {String} position (top|bottom) text position default bottom
29943 * @cfg {String} icon show icon instead of number
29946 * Create a new NavProgressItem
29947 * @param {Object} config The config object
29949 Roo.bootstrap.NavProgressItem = function(config){
29950 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29955 * The raw click event for the entire grid.
29956 * @param {Roo.bootstrap.NavProgressItem} this
29957 * @param {Roo.EventObject} e
29964 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29970 position : 'bottom',
29973 getAutoCreate : function()
29975 var iconCls = 'roo-navigation-bar-item-icon';
29977 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29981 cls: 'roo-navigation-bar-item',
29991 cfg.cls += ' active';
29994 cfg.cls += ' disabled';
30000 disable : function()
30002 this.setDisabled(true);
30005 enable : function()
30007 this.setDisabled(false);
30010 initEvents: function()
30012 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30014 this.iconEl.on('click', this.onClick, this);
30017 onClick : function(e)
30019 e.preventDefault();
30025 if(this.fireEvent('click', this, e) === false){
30029 this.parent().setActiveItem(this);
30032 isActive: function ()
30034 return this.active;
30037 setActive : function(state)
30039 if(this.active == state){
30043 this.active = state;
30046 this.el.addClass('active');
30050 this.el.removeClass('active');
30055 setDisabled : function(state)
30057 if(this.disabled == state){
30061 this.disabled = state;
30064 this.el.addClass('disabled');
30068 this.el.removeClass('disabled');
30071 tooltipEl : function()
30073 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30086 * @class Roo.bootstrap.FieldLabel
30087 * @extends Roo.bootstrap.Component
30088 * Bootstrap FieldLabel class
30089 * @cfg {String} html contents of the element
30090 * @cfg {String} tag tag of the element default label
30091 * @cfg {String} cls class of the element
30092 * @cfg {String} target label target
30093 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30094 * @cfg {String} invalidClass default "text-warning"
30095 * @cfg {String} validClass default "text-success"
30096 * @cfg {String} iconTooltip default "This field is required"
30097 * @cfg {String} indicatorpos (left|right) default left
30100 * Create a new FieldLabel
30101 * @param {Object} config The config object
30104 Roo.bootstrap.FieldLabel = function(config){
30105 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30110 * Fires after the field has been marked as invalid.
30111 * @param {Roo.form.FieldLabel} this
30112 * @param {String} msg The validation message
30117 * Fires after the field has been validated with no errors.
30118 * @param {Roo.form.FieldLabel} this
30124 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30131 invalidClass : 'has-warning',
30132 validClass : 'has-success',
30133 iconTooltip : 'This field is required',
30134 indicatorpos : 'left',
30136 getAutoCreate : function(){
30139 if (!this.allowBlank) {
30145 cls : 'roo-bootstrap-field-label ' + this.cls,
30150 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30151 tooltip : this.iconTooltip
30160 if(this.indicatorpos == 'right'){
30163 cls : 'roo-bootstrap-field-label ' + this.cls,
30172 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30173 tooltip : this.iconTooltip
30182 initEvents: function()
30184 Roo.bootstrap.Element.superclass.initEvents.call(this);
30186 this.indicator = this.indicatorEl();
30188 if(this.indicator){
30189 this.indicator.removeClass('visible');
30190 this.indicator.addClass('invisible');
30193 Roo.bootstrap.FieldLabel.register(this);
30196 indicatorEl : function()
30198 var indicator = this.el.select('i.roo-required-indicator',true).first();
30209 * Mark this field as valid
30211 markValid : function()
30213 if(this.indicator){
30214 this.indicator.removeClass('visible');
30215 this.indicator.addClass('invisible');
30218 this.el.removeClass(this.invalidClass);
30220 this.el.addClass(this.validClass);
30222 this.fireEvent('valid', this);
30226 * Mark this field as invalid
30227 * @param {String} msg The validation message
30229 markInvalid : function(msg)
30231 if(this.indicator){
30232 this.indicator.removeClass('invisible');
30233 this.indicator.addClass('visible');
30236 this.el.removeClass(this.validClass);
30238 this.el.addClass(this.invalidClass);
30240 this.fireEvent('invalid', this, msg);
30246 Roo.apply(Roo.bootstrap.FieldLabel, {
30251 * register a FieldLabel Group
30252 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30254 register : function(label)
30256 if(this.groups.hasOwnProperty(label.target)){
30260 this.groups[label.target] = label;
30264 * fetch a FieldLabel Group based on the target
30265 * @param {string} target
30266 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30268 get: function(target) {
30269 if (typeof(this.groups[target]) == 'undefined') {
30273 return this.groups[target] ;
30282 * page DateSplitField.
30288 * @class Roo.bootstrap.DateSplitField
30289 * @extends Roo.bootstrap.Component
30290 * Bootstrap DateSplitField class
30291 * @cfg {string} fieldLabel - the label associated
30292 * @cfg {Number} labelWidth set the width of label (0-12)
30293 * @cfg {String} labelAlign (top|left)
30294 * @cfg {Boolean} dayAllowBlank (true|false) default false
30295 * @cfg {Boolean} monthAllowBlank (true|false) default false
30296 * @cfg {Boolean} yearAllowBlank (true|false) default false
30297 * @cfg {string} dayPlaceholder
30298 * @cfg {string} monthPlaceholder
30299 * @cfg {string} yearPlaceholder
30300 * @cfg {string} dayFormat default 'd'
30301 * @cfg {string} monthFormat default 'm'
30302 * @cfg {string} yearFormat default 'Y'
30303 * @cfg {Number} labellg set the width of label (1-12)
30304 * @cfg {Number} labelmd set the width of label (1-12)
30305 * @cfg {Number} labelsm set the width of label (1-12)
30306 * @cfg {Number} labelxs set the width of label (1-12)
30310 * Create a new DateSplitField
30311 * @param {Object} config The config object
30314 Roo.bootstrap.DateSplitField = function(config){
30315 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30321 * getting the data of years
30322 * @param {Roo.bootstrap.DateSplitField} this
30323 * @param {Object} years
30328 * getting the data of days
30329 * @param {Roo.bootstrap.DateSplitField} this
30330 * @param {Object} days
30335 * Fires after the field has been marked as invalid.
30336 * @param {Roo.form.Field} this
30337 * @param {String} msg The validation message
30342 * Fires after the field has been validated with no errors.
30343 * @param {Roo.form.Field} this
30349 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30352 labelAlign : 'top',
30354 dayAllowBlank : false,
30355 monthAllowBlank : false,
30356 yearAllowBlank : false,
30357 dayPlaceholder : '',
30358 monthPlaceholder : '',
30359 yearPlaceholder : '',
30363 isFormField : true,
30369 getAutoCreate : function()
30373 cls : 'row roo-date-split-field-group',
30378 cls : 'form-hidden-field roo-date-split-field-group-value',
30384 var labelCls = 'col-md-12';
30385 var contentCls = 'col-md-4';
30387 if(this.fieldLabel){
30391 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30395 html : this.fieldLabel
30400 if(this.labelAlign == 'left'){
30402 if(this.labelWidth > 12){
30403 label.style = "width: " + this.labelWidth + 'px';
30406 if(this.labelWidth < 13 && this.labelmd == 0){
30407 this.labelmd = this.labelWidth;
30410 if(this.labellg > 0){
30411 labelCls = ' col-lg-' + this.labellg;
30412 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30415 if(this.labelmd > 0){
30416 labelCls = ' col-md-' + this.labelmd;
30417 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30420 if(this.labelsm > 0){
30421 labelCls = ' col-sm-' + this.labelsm;
30422 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30425 if(this.labelxs > 0){
30426 labelCls = ' col-xs-' + this.labelxs;
30427 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30431 label.cls += ' ' + labelCls;
30433 cfg.cn.push(label);
30436 Roo.each(['day', 'month', 'year'], function(t){
30439 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30446 inputEl: function ()
30448 return this.el.select('.roo-date-split-field-group-value', true).first();
30451 onRender : function(ct, position)
30455 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30457 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30459 this.dayField = new Roo.bootstrap.ComboBox({
30460 allowBlank : this.dayAllowBlank,
30461 alwaysQuery : true,
30462 displayField : 'value',
30465 forceSelection : true,
30467 placeholder : this.dayPlaceholder,
30468 selectOnFocus : true,
30469 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30470 triggerAction : 'all',
30472 valueField : 'value',
30473 store : new Roo.data.SimpleStore({
30474 data : (function() {
30476 _this.fireEvent('days', _this, days);
30479 fields : [ 'value' ]
30482 select : function (_self, record, index)
30484 _this.setValue(_this.getValue());
30489 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30491 this.monthField = new Roo.bootstrap.MonthField({
30492 after : '<i class=\"fa fa-calendar\"></i>',
30493 allowBlank : this.monthAllowBlank,
30494 placeholder : this.monthPlaceholder,
30497 render : function (_self)
30499 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30500 e.preventDefault();
30504 select : function (_self, oldvalue, newvalue)
30506 _this.setValue(_this.getValue());
30511 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30513 this.yearField = new Roo.bootstrap.ComboBox({
30514 allowBlank : this.yearAllowBlank,
30515 alwaysQuery : true,
30516 displayField : 'value',
30519 forceSelection : true,
30521 placeholder : this.yearPlaceholder,
30522 selectOnFocus : true,
30523 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30524 triggerAction : 'all',
30526 valueField : 'value',
30527 store : new Roo.data.SimpleStore({
30528 data : (function() {
30530 _this.fireEvent('years', _this, years);
30533 fields : [ 'value' ]
30536 select : function (_self, record, index)
30538 _this.setValue(_this.getValue());
30543 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30546 setValue : function(v, format)
30548 this.inputEl.dom.value = v;
30550 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30552 var d = Date.parseDate(v, f);
30559 this.setDay(d.format(this.dayFormat));
30560 this.setMonth(d.format(this.monthFormat));
30561 this.setYear(d.format(this.yearFormat));
30568 setDay : function(v)
30570 this.dayField.setValue(v);
30571 this.inputEl.dom.value = this.getValue();
30576 setMonth : function(v)
30578 this.monthField.setValue(v, true);
30579 this.inputEl.dom.value = this.getValue();
30584 setYear : function(v)
30586 this.yearField.setValue(v);
30587 this.inputEl.dom.value = this.getValue();
30592 getDay : function()
30594 return this.dayField.getValue();
30597 getMonth : function()
30599 return this.monthField.getValue();
30602 getYear : function()
30604 return this.yearField.getValue();
30607 getValue : function()
30609 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30611 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30621 this.inputEl.dom.value = '';
30626 validate : function()
30628 var d = this.dayField.validate();
30629 var m = this.monthField.validate();
30630 var y = this.yearField.validate();
30635 (!this.dayAllowBlank && !d) ||
30636 (!this.monthAllowBlank && !m) ||
30637 (!this.yearAllowBlank && !y)
30642 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30651 this.markInvalid();
30656 markValid : function()
30659 var label = this.el.select('label', true).first();
30660 var icon = this.el.select('i.fa-star', true).first();
30666 this.fireEvent('valid', this);
30670 * Mark this field as invalid
30671 * @param {String} msg The validation message
30673 markInvalid : function(msg)
30676 var label = this.el.select('label', true).first();
30677 var icon = this.el.select('i.fa-star', true).first();
30679 if(label && !icon){
30680 this.el.select('.roo-date-split-field-label', true).createChild({
30682 cls : 'text-danger fa fa-lg fa-star',
30683 tooltip : 'This field is required',
30684 style : 'margin-right:5px;'
30688 this.fireEvent('invalid', this, msg);
30691 clearInvalid : function()
30693 var label = this.el.select('label', true).first();
30694 var icon = this.el.select('i.fa-star', true).first();
30700 this.fireEvent('valid', this);
30703 getName: function()
30713 * http://masonry.desandro.com
30715 * The idea is to render all the bricks based on vertical width...
30717 * The original code extends 'outlayer' - we might need to use that....
30723 * @class Roo.bootstrap.LayoutMasonry
30724 * @extends Roo.bootstrap.Component
30725 * Bootstrap Layout Masonry class
30728 * Create a new Element
30729 * @param {Object} config The config object
30732 Roo.bootstrap.LayoutMasonry = function(config){
30734 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30738 Roo.bootstrap.LayoutMasonry.register(this);
30744 * Fire after layout the items
30745 * @param {Roo.bootstrap.LayoutMasonry} this
30746 * @param {Roo.EventObject} e
30753 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30756 * @cfg {Boolean} isLayoutInstant = no animation?
30758 isLayoutInstant : false, // needed?
30761 * @cfg {Number} boxWidth width of the columns
30766 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30771 * @cfg {Number} padWidth padding below box..
30776 * @cfg {Number} gutter gutter width..
30781 * @cfg {Number} maxCols maximum number of columns
30787 * @cfg {Boolean} isAutoInitial defalut true
30789 isAutoInitial : true,
30794 * @cfg {Boolean} isHorizontal defalut false
30796 isHorizontal : false,
30798 currentSize : null,
30804 bricks: null, //CompositeElement
30808 _isLayoutInited : false,
30810 // isAlternative : false, // only use for vertical layout...
30813 * @cfg {Number} alternativePadWidth padding below box..
30815 alternativePadWidth : 50,
30817 selectedBrick : [],
30819 getAutoCreate : function(){
30821 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30825 cls: 'blog-masonary-wrapper ' + this.cls,
30827 cls : 'mas-boxes masonary'
30834 getChildContainer: function( )
30836 if (this.boxesEl) {
30837 return this.boxesEl;
30840 this.boxesEl = this.el.select('.mas-boxes').first();
30842 return this.boxesEl;
30846 initEvents : function()
30850 if(this.isAutoInitial){
30851 Roo.log('hook children rendered');
30852 this.on('childrenrendered', function() {
30853 Roo.log('children rendered');
30859 initial : function()
30861 this.selectedBrick = [];
30863 this.currentSize = this.el.getBox(true);
30865 Roo.EventManager.onWindowResize(this.resize, this);
30867 if(!this.isAutoInitial){
30875 //this.layout.defer(500,this);
30879 resize : function()
30881 var cs = this.el.getBox(true);
30884 this.currentSize.width == cs.width &&
30885 this.currentSize.x == cs.x &&
30886 this.currentSize.height == cs.height &&
30887 this.currentSize.y == cs.y
30889 Roo.log("no change in with or X or Y");
30893 this.currentSize = cs;
30899 layout : function()
30901 this._resetLayout();
30903 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30905 this.layoutItems( isInstant );
30907 this._isLayoutInited = true;
30909 this.fireEvent('layout', this);
30913 _resetLayout : function()
30915 if(this.isHorizontal){
30916 this.horizontalMeasureColumns();
30920 this.verticalMeasureColumns();
30924 verticalMeasureColumns : function()
30926 this.getContainerWidth();
30928 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30929 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30933 var boxWidth = this.boxWidth + this.padWidth;
30935 if(this.containerWidth < this.boxWidth){
30936 boxWidth = this.containerWidth
30939 var containerWidth = this.containerWidth;
30941 var cols = Math.floor(containerWidth / boxWidth);
30943 this.cols = Math.max( cols, 1 );
30945 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30947 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30949 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30951 this.colWidth = boxWidth + avail - this.padWidth;
30953 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30954 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30957 horizontalMeasureColumns : function()
30959 this.getContainerWidth();
30961 var boxWidth = this.boxWidth;
30963 if(this.containerWidth < boxWidth){
30964 boxWidth = this.containerWidth;
30967 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30969 this.el.setHeight(boxWidth);
30973 getContainerWidth : function()
30975 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30978 layoutItems : function( isInstant )
30980 Roo.log(this.bricks);
30982 var items = Roo.apply([], this.bricks);
30984 if(this.isHorizontal){
30985 this._horizontalLayoutItems( items , isInstant );
30989 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30990 // this._verticalAlternativeLayoutItems( items , isInstant );
30994 this._verticalLayoutItems( items , isInstant );
30998 _verticalLayoutItems : function ( items , isInstant)
31000 if ( !items || !items.length ) {
31005 ['xs', 'xs', 'xs', 'tall'],
31006 ['xs', 'xs', 'tall'],
31007 ['xs', 'xs', 'sm'],
31008 ['xs', 'xs', 'xs'],
31014 ['sm', 'xs', 'xs'],
31018 ['tall', 'xs', 'xs', 'xs'],
31019 ['tall', 'xs', 'xs'],
31031 Roo.each(items, function(item, k){
31033 switch (item.size) {
31034 // these layouts take up a full box,
31045 boxes.push([item]);
31068 var filterPattern = function(box, length)
31076 var pattern = box.slice(0, length);
31080 Roo.each(pattern, function(i){
31081 format.push(i.size);
31084 Roo.each(standard, function(s){
31086 if(String(s) != String(format)){
31095 if(!match && length == 1){
31100 filterPattern(box, length - 1);
31104 queue.push(pattern);
31106 box = box.slice(length, box.length);
31108 filterPattern(box, 4);
31114 Roo.each(boxes, function(box, k){
31120 if(box.length == 1){
31125 filterPattern(box, 4);
31129 this._processVerticalLayoutQueue( queue, isInstant );
31133 // _verticalAlternativeLayoutItems : function( items , isInstant )
31135 // if ( !items || !items.length ) {
31139 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31143 _horizontalLayoutItems : function ( items , isInstant)
31145 if ( !items || !items.length || items.length < 3) {
31151 var eItems = items.slice(0, 3);
31153 items = items.slice(3, items.length);
31156 ['xs', 'xs', 'xs', 'wide'],
31157 ['xs', 'xs', 'wide'],
31158 ['xs', 'xs', 'sm'],
31159 ['xs', 'xs', 'xs'],
31165 ['sm', 'xs', 'xs'],
31169 ['wide', 'xs', 'xs', 'xs'],
31170 ['wide', 'xs', 'xs'],
31183 Roo.each(items, function(item, k){
31185 switch (item.size) {
31196 boxes.push([item]);
31220 var filterPattern = function(box, length)
31228 var pattern = box.slice(0, length);
31232 Roo.each(pattern, function(i){
31233 format.push(i.size);
31236 Roo.each(standard, function(s){
31238 if(String(s) != String(format)){
31247 if(!match && length == 1){
31252 filterPattern(box, length - 1);
31256 queue.push(pattern);
31258 box = box.slice(length, box.length);
31260 filterPattern(box, 4);
31266 Roo.each(boxes, function(box, k){
31272 if(box.length == 1){
31277 filterPattern(box, 4);
31284 var pos = this.el.getBox(true);
31288 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31290 var hit_end = false;
31292 Roo.each(queue, function(box){
31296 Roo.each(box, function(b){
31298 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31308 Roo.each(box, function(b){
31310 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31313 mx = Math.max(mx, b.x);
31317 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31321 Roo.each(box, function(b){
31323 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31337 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31340 /** Sets position of item in DOM
31341 * @param {Element} item
31342 * @param {Number} x - horizontal position
31343 * @param {Number} y - vertical position
31344 * @param {Boolean} isInstant - disables transitions
31346 _processVerticalLayoutQueue : function( queue, isInstant )
31348 var pos = this.el.getBox(true);
31353 for (var i = 0; i < this.cols; i++){
31357 Roo.each(queue, function(box, k){
31359 var col = k % this.cols;
31361 Roo.each(box, function(b,kk){
31363 b.el.position('absolute');
31365 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31366 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31368 if(b.size == 'md-left' || b.size == 'md-right'){
31369 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31370 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31373 b.el.setWidth(width);
31374 b.el.setHeight(height);
31376 b.el.select('iframe',true).setSize(width,height);
31380 for (var i = 0; i < this.cols; i++){
31382 if(maxY[i] < maxY[col]){
31387 col = Math.min(col, i);
31391 x = pos.x + col * (this.colWidth + this.padWidth);
31395 var positions = [];
31397 switch (box.length){
31399 positions = this.getVerticalOneBoxColPositions(x, y, box);
31402 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31405 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31408 positions = this.getVerticalFourBoxColPositions(x, y, box);
31414 Roo.each(box, function(b,kk){
31416 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31418 var sz = b.el.getSize();
31420 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31428 for (var i = 0; i < this.cols; i++){
31429 mY = Math.max(mY, maxY[i]);
31432 this.el.setHeight(mY - pos.y);
31436 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31438 // var pos = this.el.getBox(true);
31441 // var maxX = pos.right;
31443 // var maxHeight = 0;
31445 // Roo.each(items, function(item, k){
31449 // item.el.position('absolute');
31451 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31453 // item.el.setWidth(width);
31455 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31457 // item.el.setHeight(height);
31460 // item.el.setXY([x, y], isInstant ? false : true);
31462 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31465 // y = y + height + this.alternativePadWidth;
31467 // maxHeight = maxHeight + height + this.alternativePadWidth;
31471 // this.el.setHeight(maxHeight);
31475 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31477 var pos = this.el.getBox(true);
31482 var maxX = pos.right;
31484 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31486 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31488 Roo.each(queue, function(box, k){
31490 Roo.each(box, function(b, kk){
31492 b.el.position('absolute');
31494 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31495 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31497 if(b.size == 'md-left' || b.size == 'md-right'){
31498 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31499 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31502 b.el.setWidth(width);
31503 b.el.setHeight(height);
31511 var positions = [];
31513 switch (box.length){
31515 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31518 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31521 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31524 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31530 Roo.each(box, function(b,kk){
31532 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31534 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31542 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31544 Roo.each(eItems, function(b,k){
31546 b.size = (k == 0) ? 'sm' : 'xs';
31547 b.x = (k == 0) ? 2 : 1;
31548 b.y = (k == 0) ? 2 : 1;
31550 b.el.position('absolute');
31552 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31554 b.el.setWidth(width);
31556 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31558 b.el.setHeight(height);
31562 var positions = [];
31565 x : maxX - this.unitWidth * 2 - this.gutter,
31570 x : maxX - this.unitWidth,
31571 y : minY + (this.unitWidth + this.gutter) * 2
31575 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31579 Roo.each(eItems, function(b,k){
31581 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31587 getVerticalOneBoxColPositions : function(x, y, box)
31591 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31593 if(box[0].size == 'md-left'){
31597 if(box[0].size == 'md-right'){
31602 x : x + (this.unitWidth + this.gutter) * rand,
31609 getVerticalTwoBoxColPositions : function(x, y, box)
31613 if(box[0].size == 'xs'){
31617 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31621 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31635 x : x + (this.unitWidth + this.gutter) * 2,
31636 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31643 getVerticalThreeBoxColPositions : function(x, y, box)
31647 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31655 x : x + (this.unitWidth + this.gutter) * 1,
31660 x : x + (this.unitWidth + this.gutter) * 2,
31668 if(box[0].size == 'xs' && box[1].size == 'xs'){
31677 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31681 x : x + (this.unitWidth + this.gutter) * 1,
31695 x : x + (this.unitWidth + this.gutter) * 2,
31700 x : x + (this.unitWidth + this.gutter) * 2,
31701 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31708 getVerticalFourBoxColPositions : function(x, y, box)
31712 if(box[0].size == 'xs'){
31721 y : y + (this.unitHeight + this.gutter) * 1
31726 y : y + (this.unitHeight + this.gutter) * 2
31730 x : x + (this.unitWidth + this.gutter) * 1,
31744 x : x + (this.unitWidth + this.gutter) * 2,
31749 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31750 y : y + (this.unitHeight + this.gutter) * 1
31754 x : x + (this.unitWidth + this.gutter) * 2,
31755 y : y + (this.unitWidth + this.gutter) * 2
31762 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31766 if(box[0].size == 'md-left'){
31768 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31775 if(box[0].size == 'md-right'){
31777 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31778 y : minY + (this.unitWidth + this.gutter) * 1
31784 var rand = Math.floor(Math.random() * (4 - box[0].y));
31787 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31788 y : minY + (this.unitWidth + this.gutter) * rand
31795 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31799 if(box[0].size == 'xs'){
31802 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31807 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31808 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31816 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31821 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31822 y : minY + (this.unitWidth + this.gutter) * 2
31829 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31833 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31836 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31841 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31842 y : minY + (this.unitWidth + this.gutter) * 1
31846 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31847 y : minY + (this.unitWidth + this.gutter) * 2
31854 if(box[0].size == 'xs' && box[1].size == 'xs'){
31857 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31862 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31867 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31868 y : minY + (this.unitWidth + this.gutter) * 1
31876 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31881 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31882 y : minY + (this.unitWidth + this.gutter) * 2
31886 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31887 y : minY + (this.unitWidth + this.gutter) * 2
31894 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31898 if(box[0].size == 'xs'){
31901 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31906 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31911 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),
31916 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31917 y : minY + (this.unitWidth + this.gutter) * 1
31925 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31930 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31931 y : minY + (this.unitWidth + this.gutter) * 2
31935 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31936 y : minY + (this.unitWidth + this.gutter) * 2
31940 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),
31941 y : minY + (this.unitWidth + this.gutter) * 2
31949 * remove a Masonry Brick
31950 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31952 removeBrick : function(brick_id)
31958 for (var i = 0; i<this.bricks.length; i++) {
31959 if (this.bricks[i].id == brick_id) {
31960 this.bricks.splice(i,1);
31961 this.el.dom.removeChild(Roo.get(brick_id).dom);
31968 * adds a Masonry Brick
31969 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31971 addBrick : function(cfg)
31973 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31974 //this.register(cn);
31975 cn.parentId = this.id;
31976 cn.onRender(this.el, null);
31981 * register a Masonry Brick
31982 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31985 register : function(brick)
31987 this.bricks.push(brick);
31988 brick.masonryId = this.id;
31992 * clear all the Masonry Brick
31994 clearAll : function()
31997 //this.getChildContainer().dom.innerHTML = "";
31998 this.el.dom.innerHTML = '';
32001 getSelected : function()
32003 if (!this.selectedBrick) {
32007 return this.selectedBrick;
32011 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32015 * register a Masonry Layout
32016 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32019 register : function(layout)
32021 this.groups[layout.id] = layout;
32024 * fetch a Masonry Layout based on the masonry layout ID
32025 * @param {string} the masonry layout to add
32026 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32029 get: function(layout_id) {
32030 if (typeof(this.groups[layout_id]) == 'undefined') {
32033 return this.groups[layout_id] ;
32045 * http://masonry.desandro.com
32047 * The idea is to render all the bricks based on vertical width...
32049 * The original code extends 'outlayer' - we might need to use that....
32055 * @class Roo.bootstrap.LayoutMasonryAuto
32056 * @extends Roo.bootstrap.Component
32057 * Bootstrap Layout Masonry class
32060 * Create a new Element
32061 * @param {Object} config The config object
32064 Roo.bootstrap.LayoutMasonryAuto = function(config){
32065 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32068 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32071 * @cfg {Boolean} isFitWidth - resize the width..
32073 isFitWidth : false, // options..
32075 * @cfg {Boolean} isOriginLeft = left align?
32077 isOriginLeft : true,
32079 * @cfg {Boolean} isOriginTop = top align?
32081 isOriginTop : false,
32083 * @cfg {Boolean} isLayoutInstant = no animation?
32085 isLayoutInstant : false, // needed?
32087 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32089 isResizingContainer : true,
32091 * @cfg {Number} columnWidth width of the columns
32097 * @cfg {Number} maxCols maximum number of columns
32102 * @cfg {Number} padHeight padding below box..
32108 * @cfg {Boolean} isAutoInitial defalut true
32111 isAutoInitial : true,
32117 initialColumnWidth : 0,
32118 currentSize : null,
32120 colYs : null, // array.
32127 bricks: null, //CompositeElement
32128 cols : 0, // array?
32129 // element : null, // wrapped now this.el
32130 _isLayoutInited : null,
32133 getAutoCreate : function(){
32137 cls: 'blog-masonary-wrapper ' + this.cls,
32139 cls : 'mas-boxes masonary'
32146 getChildContainer: function( )
32148 if (this.boxesEl) {
32149 return this.boxesEl;
32152 this.boxesEl = this.el.select('.mas-boxes').first();
32154 return this.boxesEl;
32158 initEvents : function()
32162 if(this.isAutoInitial){
32163 Roo.log('hook children rendered');
32164 this.on('childrenrendered', function() {
32165 Roo.log('children rendered');
32172 initial : function()
32174 this.reloadItems();
32176 this.currentSize = this.el.getBox(true);
32178 /// was window resize... - let's see if this works..
32179 Roo.EventManager.onWindowResize(this.resize, this);
32181 if(!this.isAutoInitial){
32186 this.layout.defer(500,this);
32189 reloadItems: function()
32191 this.bricks = this.el.select('.masonry-brick', true);
32193 this.bricks.each(function(b) {
32194 //Roo.log(b.getSize());
32195 if (!b.attr('originalwidth')) {
32196 b.attr('originalwidth', b.getSize().width);
32201 Roo.log(this.bricks.elements.length);
32204 resize : function()
32207 var cs = this.el.getBox(true);
32209 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32210 Roo.log("no change in with or X");
32213 this.currentSize = cs;
32217 layout : function()
32220 this._resetLayout();
32221 //this._manageStamps();
32223 // don't animate first layout
32224 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32225 this.layoutItems( isInstant );
32227 // flag for initalized
32228 this._isLayoutInited = true;
32231 layoutItems : function( isInstant )
32233 //var items = this._getItemsForLayout( this.items );
32234 // original code supports filtering layout items.. we just ignore it..
32236 this._layoutItems( this.bricks , isInstant );
32238 this._postLayout();
32240 _layoutItems : function ( items , isInstant)
32242 //this.fireEvent( 'layout', this, items );
32245 if ( !items || !items.elements.length ) {
32246 // no items, emit event with empty array
32251 items.each(function(item) {
32252 Roo.log("layout item");
32254 // get x/y object from method
32255 var position = this._getItemLayoutPosition( item );
32257 position.item = item;
32258 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32259 queue.push( position );
32262 this._processLayoutQueue( queue );
32264 /** Sets position of item in DOM
32265 * @param {Element} item
32266 * @param {Number} x - horizontal position
32267 * @param {Number} y - vertical position
32268 * @param {Boolean} isInstant - disables transitions
32270 _processLayoutQueue : function( queue )
32272 for ( var i=0, len = queue.length; i < len; i++ ) {
32273 var obj = queue[i];
32274 obj.item.position('absolute');
32275 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32281 * Any logic you want to do after each layout,
32282 * i.e. size the container
32284 _postLayout : function()
32286 this.resizeContainer();
32289 resizeContainer : function()
32291 if ( !this.isResizingContainer ) {
32294 var size = this._getContainerSize();
32296 this.el.setSize(size.width,size.height);
32297 this.boxesEl.setSize(size.width,size.height);
32303 _resetLayout : function()
32305 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32306 this.colWidth = this.el.getWidth();
32307 //this.gutter = this.el.getWidth();
32309 this.measureColumns();
32315 this.colYs.push( 0 );
32321 measureColumns : function()
32323 this.getContainerWidth();
32324 // if columnWidth is 0, default to outerWidth of first item
32325 if ( !this.columnWidth ) {
32326 var firstItem = this.bricks.first();
32327 Roo.log(firstItem);
32328 this.columnWidth = this.containerWidth;
32329 if (firstItem && firstItem.attr('originalwidth') ) {
32330 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32332 // columnWidth fall back to item of first element
32333 Roo.log("set column width?");
32334 this.initialColumnWidth = this.columnWidth ;
32336 // if first elem has no width, default to size of container
32341 if (this.initialColumnWidth) {
32342 this.columnWidth = this.initialColumnWidth;
32347 // column width is fixed at the top - however if container width get's smaller we should
32350 // this bit calcs how man columns..
32352 var columnWidth = this.columnWidth += this.gutter;
32354 // calculate columns
32355 var containerWidth = this.containerWidth + this.gutter;
32357 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32358 // fix rounding errors, typically with gutters
32359 var excess = columnWidth - containerWidth % columnWidth;
32362 // if overshoot is less than a pixel, round up, otherwise floor it
32363 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32364 cols = Math[ mathMethod ]( cols );
32365 this.cols = Math.max( cols, 1 );
32366 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32368 // padding positioning..
32369 var totalColWidth = this.cols * this.columnWidth;
32370 var padavail = this.containerWidth - totalColWidth;
32371 // so for 2 columns - we need 3 'pads'
32373 var padNeeded = (1+this.cols) * this.padWidth;
32375 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32377 this.columnWidth += padExtra
32378 //this.padWidth = Math.floor(padavail / ( this.cols));
32380 // adjust colum width so that padding is fixed??
32382 // we have 3 columns ... total = width * 3
32383 // we have X left over... that should be used by
32385 //if (this.expandC) {
32393 getContainerWidth : function()
32395 /* // container is parent if fit width
32396 var container = this.isFitWidth ? this.element.parentNode : this.element;
32397 // check that this.size and size are there
32398 // IE8 triggers resize on body size change, so they might not be
32400 var size = getSize( container ); //FIXME
32401 this.containerWidth = size && size.innerWidth; //FIXME
32404 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32408 _getItemLayoutPosition : function( item ) // what is item?
32410 // we resize the item to our columnWidth..
32412 item.setWidth(this.columnWidth);
32413 item.autoBoxAdjust = false;
32415 var sz = item.getSize();
32417 // how many columns does this brick span
32418 var remainder = this.containerWidth % this.columnWidth;
32420 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32421 // round if off by 1 pixel, otherwise use ceil
32422 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32423 colSpan = Math.min( colSpan, this.cols );
32425 // normally this should be '1' as we dont' currently allow multi width columns..
32427 var colGroup = this._getColGroup( colSpan );
32428 // get the minimum Y value from the columns
32429 var minimumY = Math.min.apply( Math, colGroup );
32430 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32432 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32434 // position the brick
32436 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32437 y: this.currentSize.y + minimumY + this.padHeight
32441 // apply setHeight to necessary columns
32442 var setHeight = minimumY + sz.height + this.padHeight;
32443 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32445 var setSpan = this.cols + 1 - colGroup.length;
32446 for ( var i = 0; i < setSpan; i++ ) {
32447 this.colYs[ shortColIndex + i ] = setHeight ;
32454 * @param {Number} colSpan - number of columns the element spans
32455 * @returns {Array} colGroup
32457 _getColGroup : function( colSpan )
32459 if ( colSpan < 2 ) {
32460 // if brick spans only one column, use all the column Ys
32465 // how many different places could this brick fit horizontally
32466 var groupCount = this.cols + 1 - colSpan;
32467 // for each group potential horizontal position
32468 for ( var i = 0; i < groupCount; i++ ) {
32469 // make an array of colY values for that one group
32470 var groupColYs = this.colYs.slice( i, i + colSpan );
32471 // and get the max value of the array
32472 colGroup[i] = Math.max.apply( Math, groupColYs );
32477 _manageStamp : function( stamp )
32479 var stampSize = stamp.getSize();
32480 var offset = stamp.getBox();
32481 // get the columns that this stamp affects
32482 var firstX = this.isOriginLeft ? offset.x : offset.right;
32483 var lastX = firstX + stampSize.width;
32484 var firstCol = Math.floor( firstX / this.columnWidth );
32485 firstCol = Math.max( 0, firstCol );
32487 var lastCol = Math.floor( lastX / this.columnWidth );
32488 // lastCol should not go over if multiple of columnWidth #425
32489 lastCol -= lastX % this.columnWidth ? 0 : 1;
32490 lastCol = Math.min( this.cols - 1, lastCol );
32492 // set colYs to bottom of the stamp
32493 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32496 for ( var i = firstCol; i <= lastCol; i++ ) {
32497 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32502 _getContainerSize : function()
32504 this.maxY = Math.max.apply( Math, this.colYs );
32509 if ( this.isFitWidth ) {
32510 size.width = this._getContainerFitWidth();
32516 _getContainerFitWidth : function()
32518 var unusedCols = 0;
32519 // count unused columns
32522 if ( this.colYs[i] !== 0 ) {
32527 // fit container to columns that have been used
32528 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32531 needsResizeLayout : function()
32533 var previousWidth = this.containerWidth;
32534 this.getContainerWidth();
32535 return previousWidth !== this.containerWidth;
32550 * @class Roo.bootstrap.MasonryBrick
32551 * @extends Roo.bootstrap.Component
32552 * Bootstrap MasonryBrick class
32555 * Create a new MasonryBrick
32556 * @param {Object} config The config object
32559 Roo.bootstrap.MasonryBrick = function(config){
32561 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32563 Roo.bootstrap.MasonryBrick.register(this);
32569 * When a MasonryBrick is clcik
32570 * @param {Roo.bootstrap.MasonryBrick} this
32571 * @param {Roo.EventObject} e
32577 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32580 * @cfg {String} title
32584 * @cfg {String} html
32588 * @cfg {String} bgimage
32592 * @cfg {String} videourl
32596 * @cfg {String} cls
32600 * @cfg {String} href
32604 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32609 * @cfg {String} placetitle (center|bottom)
32614 * @cfg {Boolean} isFitContainer defalut true
32616 isFitContainer : true,
32619 * @cfg {Boolean} preventDefault defalut false
32621 preventDefault : false,
32624 * @cfg {Boolean} inverse defalut false
32626 maskInverse : false,
32628 getAutoCreate : function()
32630 if(!this.isFitContainer){
32631 return this.getSplitAutoCreate();
32634 var cls = 'masonry-brick masonry-brick-full';
32636 if(this.href.length){
32637 cls += ' masonry-brick-link';
32640 if(this.bgimage.length){
32641 cls += ' masonry-brick-image';
32644 if(this.maskInverse){
32645 cls += ' mask-inverse';
32648 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32649 cls += ' enable-mask';
32653 cls += ' masonry-' + this.size + '-brick';
32656 if(this.placetitle.length){
32658 switch (this.placetitle) {
32660 cls += ' masonry-center-title';
32663 cls += ' masonry-bottom-title';
32670 if(!this.html.length && !this.bgimage.length){
32671 cls += ' masonry-center-title';
32674 if(!this.html.length && this.bgimage.length){
32675 cls += ' masonry-bottom-title';
32680 cls += ' ' + this.cls;
32684 tag: (this.href.length) ? 'a' : 'div',
32689 cls: 'masonry-brick-mask'
32693 cls: 'masonry-brick-paragraph',
32699 if(this.href.length){
32700 cfg.href = this.href;
32703 var cn = cfg.cn[1].cn;
32705 if(this.title.length){
32708 cls: 'masonry-brick-title',
32713 if(this.html.length){
32716 cls: 'masonry-brick-text',
32721 if (!this.title.length && !this.html.length) {
32722 cfg.cn[1].cls += ' hide';
32725 if(this.bgimage.length){
32728 cls: 'masonry-brick-image-view',
32733 if(this.videourl.length){
32734 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32735 // youtube support only?
32738 cls: 'masonry-brick-image-view',
32741 allowfullscreen : true
32749 getSplitAutoCreate : function()
32751 var cls = 'masonry-brick masonry-brick-split';
32753 if(this.href.length){
32754 cls += ' masonry-brick-link';
32757 if(this.bgimage.length){
32758 cls += ' masonry-brick-image';
32762 cls += ' masonry-' + this.size + '-brick';
32765 switch (this.placetitle) {
32767 cls += ' masonry-center-title';
32770 cls += ' masonry-bottom-title';
32773 if(!this.bgimage.length){
32774 cls += ' masonry-center-title';
32777 if(this.bgimage.length){
32778 cls += ' masonry-bottom-title';
32784 cls += ' ' + this.cls;
32788 tag: (this.href.length) ? 'a' : 'div',
32793 cls: 'masonry-brick-split-head',
32797 cls: 'masonry-brick-paragraph',
32804 cls: 'masonry-brick-split-body',
32810 if(this.href.length){
32811 cfg.href = this.href;
32814 if(this.title.length){
32815 cfg.cn[0].cn[0].cn.push({
32817 cls: 'masonry-brick-title',
32822 if(this.html.length){
32823 cfg.cn[1].cn.push({
32825 cls: 'masonry-brick-text',
32830 if(this.bgimage.length){
32831 cfg.cn[0].cn.push({
32833 cls: 'masonry-brick-image-view',
32838 if(this.videourl.length){
32839 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32840 // youtube support only?
32841 cfg.cn[0].cn.cn.push({
32843 cls: 'masonry-brick-image-view',
32846 allowfullscreen : true
32853 initEvents: function()
32855 switch (this.size) {
32888 this.el.on('touchstart', this.onTouchStart, this);
32889 this.el.on('touchmove', this.onTouchMove, this);
32890 this.el.on('touchend', this.onTouchEnd, this);
32891 this.el.on('contextmenu', this.onContextMenu, this);
32893 this.el.on('mouseenter' ,this.enter, this);
32894 this.el.on('mouseleave', this.leave, this);
32895 this.el.on('click', this.onClick, this);
32898 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32899 this.parent().bricks.push(this);
32904 onClick: function(e, el)
32906 var time = this.endTimer - this.startTimer;
32907 // Roo.log(e.preventDefault());
32910 e.preventDefault();
32915 if(!this.preventDefault){
32919 e.preventDefault();
32921 if (this.activeClass != '') {
32922 this.selectBrick();
32925 this.fireEvent('click', this, e);
32928 enter: function(e, el)
32930 e.preventDefault();
32932 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32936 if(this.bgimage.length && this.html.length){
32937 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32941 leave: function(e, el)
32943 e.preventDefault();
32945 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32949 if(this.bgimage.length && this.html.length){
32950 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32954 onTouchStart: function(e, el)
32956 // e.preventDefault();
32958 this.touchmoved = false;
32960 if(!this.isFitContainer){
32964 if(!this.bgimage.length || !this.html.length){
32968 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32970 this.timer = new Date().getTime();
32974 onTouchMove: function(e, el)
32976 this.touchmoved = true;
32979 onContextMenu : function(e,el)
32981 e.preventDefault();
32982 e.stopPropagation();
32986 onTouchEnd: function(e, el)
32988 // e.preventDefault();
32990 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32997 if(!this.bgimage.length || !this.html.length){
32999 if(this.href.length){
33000 window.location.href = this.href;
33006 if(!this.isFitContainer){
33010 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33012 window.location.href = this.href;
33015 //selection on single brick only
33016 selectBrick : function() {
33018 if (!this.parentId) {
33022 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33023 var index = m.selectedBrick.indexOf(this.id);
33026 m.selectedBrick.splice(index,1);
33027 this.el.removeClass(this.activeClass);
33031 for(var i = 0; i < m.selectedBrick.length; i++) {
33032 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33033 b.el.removeClass(b.activeClass);
33036 m.selectedBrick = [];
33038 m.selectedBrick.push(this.id);
33039 this.el.addClass(this.activeClass);
33043 isSelected : function(){
33044 return this.el.hasClass(this.activeClass);
33049 Roo.apply(Roo.bootstrap.MasonryBrick, {
33052 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33054 * register a Masonry Brick
33055 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33058 register : function(brick)
33060 //this.groups[brick.id] = brick;
33061 this.groups.add(brick.id, brick);
33064 * fetch a masonry brick based on the masonry brick ID
33065 * @param {string} the masonry brick to add
33066 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33069 get: function(brick_id)
33071 // if (typeof(this.groups[brick_id]) == 'undefined') {
33074 // return this.groups[brick_id] ;
33076 if(this.groups.key(brick_id)) {
33077 return this.groups.key(brick_id);
33095 * @class Roo.bootstrap.Brick
33096 * @extends Roo.bootstrap.Component
33097 * Bootstrap Brick class
33100 * Create a new Brick
33101 * @param {Object} config The config object
33104 Roo.bootstrap.Brick = function(config){
33105 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33111 * When a Brick is click
33112 * @param {Roo.bootstrap.Brick} this
33113 * @param {Roo.EventObject} e
33119 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33122 * @cfg {String} title
33126 * @cfg {String} html
33130 * @cfg {String} bgimage
33134 * @cfg {String} cls
33138 * @cfg {String} href
33142 * @cfg {String} video
33146 * @cfg {Boolean} square
33150 getAutoCreate : function()
33152 var cls = 'roo-brick';
33154 if(this.href.length){
33155 cls += ' roo-brick-link';
33158 if(this.bgimage.length){
33159 cls += ' roo-brick-image';
33162 if(!this.html.length && !this.bgimage.length){
33163 cls += ' roo-brick-center-title';
33166 if(!this.html.length && this.bgimage.length){
33167 cls += ' roo-brick-bottom-title';
33171 cls += ' ' + this.cls;
33175 tag: (this.href.length) ? 'a' : 'div',
33180 cls: 'roo-brick-paragraph',
33186 if(this.href.length){
33187 cfg.href = this.href;
33190 var cn = cfg.cn[0].cn;
33192 if(this.title.length){
33195 cls: 'roo-brick-title',
33200 if(this.html.length){
33203 cls: 'roo-brick-text',
33210 if(this.bgimage.length){
33213 cls: 'roo-brick-image-view',
33221 initEvents: function()
33223 if(this.title.length || this.html.length){
33224 this.el.on('mouseenter' ,this.enter, this);
33225 this.el.on('mouseleave', this.leave, this);
33228 Roo.EventManager.onWindowResize(this.resize, this);
33230 if(this.bgimage.length){
33231 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33232 this.imageEl.on('load', this.onImageLoad, this);
33239 onImageLoad : function()
33244 resize : function()
33246 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33248 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33250 if(this.bgimage.length){
33251 var image = this.el.select('.roo-brick-image-view', true).first();
33253 image.setWidth(paragraph.getWidth());
33256 image.setHeight(paragraph.getWidth());
33259 this.el.setHeight(image.getHeight());
33260 paragraph.setHeight(image.getHeight());
33266 enter: function(e, el)
33268 e.preventDefault();
33270 if(this.bgimage.length){
33271 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33272 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33276 leave: function(e, el)
33278 e.preventDefault();
33280 if(this.bgimage.length){
33281 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33282 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33297 * @class Roo.bootstrap.NumberField
33298 * @extends Roo.bootstrap.Input
33299 * Bootstrap NumberField class
33305 * Create a new NumberField
33306 * @param {Object} config The config object
33309 Roo.bootstrap.NumberField = function(config){
33310 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33313 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33316 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33318 allowDecimals : true,
33320 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33322 decimalSeparator : ".",
33324 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33326 decimalPrecision : 2,
33328 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33330 allowNegative : true,
33333 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33337 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33339 minValue : Number.NEGATIVE_INFINITY,
33341 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33343 maxValue : Number.MAX_VALUE,
33345 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33347 minText : "The minimum value for this field is {0}",
33349 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33351 maxText : "The maximum value for this field is {0}",
33353 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33354 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33356 nanText : "{0} is not a valid number",
33358 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33360 thousandsDelimiter : false,
33362 * @cfg {String} valueAlign alignment of value
33364 valueAlign : "left",
33366 getAutoCreate : function()
33368 var hiddenInput = {
33372 cls: 'hidden-number-input'
33376 hiddenInput.name = this.name;
33381 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33383 this.name = hiddenInput.name;
33385 if(cfg.cn.length > 0) {
33386 cfg.cn.push(hiddenInput);
33393 initEvents : function()
33395 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33397 var allowed = "0123456789";
33399 if(this.allowDecimals){
33400 allowed += this.decimalSeparator;
33403 if(this.allowNegative){
33407 if(this.thousandsDelimiter) {
33411 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33413 var keyPress = function(e){
33415 var k = e.getKey();
33417 var c = e.getCharCode();
33420 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33421 allowed.indexOf(String.fromCharCode(c)) === -1
33427 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33431 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33436 this.el.on("keypress", keyPress, this);
33439 validateValue : function(value)
33442 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33446 var num = this.parseValue(value);
33449 this.markInvalid(String.format(this.nanText, value));
33453 if(num < this.minValue){
33454 this.markInvalid(String.format(this.minText, this.minValue));
33458 if(num > this.maxValue){
33459 this.markInvalid(String.format(this.maxText, this.maxValue));
33466 getValue : function()
33468 var v = this.hiddenEl().getValue();
33470 return this.fixPrecision(this.parseValue(v));
33473 parseValue : function(value)
33475 if(this.thousandsDelimiter) {
33477 r = new RegExp(",", "g");
33478 value = value.replace(r, "");
33481 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33482 return isNaN(value) ? '' : value;
33485 fixPrecision : function(value)
33487 if(this.thousandsDelimiter) {
33489 r = new RegExp(",", "g");
33490 value = value.replace(r, "");
33493 var nan = isNaN(value);
33495 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33496 return nan ? '' : value;
33498 return parseFloat(value).toFixed(this.decimalPrecision);
33501 setValue : function(v)
33503 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33509 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33511 this.inputEl().dom.value = (v == '') ? '' :
33512 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33514 if(!this.allowZero && v === '0') {
33515 this.hiddenEl().dom.value = '';
33516 this.inputEl().dom.value = '';
33523 decimalPrecisionFcn : function(v)
33525 return Math.floor(v);
33528 beforeBlur : function()
33530 var v = this.parseValue(this.getRawValue());
33532 if(v || v === 0 || v === ''){
33537 hiddenEl : function()
33539 return this.el.select('input.hidden-number-input',true).first();
33551 * @class Roo.bootstrap.DocumentSlider
33552 * @extends Roo.bootstrap.Component
33553 * Bootstrap DocumentSlider class
33556 * Create a new DocumentViewer
33557 * @param {Object} config The config object
33560 Roo.bootstrap.DocumentSlider = function(config){
33561 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33568 * Fire after initEvent
33569 * @param {Roo.bootstrap.DocumentSlider} this
33574 * Fire after update
33575 * @param {Roo.bootstrap.DocumentSlider} this
33581 * @param {Roo.bootstrap.DocumentSlider} this
33587 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33593 getAutoCreate : function()
33597 cls : 'roo-document-slider',
33601 cls : 'roo-document-slider-header',
33605 cls : 'roo-document-slider-header-title'
33611 cls : 'roo-document-slider-body',
33615 cls : 'roo-document-slider-prev',
33619 cls : 'fa fa-chevron-left'
33625 cls : 'roo-document-slider-thumb',
33629 cls : 'roo-document-slider-image'
33635 cls : 'roo-document-slider-next',
33639 cls : 'fa fa-chevron-right'
33651 initEvents : function()
33653 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33654 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33656 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33657 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33659 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33660 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33662 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33663 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33665 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33666 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33668 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33669 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33671 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33672 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33674 this.thumbEl.on('click', this.onClick, this);
33676 this.prevIndicator.on('click', this.prev, this);
33678 this.nextIndicator.on('click', this.next, this);
33682 initial : function()
33684 if(this.files.length){
33685 this.indicator = 1;
33689 this.fireEvent('initial', this);
33692 update : function()
33694 this.imageEl.attr('src', this.files[this.indicator - 1]);
33696 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33698 this.prevIndicator.show();
33700 if(this.indicator == 1){
33701 this.prevIndicator.hide();
33704 this.nextIndicator.show();
33706 if(this.indicator == this.files.length){
33707 this.nextIndicator.hide();
33710 this.thumbEl.scrollTo('top');
33712 this.fireEvent('update', this);
33715 onClick : function(e)
33717 e.preventDefault();
33719 this.fireEvent('click', this);
33724 e.preventDefault();
33726 this.indicator = Math.max(1, this.indicator - 1);
33733 e.preventDefault();
33735 this.indicator = Math.min(this.files.length, this.indicator + 1);
33749 * @class Roo.bootstrap.RadioSet
33750 * @extends Roo.bootstrap.Input
33751 * Bootstrap RadioSet class
33752 * @cfg {String} indicatorpos (left|right) default left
33753 * @cfg {Boolean} inline (true|false) inline the element (default true)
33754 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33756 * Create a new RadioSet
33757 * @param {Object} config The config object
33760 Roo.bootstrap.RadioSet = function(config){
33762 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33766 Roo.bootstrap.RadioSet.register(this);
33771 * Fires when the element is checked or unchecked.
33772 * @param {Roo.bootstrap.RadioSet} this This radio
33773 * @param {Roo.bootstrap.Radio} item The checked item
33778 * Fires when the element is click.
33779 * @param {Roo.bootstrap.RadioSet} this This radio set
33780 * @param {Roo.bootstrap.Radio} item The checked item
33781 * @param {Roo.EventObject} e The event object
33788 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33796 indicatorpos : 'left',
33798 getAutoCreate : function()
33802 cls : 'roo-radio-set-label',
33806 html : this.fieldLabel
33811 if(this.indicatorpos == 'left'){
33814 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33815 tooltip : 'This field is required'
33820 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33821 tooltip : 'This field is required'
33827 cls : 'roo-radio-set-items'
33830 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33832 if (align === 'left' && this.fieldLabel.length) {
33835 cls : "roo-radio-set-right",
33841 if(this.labelWidth > 12){
33842 label.style = "width: " + this.labelWidth + 'px';
33845 if(this.labelWidth < 13 && this.labelmd == 0){
33846 this.labelmd = this.labelWidth;
33849 if(this.labellg > 0){
33850 label.cls += ' col-lg-' + this.labellg;
33851 items.cls += ' col-lg-' + (12 - this.labellg);
33854 if(this.labelmd > 0){
33855 label.cls += ' col-md-' + this.labelmd;
33856 items.cls += ' col-md-' + (12 - this.labelmd);
33859 if(this.labelsm > 0){
33860 label.cls += ' col-sm-' + this.labelsm;
33861 items.cls += ' col-sm-' + (12 - this.labelsm);
33864 if(this.labelxs > 0){
33865 label.cls += ' col-xs-' + this.labelxs;
33866 items.cls += ' col-xs-' + (12 - this.labelxs);
33872 cls : 'roo-radio-set',
33876 cls : 'roo-radio-set-input',
33879 value : this.value ? this.value : ''
33886 if(this.weight.length){
33887 cfg.cls += ' roo-radio-' + this.weight;
33891 cfg.cls += ' roo-radio-set-inline';
33895 ['xs','sm','md','lg'].map(function(size){
33896 if (settings[size]) {
33897 cfg.cls += ' col-' + size + '-' + settings[size];
33905 initEvents : function()
33907 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33908 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33910 if(!this.fieldLabel.length){
33911 this.labelEl.hide();
33914 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33915 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33917 this.indicator = this.indicatorEl();
33919 if(this.indicator){
33920 this.indicator.addClass('invisible');
33923 this.originalValue = this.getValue();
33927 inputEl: function ()
33929 return this.el.select('.roo-radio-set-input', true).first();
33932 getChildContainer : function()
33934 return this.itemsEl;
33937 register : function(item)
33939 this.radioes.push(item);
33943 validate : function()
33945 if(this.getVisibilityEl().hasClass('hidden')){
33951 Roo.each(this.radioes, function(i){
33960 if(this.allowBlank) {
33964 if(this.disabled || valid){
33969 this.markInvalid();
33974 markValid : function()
33976 if(this.labelEl.isVisible(true)){
33977 this.indicatorEl().removeClass('visible');
33978 this.indicatorEl().addClass('invisible');
33981 this.el.removeClass([this.invalidClass, this.validClass]);
33982 this.el.addClass(this.validClass);
33984 this.fireEvent('valid', this);
33987 markInvalid : function(msg)
33989 if(this.allowBlank || this.disabled){
33993 if(this.labelEl.isVisible(true)){
33994 this.indicatorEl().removeClass('invisible');
33995 this.indicatorEl().addClass('visible');
33998 this.el.removeClass([this.invalidClass, this.validClass]);
33999 this.el.addClass(this.invalidClass);
34001 this.fireEvent('invalid', this, msg);
34005 setValue : function(v, suppressEvent)
34007 if(this.value === v){
34014 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34017 Roo.each(this.radioes, function(i){
34019 i.el.removeClass('checked');
34022 Roo.each(this.radioes, function(i){
34024 if(i.value === v || i.value.toString() === v.toString()){
34026 i.el.addClass('checked');
34028 if(suppressEvent !== true){
34029 this.fireEvent('check', this, i);
34040 clearInvalid : function(){
34042 if(!this.el || this.preventMark){
34046 this.el.removeClass([this.invalidClass]);
34048 this.fireEvent('valid', this);
34053 Roo.apply(Roo.bootstrap.RadioSet, {
34057 register : function(set)
34059 this.groups[set.name] = set;
34062 get: function(name)
34064 if (typeof(this.groups[name]) == 'undefined') {
34068 return this.groups[name] ;
34074 * Ext JS Library 1.1.1
34075 * Copyright(c) 2006-2007, Ext JS, LLC.
34077 * Originally Released Under LGPL - original licence link has changed is not relivant.
34080 * <script type="text/javascript">
34085 * @class Roo.bootstrap.SplitBar
34086 * @extends Roo.util.Observable
34087 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34091 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34092 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34093 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34094 split.minSize = 100;
34095 split.maxSize = 600;
34096 split.animate = true;
34097 split.on('moved', splitterMoved);
34100 * Create a new SplitBar
34101 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34102 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34103 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34104 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34105 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34106 position of the SplitBar).
34108 Roo.bootstrap.SplitBar = function(cfg){
34113 // dragElement : elm
34114 // resizingElement: el,
34116 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34117 // placement : Roo.bootstrap.SplitBar.LEFT ,
34118 // existingProxy ???
34121 this.el = Roo.get(cfg.dragElement, true);
34122 this.el.dom.unselectable = "on";
34124 this.resizingEl = Roo.get(cfg.resizingElement, true);
34128 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34129 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34132 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34135 * The minimum size of the resizing element. (Defaults to 0)
34141 * The maximum size of the resizing element. (Defaults to 2000)
34144 this.maxSize = 2000;
34147 * Whether to animate the transition to the new size
34150 this.animate = false;
34153 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34156 this.useShim = false;
34161 if(!cfg.existingProxy){
34163 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34165 this.proxy = Roo.get(cfg.existingProxy).dom;
34168 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34171 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34174 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34177 this.dragSpecs = {};
34180 * @private The adapter to use to positon and resize elements
34182 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34183 this.adapter.init(this);
34185 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34187 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34188 this.el.addClass("roo-splitbar-h");
34191 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34192 this.el.addClass("roo-splitbar-v");
34198 * Fires when the splitter is moved (alias for {@link #event-moved})
34199 * @param {Roo.bootstrap.SplitBar} this
34200 * @param {Number} newSize the new width or height
34205 * Fires when the splitter is moved
34206 * @param {Roo.bootstrap.SplitBar} this
34207 * @param {Number} newSize the new width or height
34211 * @event beforeresize
34212 * Fires before the splitter is dragged
34213 * @param {Roo.bootstrap.SplitBar} this
34215 "beforeresize" : true,
34217 "beforeapply" : true
34220 Roo.util.Observable.call(this);
34223 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34224 onStartProxyDrag : function(x, y){
34225 this.fireEvent("beforeresize", this);
34227 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34229 o.enableDisplayMode("block");
34230 // all splitbars share the same overlay
34231 Roo.bootstrap.SplitBar.prototype.overlay = o;
34233 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34234 this.overlay.show();
34235 Roo.get(this.proxy).setDisplayed("block");
34236 var size = this.adapter.getElementSize(this);
34237 this.activeMinSize = this.getMinimumSize();;
34238 this.activeMaxSize = this.getMaximumSize();;
34239 var c1 = size - this.activeMinSize;
34240 var c2 = Math.max(this.activeMaxSize - size, 0);
34241 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34242 this.dd.resetConstraints();
34243 this.dd.setXConstraint(
34244 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34245 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34247 this.dd.setYConstraint(0, 0);
34249 this.dd.resetConstraints();
34250 this.dd.setXConstraint(0, 0);
34251 this.dd.setYConstraint(
34252 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34253 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34256 this.dragSpecs.startSize = size;
34257 this.dragSpecs.startPoint = [x, y];
34258 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34262 * @private Called after the drag operation by the DDProxy
34264 onEndProxyDrag : function(e){
34265 Roo.get(this.proxy).setDisplayed(false);
34266 var endPoint = Roo.lib.Event.getXY(e);
34268 this.overlay.hide();
34271 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34272 newSize = this.dragSpecs.startSize +
34273 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34274 endPoint[0] - this.dragSpecs.startPoint[0] :
34275 this.dragSpecs.startPoint[0] - endPoint[0]
34278 newSize = this.dragSpecs.startSize +
34279 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34280 endPoint[1] - this.dragSpecs.startPoint[1] :
34281 this.dragSpecs.startPoint[1] - endPoint[1]
34284 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34285 if(newSize != this.dragSpecs.startSize){
34286 if(this.fireEvent('beforeapply', this, newSize) !== false){
34287 this.adapter.setElementSize(this, newSize);
34288 this.fireEvent("moved", this, newSize);
34289 this.fireEvent("resize", this, newSize);
34295 * Get the adapter this SplitBar uses
34296 * @return The adapter object
34298 getAdapter : function(){
34299 return this.adapter;
34303 * Set the adapter this SplitBar uses
34304 * @param {Object} adapter A SplitBar adapter object
34306 setAdapter : function(adapter){
34307 this.adapter = adapter;
34308 this.adapter.init(this);
34312 * Gets the minimum size for the resizing element
34313 * @return {Number} The minimum size
34315 getMinimumSize : function(){
34316 return this.minSize;
34320 * Sets the minimum size for the resizing element
34321 * @param {Number} minSize The minimum size
34323 setMinimumSize : function(minSize){
34324 this.minSize = minSize;
34328 * Gets the maximum size for the resizing element
34329 * @return {Number} The maximum size
34331 getMaximumSize : function(){
34332 return this.maxSize;
34336 * Sets the maximum size for the resizing element
34337 * @param {Number} maxSize The maximum size
34339 setMaximumSize : function(maxSize){
34340 this.maxSize = maxSize;
34344 * Sets the initialize size for the resizing element
34345 * @param {Number} size The initial size
34347 setCurrentSize : function(size){
34348 var oldAnimate = this.animate;
34349 this.animate = false;
34350 this.adapter.setElementSize(this, size);
34351 this.animate = oldAnimate;
34355 * Destroy this splitbar.
34356 * @param {Boolean} removeEl True to remove the element
34358 destroy : function(removeEl){
34360 this.shim.remove();
34363 this.proxy.parentNode.removeChild(this.proxy);
34371 * @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.
34373 Roo.bootstrap.SplitBar.createProxy = function(dir){
34374 var proxy = new Roo.Element(document.createElement("div"));
34375 proxy.unselectable();
34376 var cls = 'roo-splitbar-proxy';
34377 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34378 document.body.appendChild(proxy.dom);
34383 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34384 * Default Adapter. It assumes the splitter and resizing element are not positioned
34385 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34387 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34390 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34391 // do nothing for now
34392 init : function(s){
34396 * Called before drag operations to get the current size of the resizing element.
34397 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34399 getElementSize : function(s){
34400 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34401 return s.resizingEl.getWidth();
34403 return s.resizingEl.getHeight();
34408 * Called after drag operations to set the size of the resizing element.
34409 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34410 * @param {Number} newSize The new size to set
34411 * @param {Function} onComplete A function to be invoked when resizing is complete
34413 setElementSize : function(s, newSize, onComplete){
34414 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34416 s.resizingEl.setWidth(newSize);
34418 onComplete(s, newSize);
34421 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34426 s.resizingEl.setHeight(newSize);
34428 onComplete(s, newSize);
34431 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34438 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34439 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34440 * Adapter that moves the splitter element to align with the resized sizing element.
34441 * Used with an absolute positioned SplitBar.
34442 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34443 * document.body, make sure you assign an id to the body element.
34445 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34446 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34447 this.container = Roo.get(container);
34450 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34451 init : function(s){
34452 this.basic.init(s);
34455 getElementSize : function(s){
34456 return this.basic.getElementSize(s);
34459 setElementSize : function(s, newSize, onComplete){
34460 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34463 moveSplitter : function(s){
34464 var yes = Roo.bootstrap.SplitBar;
34465 switch(s.placement){
34467 s.el.setX(s.resizingEl.getRight());
34470 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34473 s.el.setY(s.resizingEl.getBottom());
34476 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34483 * Orientation constant - Create a vertical SplitBar
34487 Roo.bootstrap.SplitBar.VERTICAL = 1;
34490 * Orientation constant - Create a horizontal SplitBar
34494 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34497 * Placement constant - The resizing element is to the left of the splitter element
34501 Roo.bootstrap.SplitBar.LEFT = 1;
34504 * Placement constant - The resizing element is to the right of the splitter element
34508 Roo.bootstrap.SplitBar.RIGHT = 2;
34511 * Placement constant - The resizing element is positioned above the splitter element
34515 Roo.bootstrap.SplitBar.TOP = 3;
34518 * Placement constant - The resizing element is positioned under splitter element
34522 Roo.bootstrap.SplitBar.BOTTOM = 4;
34523 Roo.namespace("Roo.bootstrap.layout");/*
34525 * Ext JS Library 1.1.1
34526 * Copyright(c) 2006-2007, Ext JS, LLC.
34528 * Originally Released Under LGPL - original licence link has changed is not relivant.
34531 * <script type="text/javascript">
34535 * @class Roo.bootstrap.layout.Manager
34536 * @extends Roo.bootstrap.Component
34537 * Base class for layout managers.
34539 Roo.bootstrap.layout.Manager = function(config)
34541 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34547 /** false to disable window resize monitoring @type Boolean */
34548 this.monitorWindowResize = true;
34553 * Fires when a layout is performed.
34554 * @param {Roo.LayoutManager} this
34558 * @event regionresized
34559 * Fires when the user resizes a region.
34560 * @param {Roo.LayoutRegion} region The resized region
34561 * @param {Number} newSize The new size (width for east/west, height for north/south)
34563 "regionresized" : true,
34565 * @event regioncollapsed
34566 * Fires when a region is collapsed.
34567 * @param {Roo.LayoutRegion} region The collapsed region
34569 "regioncollapsed" : true,
34571 * @event regionexpanded
34572 * Fires when a region is expanded.
34573 * @param {Roo.LayoutRegion} region The expanded region
34575 "regionexpanded" : true
34577 this.updating = false;
34580 this.el = Roo.get(config.el);
34586 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34591 monitorWindowResize : true,
34597 onRender : function(ct, position)
34600 this.el = Roo.get(ct);
34603 //this.fireEvent('render',this);
34607 initEvents: function()
34611 // ie scrollbar fix
34612 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34613 document.body.scroll = "no";
34614 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34615 this.el.position('relative');
34617 this.id = this.el.id;
34618 this.el.addClass("roo-layout-container");
34619 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34620 if(this.el.dom != document.body ) {
34621 this.el.on('resize', this.layout,this);
34622 this.el.on('show', this.layout,this);
34628 * Returns true if this layout is currently being updated
34629 * @return {Boolean}
34631 isUpdating : function(){
34632 return this.updating;
34636 * Suspend the LayoutManager from doing auto-layouts while
34637 * making multiple add or remove calls
34639 beginUpdate : function(){
34640 this.updating = true;
34644 * Restore auto-layouts and optionally disable the manager from performing a layout
34645 * @param {Boolean} noLayout true to disable a layout update
34647 endUpdate : function(noLayout){
34648 this.updating = false;
34654 layout: function(){
34658 onRegionResized : function(region, newSize){
34659 this.fireEvent("regionresized", region, newSize);
34663 onRegionCollapsed : function(region){
34664 this.fireEvent("regioncollapsed", region);
34667 onRegionExpanded : function(region){
34668 this.fireEvent("regionexpanded", region);
34672 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34673 * performs box-model adjustments.
34674 * @return {Object} The size as an object {width: (the width), height: (the height)}
34676 getViewSize : function()
34679 if(this.el.dom != document.body){
34680 size = this.el.getSize();
34682 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34684 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34685 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34690 * Returns the Element this layout is bound to.
34691 * @return {Roo.Element}
34693 getEl : function(){
34698 * Returns the specified region.
34699 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34700 * @return {Roo.LayoutRegion}
34702 getRegion : function(target){
34703 return this.regions[target.toLowerCase()];
34706 onWindowResize : function(){
34707 if(this.monitorWindowResize){
34714 * Ext JS Library 1.1.1
34715 * Copyright(c) 2006-2007, Ext JS, LLC.
34717 * Originally Released Under LGPL - original licence link has changed is not relivant.
34720 * <script type="text/javascript">
34723 * @class Roo.bootstrap.layout.Border
34724 * @extends Roo.bootstrap.layout.Manager
34725 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34726 * please see: examples/bootstrap/nested.html<br><br>
34728 <b>The container the layout is rendered into can be either the body element or any other element.
34729 If it is not the body element, the container needs to either be an absolute positioned element,
34730 or you will need to add "position:relative" to the css of the container. You will also need to specify
34731 the container size if it is not the body element.</b>
34734 * Create a new Border
34735 * @param {Object} config Configuration options
34737 Roo.bootstrap.layout.Border = function(config){
34738 config = config || {};
34739 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34743 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34744 if(config[region]){
34745 config[region].region = region;
34746 this.addRegion(config[region]);
34752 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34754 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34756 * Creates and adds a new region if it doesn't already exist.
34757 * @param {String} target The target region key (north, south, east, west or center).
34758 * @param {Object} config The regions config object
34759 * @return {BorderLayoutRegion} The new region
34761 addRegion : function(config)
34763 if(!this.regions[config.region]){
34764 var r = this.factory(config);
34765 this.bindRegion(r);
34767 return this.regions[config.region];
34771 bindRegion : function(r){
34772 this.regions[r.config.region] = r;
34774 r.on("visibilitychange", this.layout, this);
34775 r.on("paneladded", this.layout, this);
34776 r.on("panelremoved", this.layout, this);
34777 r.on("invalidated", this.layout, this);
34778 r.on("resized", this.onRegionResized, this);
34779 r.on("collapsed", this.onRegionCollapsed, this);
34780 r.on("expanded", this.onRegionExpanded, this);
34784 * Performs a layout update.
34786 layout : function()
34788 if(this.updating) {
34792 // render all the rebions if they have not been done alreayd?
34793 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34794 if(this.regions[region] && !this.regions[region].bodyEl){
34795 this.regions[region].onRender(this.el)
34799 var size = this.getViewSize();
34800 var w = size.width;
34801 var h = size.height;
34806 //var x = 0, y = 0;
34808 var rs = this.regions;
34809 var north = rs["north"];
34810 var south = rs["south"];
34811 var west = rs["west"];
34812 var east = rs["east"];
34813 var center = rs["center"];
34814 //if(this.hideOnLayout){ // not supported anymore
34815 //c.el.setStyle("display", "none");
34817 if(north && north.isVisible()){
34818 var b = north.getBox();
34819 var m = north.getMargins();
34820 b.width = w - (m.left+m.right);
34823 centerY = b.height + b.y + m.bottom;
34824 centerH -= centerY;
34825 north.updateBox(this.safeBox(b));
34827 if(south && south.isVisible()){
34828 var b = south.getBox();
34829 var m = south.getMargins();
34830 b.width = w - (m.left+m.right);
34832 var totalHeight = (b.height + m.top + m.bottom);
34833 b.y = h - totalHeight + m.top;
34834 centerH -= totalHeight;
34835 south.updateBox(this.safeBox(b));
34837 if(west && west.isVisible()){
34838 var b = west.getBox();
34839 var m = west.getMargins();
34840 b.height = centerH - (m.top+m.bottom);
34842 b.y = centerY + m.top;
34843 var totalWidth = (b.width + m.left + m.right);
34844 centerX += totalWidth;
34845 centerW -= totalWidth;
34846 west.updateBox(this.safeBox(b));
34848 if(east && east.isVisible()){
34849 var b = east.getBox();
34850 var m = east.getMargins();
34851 b.height = centerH - (m.top+m.bottom);
34852 var totalWidth = (b.width + m.left + m.right);
34853 b.x = w - totalWidth + m.left;
34854 b.y = centerY + m.top;
34855 centerW -= totalWidth;
34856 east.updateBox(this.safeBox(b));
34859 var m = center.getMargins();
34861 x: centerX + m.left,
34862 y: centerY + m.top,
34863 width: centerW - (m.left+m.right),
34864 height: centerH - (m.top+m.bottom)
34866 //if(this.hideOnLayout){
34867 //center.el.setStyle("display", "block");
34869 center.updateBox(this.safeBox(centerBox));
34872 this.fireEvent("layout", this);
34876 safeBox : function(box){
34877 box.width = Math.max(0, box.width);
34878 box.height = Math.max(0, box.height);
34883 * Adds a ContentPanel (or subclass) to this layout.
34884 * @param {String} target The target region key (north, south, east, west or center).
34885 * @param {Roo.ContentPanel} panel The panel to add
34886 * @return {Roo.ContentPanel} The added panel
34888 add : function(target, panel){
34890 target = target.toLowerCase();
34891 return this.regions[target].add(panel);
34895 * Remove a ContentPanel (or subclass) to this layout.
34896 * @param {String} target The target region key (north, south, east, west or center).
34897 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34898 * @return {Roo.ContentPanel} The removed panel
34900 remove : function(target, panel){
34901 target = target.toLowerCase();
34902 return this.regions[target].remove(panel);
34906 * Searches all regions for a panel with the specified id
34907 * @param {String} panelId
34908 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34910 findPanel : function(panelId){
34911 var rs = this.regions;
34912 for(var target in rs){
34913 if(typeof rs[target] != "function"){
34914 var p = rs[target].getPanel(panelId);
34924 * Searches all regions for a panel with the specified id and activates (shows) it.
34925 * @param {String/ContentPanel} panelId The panels id or the panel itself
34926 * @return {Roo.ContentPanel} The shown panel or null
34928 showPanel : function(panelId) {
34929 var rs = this.regions;
34930 for(var target in rs){
34931 var r = rs[target];
34932 if(typeof r != "function"){
34933 if(r.hasPanel(panelId)){
34934 return r.showPanel(panelId);
34942 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34943 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34946 restoreState : function(provider){
34948 provider = Roo.state.Manager;
34950 var sm = new Roo.LayoutStateManager();
34951 sm.init(this, provider);
34957 * Adds a xtype elements to the layout.
34961 xtype : 'ContentPanel',
34968 xtype : 'NestedLayoutPanel',
34974 items : [ ... list of content panels or nested layout panels.. ]
34978 * @param {Object} cfg Xtype definition of item to add.
34980 addxtype : function(cfg)
34982 // basically accepts a pannel...
34983 // can accept a layout region..!?!?
34984 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34987 // theory? children can only be panels??
34989 //if (!cfg.xtype.match(/Panel$/)) {
34994 if (typeof(cfg.region) == 'undefined') {
34995 Roo.log("Failed to add Panel, region was not set");
34999 var region = cfg.region;
35005 xitems = cfg.items;
35012 case 'Content': // ContentPanel (el, cfg)
35013 case 'Scroll': // ContentPanel (el, cfg)
35015 cfg.autoCreate = true;
35016 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35018 // var el = this.el.createChild();
35019 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35022 this.add(region, ret);
35026 case 'TreePanel': // our new panel!
35027 cfg.el = this.el.createChild();
35028 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35029 this.add(region, ret);
35034 // create a new Layout (which is a Border Layout...
35036 var clayout = cfg.layout;
35037 clayout.el = this.el.createChild();
35038 clayout.items = clayout.items || [];
35042 // replace this exitems with the clayout ones..
35043 xitems = clayout.items;
35045 // force background off if it's in center...
35046 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35047 cfg.background = false;
35049 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35052 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35053 //console.log('adding nested layout panel ' + cfg.toSource());
35054 this.add(region, ret);
35055 nb = {}; /// find first...
35060 // needs grid and region
35062 //var el = this.getRegion(region).el.createChild();
35064 *var el = this.el.createChild();
35065 // create the grid first...
35066 cfg.grid.container = el;
35067 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35070 if (region == 'center' && this.active ) {
35071 cfg.background = false;
35074 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35076 this.add(region, ret);
35078 if (cfg.background) {
35079 // render grid on panel activation (if panel background)
35080 ret.on('activate', function(gp) {
35081 if (!gp.grid.rendered) {
35082 // gp.grid.render(el);
35086 // cfg.grid.render(el);
35092 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35093 // it was the old xcomponent building that caused this before.
35094 // espeically if border is the top element in the tree.
35104 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35106 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35107 this.add(region, ret);
35111 throw "Can not add '" + cfg.xtype + "' to Border";
35117 this.beginUpdate();
35121 Roo.each(xitems, function(i) {
35122 region = nb && i.region ? i.region : false;
35124 var add = ret.addxtype(i);
35127 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35128 if (!i.background) {
35129 abn[region] = nb[region] ;
35136 // make the last non-background panel active..
35137 //if (nb) { Roo.log(abn); }
35140 for(var r in abn) {
35141 region = this.getRegion(r);
35143 // tried using nb[r], but it does not work..
35145 region.showPanel(abn[r]);
35156 factory : function(cfg)
35159 var validRegions = Roo.bootstrap.layout.Border.regions;
35161 var target = cfg.region;
35164 var r = Roo.bootstrap.layout;
35168 return new r.North(cfg);
35170 return new r.South(cfg);
35172 return new r.East(cfg);
35174 return new r.West(cfg);
35176 return new r.Center(cfg);
35178 throw 'Layout region "'+target+'" not supported.';
35185 * Ext JS Library 1.1.1
35186 * Copyright(c) 2006-2007, Ext JS, LLC.
35188 * Originally Released Under LGPL - original licence link has changed is not relivant.
35191 * <script type="text/javascript">
35195 * @class Roo.bootstrap.layout.Basic
35196 * @extends Roo.util.Observable
35197 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35198 * and does not have a titlebar, tabs or any other features. All it does is size and position
35199 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35200 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35201 * @cfg {string} region the region that it inhabits..
35202 * @cfg {bool} skipConfig skip config?
35206 Roo.bootstrap.layout.Basic = function(config){
35208 this.mgr = config.mgr;
35210 this.position = config.region;
35212 var skipConfig = config.skipConfig;
35216 * @scope Roo.BasicLayoutRegion
35220 * @event beforeremove
35221 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35222 * @param {Roo.LayoutRegion} this
35223 * @param {Roo.ContentPanel} panel The panel
35224 * @param {Object} e The cancel event object
35226 "beforeremove" : true,
35228 * @event invalidated
35229 * Fires when the layout for this region is changed.
35230 * @param {Roo.LayoutRegion} this
35232 "invalidated" : true,
35234 * @event visibilitychange
35235 * Fires when this region is shown or hidden
35236 * @param {Roo.LayoutRegion} this
35237 * @param {Boolean} visibility true or false
35239 "visibilitychange" : true,
35241 * @event paneladded
35242 * Fires when a panel is added.
35243 * @param {Roo.LayoutRegion} this
35244 * @param {Roo.ContentPanel} panel The panel
35246 "paneladded" : true,
35248 * @event panelremoved
35249 * Fires when a panel is removed.
35250 * @param {Roo.LayoutRegion} this
35251 * @param {Roo.ContentPanel} panel The panel
35253 "panelremoved" : true,
35255 * @event beforecollapse
35256 * Fires when this region before collapse.
35257 * @param {Roo.LayoutRegion} this
35259 "beforecollapse" : true,
35262 * Fires when this region is collapsed.
35263 * @param {Roo.LayoutRegion} this
35265 "collapsed" : true,
35268 * Fires when this region is expanded.
35269 * @param {Roo.LayoutRegion} this
35274 * Fires when this region is slid into view.
35275 * @param {Roo.LayoutRegion} this
35277 "slideshow" : true,
35280 * Fires when this region slides out of view.
35281 * @param {Roo.LayoutRegion} this
35283 "slidehide" : true,
35285 * @event panelactivated
35286 * Fires when a panel is activated.
35287 * @param {Roo.LayoutRegion} this
35288 * @param {Roo.ContentPanel} panel The activated panel
35290 "panelactivated" : true,
35293 * Fires when the user resizes this region.
35294 * @param {Roo.LayoutRegion} this
35295 * @param {Number} newSize The new size (width for east/west, height for north/south)
35299 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35300 this.panels = new Roo.util.MixedCollection();
35301 this.panels.getKey = this.getPanelId.createDelegate(this);
35303 this.activePanel = null;
35304 // ensure listeners are added...
35306 if (config.listeners || config.events) {
35307 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35308 listeners : config.listeners || {},
35309 events : config.events || {}
35313 if(skipConfig !== true){
35314 this.applyConfig(config);
35318 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35320 getPanelId : function(p){
35324 applyConfig : function(config){
35325 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35326 this.config = config;
35331 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35332 * the width, for horizontal (north, south) the height.
35333 * @param {Number} newSize The new width or height
35335 resizeTo : function(newSize){
35336 var el = this.el ? this.el :
35337 (this.activePanel ? this.activePanel.getEl() : null);
35339 switch(this.position){
35342 el.setWidth(newSize);
35343 this.fireEvent("resized", this, newSize);
35347 el.setHeight(newSize);
35348 this.fireEvent("resized", this, newSize);
35354 getBox : function(){
35355 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35358 getMargins : function(){
35359 return this.margins;
35362 updateBox : function(box){
35364 var el = this.activePanel.getEl();
35365 el.dom.style.left = box.x + "px";
35366 el.dom.style.top = box.y + "px";
35367 this.activePanel.setSize(box.width, box.height);
35371 * Returns the container element for this region.
35372 * @return {Roo.Element}
35374 getEl : function(){
35375 return this.activePanel;
35379 * Returns true if this region is currently visible.
35380 * @return {Boolean}
35382 isVisible : function(){
35383 return this.activePanel ? true : false;
35386 setActivePanel : function(panel){
35387 panel = this.getPanel(panel);
35388 if(this.activePanel && this.activePanel != panel){
35389 this.activePanel.setActiveState(false);
35390 this.activePanel.getEl().setLeftTop(-10000,-10000);
35392 this.activePanel = panel;
35393 panel.setActiveState(true);
35395 panel.setSize(this.box.width, this.box.height);
35397 this.fireEvent("panelactivated", this, panel);
35398 this.fireEvent("invalidated");
35402 * Show the specified panel.
35403 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35404 * @return {Roo.ContentPanel} The shown panel or null
35406 showPanel : function(panel){
35407 panel = this.getPanel(panel);
35409 this.setActivePanel(panel);
35415 * Get the active panel for this region.
35416 * @return {Roo.ContentPanel} The active panel or null
35418 getActivePanel : function(){
35419 return this.activePanel;
35423 * Add the passed ContentPanel(s)
35424 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35425 * @return {Roo.ContentPanel} The panel added (if only one was added)
35427 add : function(panel){
35428 if(arguments.length > 1){
35429 for(var i = 0, len = arguments.length; i < len; i++) {
35430 this.add(arguments[i]);
35434 if(this.hasPanel(panel)){
35435 this.showPanel(panel);
35438 var el = panel.getEl();
35439 if(el.dom.parentNode != this.mgr.el.dom){
35440 this.mgr.el.dom.appendChild(el.dom);
35442 if(panel.setRegion){
35443 panel.setRegion(this);
35445 this.panels.add(panel);
35446 el.setStyle("position", "absolute");
35447 if(!panel.background){
35448 this.setActivePanel(panel);
35449 if(this.config.initialSize && this.panels.getCount()==1){
35450 this.resizeTo(this.config.initialSize);
35453 this.fireEvent("paneladded", this, panel);
35458 * Returns true if the panel is in this region.
35459 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35460 * @return {Boolean}
35462 hasPanel : function(panel){
35463 if(typeof panel == "object"){ // must be panel obj
35464 panel = panel.getId();
35466 return this.getPanel(panel) ? true : false;
35470 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35471 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35472 * @param {Boolean} preservePanel Overrides the config preservePanel option
35473 * @return {Roo.ContentPanel} The panel that was removed
35475 remove : function(panel, preservePanel){
35476 panel = this.getPanel(panel);
35481 this.fireEvent("beforeremove", this, panel, e);
35482 if(e.cancel === true){
35485 var panelId = panel.getId();
35486 this.panels.removeKey(panelId);
35491 * Returns the panel specified or null if it's not in this region.
35492 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35493 * @return {Roo.ContentPanel}
35495 getPanel : function(id){
35496 if(typeof id == "object"){ // must be panel obj
35499 return this.panels.get(id);
35503 * Returns this regions position (north/south/east/west/center).
35506 getPosition: function(){
35507 return this.position;
35511 * Ext JS Library 1.1.1
35512 * Copyright(c) 2006-2007, Ext JS, LLC.
35514 * Originally Released Under LGPL - original licence link has changed is not relivant.
35517 * <script type="text/javascript">
35521 * @class Roo.bootstrap.layout.Region
35522 * @extends Roo.bootstrap.layout.Basic
35523 * This class represents a region in a layout manager.
35525 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35526 * @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})
35527 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35528 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35529 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35530 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35531 * @cfg {String} title The title for the region (overrides panel titles)
35532 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35533 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35534 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35535 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35536 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35537 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35538 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35539 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35540 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35541 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35543 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35544 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35545 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35546 * @cfg {Number} width For East/West panels
35547 * @cfg {Number} height For North/South panels
35548 * @cfg {Boolean} split To show the splitter
35549 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35551 * @cfg {string} cls Extra CSS classes to add to region
35553 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35554 * @cfg {string} region the region that it inhabits..
35557 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35558 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35560 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35561 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35562 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35564 Roo.bootstrap.layout.Region = function(config)
35566 this.applyConfig(config);
35568 var mgr = config.mgr;
35569 var pos = config.region;
35570 config.skipConfig = true;
35571 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35574 this.onRender(mgr.el);
35577 this.visible = true;
35578 this.collapsed = false;
35579 this.unrendered_panels = [];
35582 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35584 position: '', // set by wrapper (eg. north/south etc..)
35585 unrendered_panels : null, // unrendered panels.
35586 createBody : function(){
35587 /** This region's body element
35588 * @type Roo.Element */
35589 this.bodyEl = this.el.createChild({
35591 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35595 onRender: function(ctr, pos)
35597 var dh = Roo.DomHelper;
35598 /** This region's container element
35599 * @type Roo.Element */
35600 this.el = dh.append(ctr.dom, {
35602 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35604 /** This region's title element
35605 * @type Roo.Element */
35607 this.titleEl = dh.append(this.el.dom,
35610 unselectable: "on",
35611 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35613 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35614 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35617 this.titleEl.enableDisplayMode();
35618 /** This region's title text element
35619 * @type HTMLElement */
35620 this.titleTextEl = this.titleEl.dom.firstChild;
35621 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35623 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35624 this.closeBtn.enableDisplayMode();
35625 this.closeBtn.on("click", this.closeClicked, this);
35626 this.closeBtn.hide();
35628 this.createBody(this.config);
35629 if(this.config.hideWhenEmpty){
35631 this.on("paneladded", this.validateVisibility, this);
35632 this.on("panelremoved", this.validateVisibility, this);
35634 if(this.autoScroll){
35635 this.bodyEl.setStyle("overflow", "auto");
35637 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35639 //if(c.titlebar !== false){
35640 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35641 this.titleEl.hide();
35643 this.titleEl.show();
35644 if(this.config.title){
35645 this.titleTextEl.innerHTML = this.config.title;
35649 if(this.config.collapsed){
35650 this.collapse(true);
35652 if(this.config.hidden){
35656 if (this.unrendered_panels && this.unrendered_panels.length) {
35657 for (var i =0;i< this.unrendered_panels.length; i++) {
35658 this.add(this.unrendered_panels[i]);
35660 this.unrendered_panels = null;
35666 applyConfig : function(c)
35669 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35670 var dh = Roo.DomHelper;
35671 if(c.titlebar !== false){
35672 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35673 this.collapseBtn.on("click", this.collapse, this);
35674 this.collapseBtn.enableDisplayMode();
35676 if(c.showPin === true || this.showPin){
35677 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35678 this.stickBtn.enableDisplayMode();
35679 this.stickBtn.on("click", this.expand, this);
35680 this.stickBtn.hide();
35685 /** This region's collapsed element
35686 * @type Roo.Element */
35689 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35690 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35693 if(c.floatable !== false){
35694 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35695 this.collapsedEl.on("click", this.collapseClick, this);
35698 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35699 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35700 id: "message", unselectable: "on", style:{"float":"left"}});
35701 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35703 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35704 this.expandBtn.on("click", this.expand, this);
35708 if(this.collapseBtn){
35709 this.collapseBtn.setVisible(c.collapsible == true);
35712 this.cmargins = c.cmargins || this.cmargins ||
35713 (this.position == "west" || this.position == "east" ?
35714 {top: 0, left: 2, right:2, bottom: 0} :
35715 {top: 2, left: 0, right:0, bottom: 2});
35717 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35720 this.bottomTabs = c.tabPosition != "top";
35722 this.autoScroll = c.autoScroll || false;
35727 this.duration = c.duration || .30;
35728 this.slideDuration = c.slideDuration || .45;
35733 * Returns true if this region is currently visible.
35734 * @return {Boolean}
35736 isVisible : function(){
35737 return this.visible;
35741 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35742 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35744 //setCollapsedTitle : function(title){
35745 // title = title || " ";
35746 // if(this.collapsedTitleTextEl){
35747 // this.collapsedTitleTextEl.innerHTML = title;
35751 getBox : function(){
35753 // if(!this.collapsed){
35754 b = this.el.getBox(false, true);
35756 // b = this.collapsedEl.getBox(false, true);
35761 getMargins : function(){
35762 return this.margins;
35763 //return this.collapsed ? this.cmargins : this.margins;
35766 highlight : function(){
35767 this.el.addClass("x-layout-panel-dragover");
35770 unhighlight : function(){
35771 this.el.removeClass("x-layout-panel-dragover");
35774 updateBox : function(box)
35776 if (!this.bodyEl) {
35777 return; // not rendered yet..
35781 if(!this.collapsed){
35782 this.el.dom.style.left = box.x + "px";
35783 this.el.dom.style.top = box.y + "px";
35784 this.updateBody(box.width, box.height);
35786 this.collapsedEl.dom.style.left = box.x + "px";
35787 this.collapsedEl.dom.style.top = box.y + "px";
35788 this.collapsedEl.setSize(box.width, box.height);
35791 this.tabs.autoSizeTabs();
35795 updateBody : function(w, h)
35798 this.el.setWidth(w);
35799 w -= this.el.getBorderWidth("rl");
35800 if(this.config.adjustments){
35801 w += this.config.adjustments[0];
35804 if(h !== null && h > 0){
35805 this.el.setHeight(h);
35806 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35807 h -= this.el.getBorderWidth("tb");
35808 if(this.config.adjustments){
35809 h += this.config.adjustments[1];
35811 this.bodyEl.setHeight(h);
35813 h = this.tabs.syncHeight(h);
35816 if(this.panelSize){
35817 w = w !== null ? w : this.panelSize.width;
35818 h = h !== null ? h : this.panelSize.height;
35820 if(this.activePanel){
35821 var el = this.activePanel.getEl();
35822 w = w !== null ? w : el.getWidth();
35823 h = h !== null ? h : el.getHeight();
35824 this.panelSize = {width: w, height: h};
35825 this.activePanel.setSize(w, h);
35827 if(Roo.isIE && this.tabs){
35828 this.tabs.el.repaint();
35833 * Returns the container element for this region.
35834 * @return {Roo.Element}
35836 getEl : function(){
35841 * Hides this region.
35844 //if(!this.collapsed){
35845 this.el.dom.style.left = "-2000px";
35848 // this.collapsedEl.dom.style.left = "-2000px";
35849 // this.collapsedEl.hide();
35851 this.visible = false;
35852 this.fireEvent("visibilitychange", this, false);
35856 * Shows this region if it was previously hidden.
35859 //if(!this.collapsed){
35862 // this.collapsedEl.show();
35864 this.visible = true;
35865 this.fireEvent("visibilitychange", this, true);
35868 closeClicked : function(){
35869 if(this.activePanel){
35870 this.remove(this.activePanel);
35874 collapseClick : function(e){
35876 e.stopPropagation();
35879 e.stopPropagation();
35885 * Collapses this region.
35886 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35889 collapse : function(skipAnim, skipCheck = false){
35890 if(this.collapsed) {
35894 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35896 this.collapsed = true;
35898 this.split.el.hide();
35900 if(this.config.animate && skipAnim !== true){
35901 this.fireEvent("invalidated", this);
35902 this.animateCollapse();
35904 this.el.setLocation(-20000,-20000);
35906 this.collapsedEl.show();
35907 this.fireEvent("collapsed", this);
35908 this.fireEvent("invalidated", this);
35914 animateCollapse : function(){
35919 * Expands this region if it was previously collapsed.
35920 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35921 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35924 expand : function(e, skipAnim){
35926 e.stopPropagation();
35928 if(!this.collapsed || this.el.hasActiveFx()) {
35932 this.afterSlideIn();
35935 this.collapsed = false;
35936 if(this.config.animate && skipAnim !== true){
35937 this.animateExpand();
35941 this.split.el.show();
35943 this.collapsedEl.setLocation(-2000,-2000);
35944 this.collapsedEl.hide();
35945 this.fireEvent("invalidated", this);
35946 this.fireEvent("expanded", this);
35950 animateExpand : function(){
35954 initTabs : function()
35956 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35958 var ts = new Roo.bootstrap.panel.Tabs({
35959 el: this.bodyEl.dom,
35960 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35961 disableTooltips: this.config.disableTabTips,
35962 toolbar : this.config.toolbar
35965 if(this.config.hideTabs){
35966 ts.stripWrap.setDisplayed(false);
35969 ts.resizeTabs = this.config.resizeTabs === true;
35970 ts.minTabWidth = this.config.minTabWidth || 40;
35971 ts.maxTabWidth = this.config.maxTabWidth || 250;
35972 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35973 ts.monitorResize = false;
35974 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35975 ts.bodyEl.addClass('roo-layout-tabs-body');
35976 this.panels.each(this.initPanelAsTab, this);
35979 initPanelAsTab : function(panel){
35980 var ti = this.tabs.addTab(
35984 this.config.closeOnTab && panel.isClosable(),
35987 if(panel.tabTip !== undefined){
35988 ti.setTooltip(panel.tabTip);
35990 ti.on("activate", function(){
35991 this.setActivePanel(panel);
35994 if(this.config.closeOnTab){
35995 ti.on("beforeclose", function(t, e){
35997 this.remove(panel);
36001 panel.tabItem = ti;
36006 updatePanelTitle : function(panel, title)
36008 if(this.activePanel == panel){
36009 this.updateTitle(title);
36012 var ti = this.tabs.getTab(panel.getEl().id);
36014 if(panel.tabTip !== undefined){
36015 ti.setTooltip(panel.tabTip);
36020 updateTitle : function(title){
36021 if(this.titleTextEl && !this.config.title){
36022 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36026 setActivePanel : function(panel)
36028 panel = this.getPanel(panel);
36029 if(this.activePanel && this.activePanel != panel){
36030 if(this.activePanel.setActiveState(false) === false){
36034 this.activePanel = panel;
36035 panel.setActiveState(true);
36036 if(this.panelSize){
36037 panel.setSize(this.panelSize.width, this.panelSize.height);
36040 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36042 this.updateTitle(panel.getTitle());
36044 this.fireEvent("invalidated", this);
36046 this.fireEvent("panelactivated", this, panel);
36050 * Shows the specified panel.
36051 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36052 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36054 showPanel : function(panel)
36056 panel = this.getPanel(panel);
36059 var tab = this.tabs.getTab(panel.getEl().id);
36060 if(tab.isHidden()){
36061 this.tabs.unhideTab(tab.id);
36065 this.setActivePanel(panel);
36072 * Get the active panel for this region.
36073 * @return {Roo.ContentPanel} The active panel or null
36075 getActivePanel : function(){
36076 return this.activePanel;
36079 validateVisibility : function(){
36080 if(this.panels.getCount() < 1){
36081 this.updateTitle(" ");
36082 this.closeBtn.hide();
36085 if(!this.isVisible()){
36092 * Adds the passed ContentPanel(s) to this region.
36093 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36094 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36096 add : function(panel)
36098 if(arguments.length > 1){
36099 for(var i = 0, len = arguments.length; i < len; i++) {
36100 this.add(arguments[i]);
36105 // if we have not been rendered yet, then we can not really do much of this..
36106 if (!this.bodyEl) {
36107 this.unrendered_panels.push(panel);
36114 if(this.hasPanel(panel)){
36115 this.showPanel(panel);
36118 panel.setRegion(this);
36119 this.panels.add(panel);
36120 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36121 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36122 // and hide them... ???
36123 this.bodyEl.dom.appendChild(panel.getEl().dom);
36124 if(panel.background !== true){
36125 this.setActivePanel(panel);
36127 this.fireEvent("paneladded", this, panel);
36134 this.initPanelAsTab(panel);
36138 if(panel.background !== true){
36139 this.tabs.activate(panel.getEl().id);
36141 this.fireEvent("paneladded", this, panel);
36146 * Hides the tab for the specified panel.
36147 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36149 hidePanel : function(panel){
36150 if(this.tabs && (panel = this.getPanel(panel))){
36151 this.tabs.hideTab(panel.getEl().id);
36156 * Unhides the tab for a previously hidden panel.
36157 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36159 unhidePanel : function(panel){
36160 if(this.tabs && (panel = this.getPanel(panel))){
36161 this.tabs.unhideTab(panel.getEl().id);
36165 clearPanels : function(){
36166 while(this.panels.getCount() > 0){
36167 this.remove(this.panels.first());
36172 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36173 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36174 * @param {Boolean} preservePanel Overrides the config preservePanel option
36175 * @return {Roo.ContentPanel} The panel that was removed
36177 remove : function(panel, preservePanel)
36179 panel = this.getPanel(panel);
36184 this.fireEvent("beforeremove", this, panel, e);
36185 if(e.cancel === true){
36188 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36189 var panelId = panel.getId();
36190 this.panels.removeKey(panelId);
36192 document.body.appendChild(panel.getEl().dom);
36195 this.tabs.removeTab(panel.getEl().id);
36196 }else if (!preservePanel){
36197 this.bodyEl.dom.removeChild(panel.getEl().dom);
36199 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36200 var p = this.panels.first();
36201 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36202 tempEl.appendChild(p.getEl().dom);
36203 this.bodyEl.update("");
36204 this.bodyEl.dom.appendChild(p.getEl().dom);
36206 this.updateTitle(p.getTitle());
36208 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36209 this.setActivePanel(p);
36211 panel.setRegion(null);
36212 if(this.activePanel == panel){
36213 this.activePanel = null;
36215 if(this.config.autoDestroy !== false && preservePanel !== true){
36216 try{panel.destroy();}catch(e){}
36218 this.fireEvent("panelremoved", this, panel);
36223 * Returns the TabPanel component used by this region
36224 * @return {Roo.TabPanel}
36226 getTabs : function(){
36230 createTool : function(parentEl, className){
36231 var btn = Roo.DomHelper.append(parentEl, {
36233 cls: "x-layout-tools-button",
36236 cls: "roo-layout-tools-button-inner " + className,
36240 btn.addClassOnOver("roo-layout-tools-button-over");
36245 * Ext JS Library 1.1.1
36246 * Copyright(c) 2006-2007, Ext JS, LLC.
36248 * Originally Released Under LGPL - original licence link has changed is not relivant.
36251 * <script type="text/javascript">
36257 * @class Roo.SplitLayoutRegion
36258 * @extends Roo.LayoutRegion
36259 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36261 Roo.bootstrap.layout.Split = function(config){
36262 this.cursor = config.cursor;
36263 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36266 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36268 splitTip : "Drag to resize.",
36269 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36270 useSplitTips : false,
36272 applyConfig : function(config){
36273 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36276 onRender : function(ctr,pos) {
36278 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36279 if(!this.config.split){
36284 var splitEl = Roo.DomHelper.append(ctr.dom, {
36286 id: this.el.id + "-split",
36287 cls: "roo-layout-split roo-layout-split-"+this.position,
36290 /** The SplitBar for this region
36291 * @type Roo.SplitBar */
36292 // does not exist yet...
36293 Roo.log([this.position, this.orientation]);
36295 this.split = new Roo.bootstrap.SplitBar({
36296 dragElement : splitEl,
36297 resizingElement: this.el,
36298 orientation : this.orientation
36301 this.split.on("moved", this.onSplitMove, this);
36302 this.split.useShim = this.config.useShim === true;
36303 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36304 if(this.useSplitTips){
36305 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36307 //if(config.collapsible){
36308 // this.split.el.on("dblclick", this.collapse, this);
36311 if(typeof this.config.minSize != "undefined"){
36312 this.split.minSize = this.config.minSize;
36314 if(typeof this.config.maxSize != "undefined"){
36315 this.split.maxSize = this.config.maxSize;
36317 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36318 this.hideSplitter();
36323 getHMaxSize : function(){
36324 var cmax = this.config.maxSize || 10000;
36325 var center = this.mgr.getRegion("center");
36326 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36329 getVMaxSize : function(){
36330 var cmax = this.config.maxSize || 10000;
36331 var center = this.mgr.getRegion("center");
36332 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36335 onSplitMove : function(split, newSize){
36336 this.fireEvent("resized", this, newSize);
36340 * Returns the {@link Roo.SplitBar} for this region.
36341 * @return {Roo.SplitBar}
36343 getSplitBar : function(){
36348 this.hideSplitter();
36349 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36352 hideSplitter : function(){
36354 this.split.el.setLocation(-2000,-2000);
36355 this.split.el.hide();
36361 this.split.el.show();
36363 Roo.bootstrap.layout.Split.superclass.show.call(this);
36366 beforeSlide: function(){
36367 if(Roo.isGecko){// firefox overflow auto bug workaround
36368 this.bodyEl.clip();
36370 this.tabs.bodyEl.clip();
36372 if(this.activePanel){
36373 this.activePanel.getEl().clip();
36375 if(this.activePanel.beforeSlide){
36376 this.activePanel.beforeSlide();
36382 afterSlide : function(){
36383 if(Roo.isGecko){// firefox overflow auto bug workaround
36384 this.bodyEl.unclip();
36386 this.tabs.bodyEl.unclip();
36388 if(this.activePanel){
36389 this.activePanel.getEl().unclip();
36390 if(this.activePanel.afterSlide){
36391 this.activePanel.afterSlide();
36397 initAutoHide : function(){
36398 if(this.autoHide !== false){
36399 if(!this.autoHideHd){
36400 var st = new Roo.util.DelayedTask(this.slideIn, this);
36401 this.autoHideHd = {
36402 "mouseout": function(e){
36403 if(!e.within(this.el, true)){
36407 "mouseover" : function(e){
36413 this.el.on(this.autoHideHd);
36417 clearAutoHide : function(){
36418 if(this.autoHide !== false){
36419 this.el.un("mouseout", this.autoHideHd.mouseout);
36420 this.el.un("mouseover", this.autoHideHd.mouseover);
36424 clearMonitor : function(){
36425 Roo.get(document).un("click", this.slideInIf, this);
36428 // these names are backwards but not changed for compat
36429 slideOut : function(){
36430 if(this.isSlid || this.el.hasActiveFx()){
36433 this.isSlid = true;
36434 if(this.collapseBtn){
36435 this.collapseBtn.hide();
36437 this.closeBtnState = this.closeBtn.getStyle('display');
36438 this.closeBtn.hide();
36440 this.stickBtn.show();
36443 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36444 this.beforeSlide();
36445 this.el.setStyle("z-index", 10001);
36446 this.el.slideIn(this.getSlideAnchor(), {
36447 callback: function(){
36449 this.initAutoHide();
36450 Roo.get(document).on("click", this.slideInIf, this);
36451 this.fireEvent("slideshow", this);
36458 afterSlideIn : function(){
36459 this.clearAutoHide();
36460 this.isSlid = false;
36461 this.clearMonitor();
36462 this.el.setStyle("z-index", "");
36463 if(this.collapseBtn){
36464 this.collapseBtn.show();
36466 this.closeBtn.setStyle('display', this.closeBtnState);
36468 this.stickBtn.hide();
36470 this.fireEvent("slidehide", this);
36473 slideIn : function(cb){
36474 if(!this.isSlid || this.el.hasActiveFx()){
36478 this.isSlid = false;
36479 this.beforeSlide();
36480 this.el.slideOut(this.getSlideAnchor(), {
36481 callback: function(){
36482 this.el.setLeftTop(-10000, -10000);
36484 this.afterSlideIn();
36492 slideInIf : function(e){
36493 if(!e.within(this.el)){
36498 animateCollapse : function(){
36499 this.beforeSlide();
36500 this.el.setStyle("z-index", 20000);
36501 var anchor = this.getSlideAnchor();
36502 this.el.slideOut(anchor, {
36503 callback : function(){
36504 this.el.setStyle("z-index", "");
36505 this.collapsedEl.slideIn(anchor, {duration:.3});
36507 this.el.setLocation(-10000,-10000);
36509 this.fireEvent("collapsed", this);
36516 animateExpand : function(){
36517 this.beforeSlide();
36518 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36519 this.el.setStyle("z-index", 20000);
36520 this.collapsedEl.hide({
36523 this.el.slideIn(this.getSlideAnchor(), {
36524 callback : function(){
36525 this.el.setStyle("z-index", "");
36528 this.split.el.show();
36530 this.fireEvent("invalidated", this);
36531 this.fireEvent("expanded", this);
36559 getAnchor : function(){
36560 return this.anchors[this.position];
36563 getCollapseAnchor : function(){
36564 return this.canchors[this.position];
36567 getSlideAnchor : function(){
36568 return this.sanchors[this.position];
36571 getAlignAdj : function(){
36572 var cm = this.cmargins;
36573 switch(this.position){
36589 getExpandAdj : function(){
36590 var c = this.collapsedEl, cm = this.cmargins;
36591 switch(this.position){
36593 return [-(cm.right+c.getWidth()+cm.left), 0];
36596 return [cm.right+c.getWidth()+cm.left, 0];
36599 return [0, -(cm.top+cm.bottom+c.getHeight())];
36602 return [0, cm.top+cm.bottom+c.getHeight()];
36608 * Ext JS Library 1.1.1
36609 * Copyright(c) 2006-2007, Ext JS, LLC.
36611 * Originally Released Under LGPL - original licence link has changed is not relivant.
36614 * <script type="text/javascript">
36617 * These classes are private internal classes
36619 Roo.bootstrap.layout.Center = function(config){
36620 config.region = "center";
36621 Roo.bootstrap.layout.Region.call(this, config);
36622 this.visible = true;
36623 this.minWidth = config.minWidth || 20;
36624 this.minHeight = config.minHeight || 20;
36627 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36629 // center panel can't be hidden
36633 // center panel can't be hidden
36636 getMinWidth: function(){
36637 return this.minWidth;
36640 getMinHeight: function(){
36641 return this.minHeight;
36654 Roo.bootstrap.layout.North = function(config)
36656 config.region = 'north';
36657 config.cursor = 'n-resize';
36659 Roo.bootstrap.layout.Split.call(this, config);
36663 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36664 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36665 this.split.el.addClass("roo-layout-split-v");
36667 var size = config.initialSize || config.height;
36668 if(typeof size != "undefined"){
36669 this.el.setHeight(size);
36672 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36674 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36678 getBox : function(){
36679 if(this.collapsed){
36680 return this.collapsedEl.getBox();
36682 var box = this.el.getBox();
36684 box.height += this.split.el.getHeight();
36689 updateBox : function(box){
36690 if(this.split && !this.collapsed){
36691 box.height -= this.split.el.getHeight();
36692 this.split.el.setLeft(box.x);
36693 this.split.el.setTop(box.y+box.height);
36694 this.split.el.setWidth(box.width);
36696 if(this.collapsed){
36697 this.updateBody(box.width, null);
36699 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36707 Roo.bootstrap.layout.South = function(config){
36708 config.region = 'south';
36709 config.cursor = 's-resize';
36710 Roo.bootstrap.layout.Split.call(this, config);
36712 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36713 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36714 this.split.el.addClass("roo-layout-split-v");
36716 var size = config.initialSize || config.height;
36717 if(typeof size != "undefined"){
36718 this.el.setHeight(size);
36722 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36723 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36724 getBox : function(){
36725 if(this.collapsed){
36726 return this.collapsedEl.getBox();
36728 var box = this.el.getBox();
36730 var sh = this.split.el.getHeight();
36737 updateBox : function(box){
36738 if(this.split && !this.collapsed){
36739 var sh = this.split.el.getHeight();
36742 this.split.el.setLeft(box.x);
36743 this.split.el.setTop(box.y-sh);
36744 this.split.el.setWidth(box.width);
36746 if(this.collapsed){
36747 this.updateBody(box.width, null);
36749 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36753 Roo.bootstrap.layout.East = function(config){
36754 config.region = "east";
36755 config.cursor = "e-resize";
36756 Roo.bootstrap.layout.Split.call(this, config);
36758 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36759 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36760 this.split.el.addClass("roo-layout-split-h");
36762 var size = config.initialSize || config.width;
36763 if(typeof size != "undefined"){
36764 this.el.setWidth(size);
36767 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36768 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36769 getBox : function(){
36770 if(this.collapsed){
36771 return this.collapsedEl.getBox();
36773 var box = this.el.getBox();
36775 var sw = this.split.el.getWidth();
36782 updateBox : function(box){
36783 if(this.split && !this.collapsed){
36784 var sw = this.split.el.getWidth();
36786 this.split.el.setLeft(box.x);
36787 this.split.el.setTop(box.y);
36788 this.split.el.setHeight(box.height);
36791 if(this.collapsed){
36792 this.updateBody(null, box.height);
36794 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36798 Roo.bootstrap.layout.West = function(config){
36799 config.region = "west";
36800 config.cursor = "w-resize";
36802 Roo.bootstrap.layout.Split.call(this, config);
36804 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36805 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36806 this.split.el.addClass("roo-layout-split-h");
36810 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36811 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36813 onRender: function(ctr, pos)
36815 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36816 var size = this.config.initialSize || this.config.width;
36817 if(typeof size != "undefined"){
36818 this.el.setWidth(size);
36822 getBox : function(){
36823 if(this.collapsed){
36824 return this.collapsedEl.getBox();
36826 var box = this.el.getBox();
36828 box.width += this.split.el.getWidth();
36833 updateBox : function(box){
36834 if(this.split && !this.collapsed){
36835 var sw = this.split.el.getWidth();
36837 this.split.el.setLeft(box.x+box.width);
36838 this.split.el.setTop(box.y);
36839 this.split.el.setHeight(box.height);
36841 if(this.collapsed){
36842 this.updateBody(null, box.height);
36844 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36847 Roo.namespace("Roo.bootstrap.panel");/*
36849 * Ext JS Library 1.1.1
36850 * Copyright(c) 2006-2007, Ext JS, LLC.
36852 * Originally Released Under LGPL - original licence link has changed is not relivant.
36855 * <script type="text/javascript">
36858 * @class Roo.ContentPanel
36859 * @extends Roo.util.Observable
36860 * A basic ContentPanel element.
36861 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36862 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36863 * @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
36864 * @cfg {Boolean} closable True if the panel can be closed/removed
36865 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36866 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36867 * @cfg {Toolbar} toolbar A toolbar for this panel
36868 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36869 * @cfg {String} title The title for this panel
36870 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36871 * @cfg {String} url Calls {@link #setUrl} with this value
36872 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36873 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36874 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36875 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36876 * @cfg {Boolean} badges render the badges
36879 * Create a new ContentPanel.
36880 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36881 * @param {String/Object} config A string to set only the title or a config object
36882 * @param {String} content (optional) Set the HTML content for this panel
36883 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36885 Roo.bootstrap.panel.Content = function( config){
36887 this.tpl = config.tpl || false;
36889 var el = config.el;
36890 var content = config.content;
36892 if(config.autoCreate){ // xtype is available if this is called from factory
36895 this.el = Roo.get(el);
36896 if(!this.el && config && config.autoCreate){
36897 if(typeof config.autoCreate == "object"){
36898 if(!config.autoCreate.id){
36899 config.autoCreate.id = config.id||el;
36901 this.el = Roo.DomHelper.append(document.body,
36902 config.autoCreate, true);
36904 var elcfg = { tag: "div",
36905 cls: "roo-layout-inactive-content",
36909 elcfg.html = config.html;
36913 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36916 this.closable = false;
36917 this.loaded = false;
36918 this.active = false;
36921 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36923 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36925 this.wrapEl = this.el; //this.el.wrap();
36927 if (config.toolbar.items) {
36928 ti = config.toolbar.items ;
36929 delete config.toolbar.items ;
36933 this.toolbar.render(this.wrapEl, 'before');
36934 for(var i =0;i < ti.length;i++) {
36935 // Roo.log(['add child', items[i]]);
36936 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36938 this.toolbar.items = nitems;
36939 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36940 delete config.toolbar;
36944 // xtype created footer. - not sure if will work as we normally have to render first..
36945 if (this.footer && !this.footer.el && this.footer.xtype) {
36946 if (!this.wrapEl) {
36947 this.wrapEl = this.el.wrap();
36950 this.footer.container = this.wrapEl.createChild();
36952 this.footer = Roo.factory(this.footer, Roo);
36957 if(typeof config == "string"){
36958 this.title = config;
36960 Roo.apply(this, config);
36964 this.resizeEl = Roo.get(this.resizeEl, true);
36966 this.resizeEl = this.el;
36968 // handle view.xtype
36976 * Fires when this panel is activated.
36977 * @param {Roo.ContentPanel} this
36981 * @event deactivate
36982 * Fires when this panel is activated.
36983 * @param {Roo.ContentPanel} this
36985 "deactivate" : true,
36989 * Fires when this panel is resized if fitToFrame is true.
36990 * @param {Roo.ContentPanel} this
36991 * @param {Number} width The width after any component adjustments
36992 * @param {Number} height The height after any component adjustments
36998 * Fires when this tab is created
36999 * @param {Roo.ContentPanel} this
37010 if(this.autoScroll){
37011 this.resizeEl.setStyle("overflow", "auto");
37013 // fix randome scrolling
37014 //this.el.on('scroll', function() {
37015 // Roo.log('fix random scolling');
37016 // this.scrollTo('top',0);
37019 content = content || this.content;
37021 this.setContent(content);
37023 if(config && config.url){
37024 this.setUrl(this.url, this.params, this.loadOnce);
37029 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37031 if (this.view && typeof(this.view.xtype) != 'undefined') {
37032 this.view.el = this.el.appendChild(document.createElement("div"));
37033 this.view = Roo.factory(this.view);
37034 this.view.render && this.view.render(false, '');
37038 this.fireEvent('render', this);
37041 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37045 setRegion : function(region){
37046 this.region = region;
37047 this.setActiveClass(region && !this.background);
37051 setActiveClass: function(state)
37054 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37055 this.el.setStyle('position','relative');
37057 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37058 this.el.setStyle('position', 'absolute');
37063 * Returns the toolbar for this Panel if one was configured.
37064 * @return {Roo.Toolbar}
37066 getToolbar : function(){
37067 return this.toolbar;
37070 setActiveState : function(active)
37072 this.active = active;
37073 this.setActiveClass(active);
37075 if(this.fireEvent("deactivate", this) === false){
37080 this.fireEvent("activate", this);
37084 * Updates this panel's element
37085 * @param {String} content The new content
37086 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37088 setContent : function(content, loadScripts){
37089 this.el.update(content, loadScripts);
37092 ignoreResize : function(w, h){
37093 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37096 this.lastSize = {width: w, height: h};
37101 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37102 * @return {Roo.UpdateManager} The UpdateManager
37104 getUpdateManager : function(){
37105 return this.el.getUpdateManager();
37108 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37109 * @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:
37112 url: "your-url.php",
37113 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37114 callback: yourFunction,
37115 scope: yourObject, //(optional scope)
37118 text: "Loading...",
37123 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37124 * 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.
37125 * @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}
37126 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37127 * @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.
37128 * @return {Roo.ContentPanel} this
37131 var um = this.el.getUpdateManager();
37132 um.update.apply(um, arguments);
37138 * 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.
37139 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37140 * @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)
37141 * @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)
37142 * @return {Roo.UpdateManager} The UpdateManager
37144 setUrl : function(url, params, loadOnce){
37145 if(this.refreshDelegate){
37146 this.removeListener("activate", this.refreshDelegate);
37148 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37149 this.on("activate", this.refreshDelegate);
37150 return this.el.getUpdateManager();
37153 _handleRefresh : function(url, params, loadOnce){
37154 if(!loadOnce || !this.loaded){
37155 var updater = this.el.getUpdateManager();
37156 updater.update(url, params, this._setLoaded.createDelegate(this));
37160 _setLoaded : function(){
37161 this.loaded = true;
37165 * Returns this panel's id
37168 getId : function(){
37173 * Returns this panel's element - used by regiosn to add.
37174 * @return {Roo.Element}
37176 getEl : function(){
37177 return this.wrapEl || this.el;
37182 adjustForComponents : function(width, height)
37184 //Roo.log('adjustForComponents ');
37185 if(this.resizeEl != this.el){
37186 width -= this.el.getFrameWidth('lr');
37187 height -= this.el.getFrameWidth('tb');
37190 var te = this.toolbar.getEl();
37191 te.setWidth(width);
37192 height -= te.getHeight();
37195 var te = this.footer.getEl();
37196 te.setWidth(width);
37197 height -= te.getHeight();
37201 if(this.adjustments){
37202 width += this.adjustments[0];
37203 height += this.adjustments[1];
37205 return {"width": width, "height": height};
37208 setSize : function(width, height){
37209 if(this.fitToFrame && !this.ignoreResize(width, height)){
37210 if(this.fitContainer && this.resizeEl != this.el){
37211 this.el.setSize(width, height);
37213 var size = this.adjustForComponents(width, height);
37214 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37215 this.fireEvent('resize', this, size.width, size.height);
37220 * Returns this panel's title
37223 getTitle : function(){
37225 if (typeof(this.title) != 'object') {
37230 for (var k in this.title) {
37231 if (!this.title.hasOwnProperty(k)) {
37235 if (k.indexOf('-') >= 0) {
37236 var s = k.split('-');
37237 for (var i = 0; i<s.length; i++) {
37238 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37241 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37248 * Set this panel's title
37249 * @param {String} title
37251 setTitle : function(title){
37252 this.title = title;
37254 this.region.updatePanelTitle(this, title);
37259 * Returns true is this panel was configured to be closable
37260 * @return {Boolean}
37262 isClosable : function(){
37263 return this.closable;
37266 beforeSlide : function(){
37268 this.resizeEl.clip();
37271 afterSlide : function(){
37273 this.resizeEl.unclip();
37277 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37278 * Will fail silently if the {@link #setUrl} method has not been called.
37279 * This does not activate the panel, just updates its content.
37281 refresh : function(){
37282 if(this.refreshDelegate){
37283 this.loaded = false;
37284 this.refreshDelegate();
37289 * Destroys this panel
37291 destroy : function(){
37292 this.el.removeAllListeners();
37293 var tempEl = document.createElement("span");
37294 tempEl.appendChild(this.el.dom);
37295 tempEl.innerHTML = "";
37301 * form - if the content panel contains a form - this is a reference to it.
37302 * @type {Roo.form.Form}
37306 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37307 * This contains a reference to it.
37313 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37323 * @param {Object} cfg Xtype definition of item to add.
37327 getChildContainer: function () {
37328 return this.getEl();
37333 var ret = new Roo.factory(cfg);
37338 if (cfg.xtype.match(/^Form$/)) {
37341 //if (this.footer) {
37342 // el = this.footer.container.insertSibling(false, 'before');
37344 el = this.el.createChild();
37347 this.form = new Roo.form.Form(cfg);
37350 if ( this.form.allItems.length) {
37351 this.form.render(el.dom);
37355 // should only have one of theses..
37356 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37357 // views.. should not be just added - used named prop 'view''
37359 cfg.el = this.el.appendChild(document.createElement("div"));
37362 var ret = new Roo.factory(cfg);
37364 ret.render && ret.render(false, ''); // render blank..
37374 * @class Roo.bootstrap.panel.Grid
37375 * @extends Roo.bootstrap.panel.Content
37377 * Create a new GridPanel.
37378 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37379 * @param {Object} config A the config object
37385 Roo.bootstrap.panel.Grid = function(config)
37389 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37390 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37392 config.el = this.wrapper;
37393 //this.el = this.wrapper;
37395 if (config.container) {
37396 // ctor'ed from a Border/panel.grid
37399 this.wrapper.setStyle("overflow", "hidden");
37400 this.wrapper.addClass('roo-grid-container');
37405 if(config.toolbar){
37406 var tool_el = this.wrapper.createChild();
37407 this.toolbar = Roo.factory(config.toolbar);
37409 if (config.toolbar.items) {
37410 ti = config.toolbar.items ;
37411 delete config.toolbar.items ;
37415 this.toolbar.render(tool_el);
37416 for(var i =0;i < ti.length;i++) {
37417 // Roo.log(['add child', items[i]]);
37418 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37420 this.toolbar.items = nitems;
37422 delete config.toolbar;
37425 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37426 config.grid.scrollBody = true;;
37427 config.grid.monitorWindowResize = false; // turn off autosizing
37428 config.grid.autoHeight = false;
37429 config.grid.autoWidth = false;
37431 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37433 if (config.background) {
37434 // render grid on panel activation (if panel background)
37435 this.on('activate', function(gp) {
37436 if (!gp.grid.rendered) {
37437 gp.grid.render(this.wrapper);
37438 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37443 this.grid.render(this.wrapper);
37444 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37447 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37448 // ??? needed ??? config.el = this.wrapper;
37453 // xtype created footer. - not sure if will work as we normally have to render first..
37454 if (this.footer && !this.footer.el && this.footer.xtype) {
37456 var ctr = this.grid.getView().getFooterPanel(true);
37457 this.footer.dataSource = this.grid.dataSource;
37458 this.footer = Roo.factory(this.footer, Roo);
37459 this.footer.render(ctr);
37469 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37470 getId : function(){
37471 return this.grid.id;
37475 * Returns the grid for this panel
37476 * @return {Roo.bootstrap.Table}
37478 getGrid : function(){
37482 setSize : function(width, height){
37483 if(!this.ignoreResize(width, height)){
37484 var grid = this.grid;
37485 var size = this.adjustForComponents(width, height);
37486 var gridel = grid.getGridEl();
37487 gridel.setSize(size.width, size.height);
37489 var thd = grid.getGridEl().select('thead',true).first();
37490 var tbd = grid.getGridEl().select('tbody', true).first();
37492 tbd.setSize(width, height - thd.getHeight());
37501 beforeSlide : function(){
37502 this.grid.getView().scroller.clip();
37505 afterSlide : function(){
37506 this.grid.getView().scroller.unclip();
37509 destroy : function(){
37510 this.grid.destroy();
37512 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37517 * @class Roo.bootstrap.panel.Nest
37518 * @extends Roo.bootstrap.panel.Content
37520 * Create a new Panel, that can contain a layout.Border.
37523 * @param {Roo.BorderLayout} layout The layout for this panel
37524 * @param {String/Object} config A string to set only the title or a config object
37526 Roo.bootstrap.panel.Nest = function(config)
37528 // construct with only one argument..
37529 /* FIXME - implement nicer consturctors
37530 if (layout.layout) {
37532 layout = config.layout;
37533 delete config.layout;
37535 if (layout.xtype && !layout.getEl) {
37536 // then layout needs constructing..
37537 layout = Roo.factory(layout, Roo);
37541 config.el = config.layout.getEl();
37543 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37545 config.layout.monitorWindowResize = false; // turn off autosizing
37546 this.layout = config.layout;
37547 this.layout.getEl().addClass("roo-layout-nested-layout");
37554 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37556 setSize : function(width, height){
37557 if(!this.ignoreResize(width, height)){
37558 var size = this.adjustForComponents(width, height);
37559 var el = this.layout.getEl();
37560 if (size.height < 1) {
37561 el.setWidth(size.width);
37563 el.setSize(size.width, size.height);
37565 var touch = el.dom.offsetWidth;
37566 this.layout.layout();
37567 // ie requires a double layout on the first pass
37568 if(Roo.isIE && !this.initialized){
37569 this.initialized = true;
37570 this.layout.layout();
37575 // activate all subpanels if not currently active..
37577 setActiveState : function(active){
37578 this.active = active;
37579 this.setActiveClass(active);
37582 this.fireEvent("deactivate", this);
37586 this.fireEvent("activate", this);
37587 // not sure if this should happen before or after..
37588 if (!this.layout) {
37589 return; // should not happen..
37592 for (var r in this.layout.regions) {
37593 reg = this.layout.getRegion(r);
37594 if (reg.getActivePanel()) {
37595 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37596 reg.setActivePanel(reg.getActivePanel());
37599 if (!reg.panels.length) {
37602 reg.showPanel(reg.getPanel(0));
37611 * Returns the nested BorderLayout for this panel
37612 * @return {Roo.BorderLayout}
37614 getLayout : function(){
37615 return this.layout;
37619 * Adds a xtype elements to the layout of the nested panel
37623 xtype : 'ContentPanel',
37630 xtype : 'NestedLayoutPanel',
37636 items : [ ... list of content panels or nested layout panels.. ]
37640 * @param {Object} cfg Xtype definition of item to add.
37642 addxtype : function(cfg) {
37643 return this.layout.addxtype(cfg);
37648 * Ext JS Library 1.1.1
37649 * Copyright(c) 2006-2007, Ext JS, LLC.
37651 * Originally Released Under LGPL - original licence link has changed is not relivant.
37654 * <script type="text/javascript">
37657 * @class Roo.TabPanel
37658 * @extends Roo.util.Observable
37659 * A lightweight tab container.
37663 // basic tabs 1, built from existing content
37664 var tabs = new Roo.TabPanel("tabs1");
37665 tabs.addTab("script", "View Script");
37666 tabs.addTab("markup", "View Markup");
37667 tabs.activate("script");
37669 // more advanced tabs, built from javascript
37670 var jtabs = new Roo.TabPanel("jtabs");
37671 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37673 // set up the UpdateManager
37674 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37675 var updater = tab2.getUpdateManager();
37676 updater.setDefaultUrl("ajax1.htm");
37677 tab2.on('activate', updater.refresh, updater, true);
37679 // Use setUrl for Ajax loading
37680 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37681 tab3.setUrl("ajax2.htm", null, true);
37684 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37687 jtabs.activate("jtabs-1");
37690 * Create a new TabPanel.
37691 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37692 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37694 Roo.bootstrap.panel.Tabs = function(config){
37696 * The container element for this TabPanel.
37697 * @type Roo.Element
37699 this.el = Roo.get(config.el);
37702 if(typeof config == "boolean"){
37703 this.tabPosition = config ? "bottom" : "top";
37705 Roo.apply(this, config);
37709 if(this.tabPosition == "bottom"){
37710 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37711 this.el.addClass("roo-tabs-bottom");
37713 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37714 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37715 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37717 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37719 if(this.tabPosition != "bottom"){
37720 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37721 * @type Roo.Element
37723 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37724 this.el.addClass("roo-tabs-top");
37728 this.bodyEl.setStyle("position", "relative");
37730 this.active = null;
37731 this.activateDelegate = this.activate.createDelegate(this);
37736 * Fires when the active tab changes
37737 * @param {Roo.TabPanel} this
37738 * @param {Roo.TabPanelItem} activePanel The new active tab
37742 * @event beforetabchange
37743 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37744 * @param {Roo.TabPanel} this
37745 * @param {Object} e Set cancel to true on this object to cancel the tab change
37746 * @param {Roo.TabPanelItem} tab The tab being changed to
37748 "beforetabchange" : true
37751 Roo.EventManager.onWindowResize(this.onResize, this);
37752 this.cpad = this.el.getPadding("lr");
37753 this.hiddenCount = 0;
37756 // toolbar on the tabbar support...
37757 if (this.toolbar) {
37758 alert("no toolbar support yet");
37759 this.toolbar = false;
37761 var tcfg = this.toolbar;
37762 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37763 this.toolbar = new Roo.Toolbar(tcfg);
37764 if (Roo.isSafari) {
37765 var tbl = tcfg.container.child('table', true);
37766 tbl.setAttribute('width', '100%');
37774 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37777 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37779 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37781 tabPosition : "top",
37783 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37785 currentTabWidth : 0,
37787 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37791 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37795 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37797 preferredTabWidth : 175,
37799 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37801 resizeTabs : false,
37803 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37805 monitorResize : true,
37807 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37812 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37813 * @param {String} id The id of the div to use <b>or create</b>
37814 * @param {String} text The text for the tab
37815 * @param {String} content (optional) Content to put in the TabPanelItem body
37816 * @param {Boolean} closable (optional) True to create a close icon on the tab
37817 * @return {Roo.TabPanelItem} The created TabPanelItem
37819 addTab : function(id, text, content, closable, tpl)
37821 var item = new Roo.bootstrap.panel.TabItem({
37825 closable : closable,
37828 this.addTabItem(item);
37830 item.setContent(content);
37836 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37837 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37838 * @return {Roo.TabPanelItem}
37840 getTab : function(id){
37841 return this.items[id];
37845 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37846 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37848 hideTab : function(id){
37849 var t = this.items[id];
37852 this.hiddenCount++;
37853 this.autoSizeTabs();
37858 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37859 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37861 unhideTab : function(id){
37862 var t = this.items[id];
37864 t.setHidden(false);
37865 this.hiddenCount--;
37866 this.autoSizeTabs();
37871 * Adds an existing {@link Roo.TabPanelItem}.
37872 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37874 addTabItem : function(item){
37875 this.items[item.id] = item;
37876 this.items.push(item);
37877 // if(this.resizeTabs){
37878 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37879 // this.autoSizeTabs();
37881 // item.autoSize();
37886 * Removes a {@link Roo.TabPanelItem}.
37887 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37889 removeTab : function(id){
37890 var items = this.items;
37891 var tab = items[id];
37892 if(!tab) { return; }
37893 var index = items.indexOf(tab);
37894 if(this.active == tab && items.length > 1){
37895 var newTab = this.getNextAvailable(index);
37900 this.stripEl.dom.removeChild(tab.pnode.dom);
37901 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37902 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37904 items.splice(index, 1);
37905 delete this.items[tab.id];
37906 tab.fireEvent("close", tab);
37907 tab.purgeListeners();
37908 this.autoSizeTabs();
37911 getNextAvailable : function(start){
37912 var items = this.items;
37914 // look for a next tab that will slide over to
37915 // replace the one being removed
37916 while(index < items.length){
37917 var item = items[++index];
37918 if(item && !item.isHidden()){
37922 // if one isn't found select the previous tab (on the left)
37925 var item = items[--index];
37926 if(item && !item.isHidden()){
37934 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37935 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37937 disableTab : function(id){
37938 var tab = this.items[id];
37939 if(tab && this.active != tab){
37945 * Enables a {@link Roo.TabPanelItem} that is disabled.
37946 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37948 enableTab : function(id){
37949 var tab = this.items[id];
37954 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37955 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37956 * @return {Roo.TabPanelItem} The TabPanelItem.
37958 activate : function(id){
37959 var tab = this.items[id];
37963 if(tab == this.active || tab.disabled){
37967 this.fireEvent("beforetabchange", this, e, tab);
37968 if(e.cancel !== true && !tab.disabled){
37970 this.active.hide();
37972 this.active = this.items[id];
37973 this.active.show();
37974 this.fireEvent("tabchange", this, this.active);
37980 * Gets the active {@link Roo.TabPanelItem}.
37981 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37983 getActiveTab : function(){
37984 return this.active;
37988 * Updates the tab body element to fit the height of the container element
37989 * for overflow scrolling
37990 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37992 syncHeight : function(targetHeight){
37993 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37994 var bm = this.bodyEl.getMargins();
37995 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37996 this.bodyEl.setHeight(newHeight);
38000 onResize : function(){
38001 if(this.monitorResize){
38002 this.autoSizeTabs();
38007 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38009 beginUpdate : function(){
38010 this.updating = true;
38014 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38016 endUpdate : function(){
38017 this.updating = false;
38018 this.autoSizeTabs();
38022 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38024 autoSizeTabs : function(){
38025 var count = this.items.length;
38026 var vcount = count - this.hiddenCount;
38027 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38030 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38031 var availWidth = Math.floor(w / vcount);
38032 var b = this.stripBody;
38033 if(b.getWidth() > w){
38034 var tabs = this.items;
38035 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38036 if(availWidth < this.minTabWidth){
38037 /*if(!this.sleft){ // incomplete scrolling code
38038 this.createScrollButtons();
38041 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38044 if(this.currentTabWidth < this.preferredTabWidth){
38045 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38051 * Returns the number of tabs in this TabPanel.
38054 getCount : function(){
38055 return this.items.length;
38059 * Resizes all the tabs to the passed width
38060 * @param {Number} The new width
38062 setTabWidth : function(width){
38063 this.currentTabWidth = width;
38064 for(var i = 0, len = this.items.length; i < len; i++) {
38065 if(!this.items[i].isHidden()) {
38066 this.items[i].setWidth(width);
38072 * Destroys this TabPanel
38073 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38075 destroy : function(removeEl){
38076 Roo.EventManager.removeResizeListener(this.onResize, this);
38077 for(var i = 0, len = this.items.length; i < len; i++){
38078 this.items[i].purgeListeners();
38080 if(removeEl === true){
38081 this.el.update("");
38086 createStrip : function(container)
38088 var strip = document.createElement("nav");
38089 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38090 container.appendChild(strip);
38094 createStripList : function(strip)
38096 // div wrapper for retard IE
38097 // returns the "tr" element.
38098 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38099 //'<div class="x-tabs-strip-wrap">'+
38100 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38101 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38102 return strip.firstChild; //.firstChild.firstChild.firstChild;
38104 createBody : function(container)
38106 var body = document.createElement("div");
38107 Roo.id(body, "tab-body");
38108 //Roo.fly(body).addClass("x-tabs-body");
38109 Roo.fly(body).addClass("tab-content");
38110 container.appendChild(body);
38113 createItemBody :function(bodyEl, id){
38114 var body = Roo.getDom(id);
38116 body = document.createElement("div");
38119 //Roo.fly(body).addClass("x-tabs-item-body");
38120 Roo.fly(body).addClass("tab-pane");
38121 bodyEl.insertBefore(body, bodyEl.firstChild);
38125 createStripElements : function(stripEl, text, closable, tpl)
38127 var td = document.createElement("li"); // was td..
38130 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38133 stripEl.appendChild(td);
38135 td.className = "x-tabs-closable";
38136 if(!this.closeTpl){
38137 this.closeTpl = new Roo.Template(
38138 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38139 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38140 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38143 var el = this.closeTpl.overwrite(td, {"text": text});
38144 var close = el.getElementsByTagName("div")[0];
38145 var inner = el.getElementsByTagName("em")[0];
38146 return {"el": el, "close": close, "inner": inner};
38149 // not sure what this is..
38150 // if(!this.tabTpl){
38151 //this.tabTpl = new Roo.Template(
38152 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38153 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38155 // this.tabTpl = new Roo.Template(
38156 // '<a href="#">' +
38157 // '<span unselectable="on"' +
38158 // (this.disableTooltips ? '' : ' title="{text}"') +
38159 // ' >{text}</span></a>'
38165 var template = tpl || this.tabTpl || false;
38169 template = new Roo.Template(
38171 '<span unselectable="on"' +
38172 (this.disableTooltips ? '' : ' title="{text}"') +
38173 ' >{text}</span></a>'
38177 switch (typeof(template)) {
38181 template = new Roo.Template(template);
38187 var el = template.overwrite(td, {"text": text});
38189 var inner = el.getElementsByTagName("span")[0];
38191 return {"el": el, "inner": inner};
38199 * @class Roo.TabPanelItem
38200 * @extends Roo.util.Observable
38201 * Represents an individual item (tab plus body) in a TabPanel.
38202 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38203 * @param {String} id The id of this TabPanelItem
38204 * @param {String} text The text for the tab of this TabPanelItem
38205 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38207 Roo.bootstrap.panel.TabItem = function(config){
38209 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38210 * @type Roo.TabPanel
38212 this.tabPanel = config.panel;
38214 * The id for this TabPanelItem
38217 this.id = config.id;
38219 this.disabled = false;
38221 this.text = config.text;
38223 this.loaded = false;
38224 this.closable = config.closable;
38227 * The body element for this TabPanelItem.
38228 * @type Roo.Element
38230 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38231 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38232 this.bodyEl.setStyle("display", "block");
38233 this.bodyEl.setStyle("zoom", "1");
38234 //this.hideAction();
38236 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38238 this.el = Roo.get(els.el);
38239 this.inner = Roo.get(els.inner, true);
38240 this.textEl = Roo.get(this.el.dom.firstChild, true);
38241 this.pnode = Roo.get(els.el.parentNode, true);
38242 // this.el.on("mousedown", this.onTabMouseDown, this);
38243 this.el.on("click", this.onTabClick, this);
38245 if(config.closable){
38246 var c = Roo.get(els.close, true);
38247 c.dom.title = this.closeText;
38248 c.addClassOnOver("close-over");
38249 c.on("click", this.closeClick, this);
38255 * Fires when this tab becomes the active tab.
38256 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38257 * @param {Roo.TabPanelItem} this
38261 * @event beforeclose
38262 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38263 * @param {Roo.TabPanelItem} this
38264 * @param {Object} e Set cancel to true on this object to cancel the close.
38266 "beforeclose": true,
38269 * Fires when this tab is closed.
38270 * @param {Roo.TabPanelItem} this
38274 * @event deactivate
38275 * Fires when this tab is no longer the active tab.
38276 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38277 * @param {Roo.TabPanelItem} this
38279 "deactivate" : true
38281 this.hidden = false;
38283 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38286 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38288 purgeListeners : function(){
38289 Roo.util.Observable.prototype.purgeListeners.call(this);
38290 this.el.removeAllListeners();
38293 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38296 this.pnode.addClass("active");
38299 this.tabPanel.stripWrap.repaint();
38301 this.fireEvent("activate", this.tabPanel, this);
38305 * Returns true if this tab is the active tab.
38306 * @return {Boolean}
38308 isActive : function(){
38309 return this.tabPanel.getActiveTab() == this;
38313 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38316 this.pnode.removeClass("active");
38318 this.fireEvent("deactivate", this.tabPanel, this);
38321 hideAction : function(){
38322 this.bodyEl.hide();
38323 this.bodyEl.setStyle("position", "absolute");
38324 this.bodyEl.setLeft("-20000px");
38325 this.bodyEl.setTop("-20000px");
38328 showAction : function(){
38329 this.bodyEl.setStyle("position", "relative");
38330 this.bodyEl.setTop("");
38331 this.bodyEl.setLeft("");
38332 this.bodyEl.show();
38336 * Set the tooltip for the tab.
38337 * @param {String} tooltip The tab's tooltip
38339 setTooltip : function(text){
38340 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38341 this.textEl.dom.qtip = text;
38342 this.textEl.dom.removeAttribute('title');
38344 this.textEl.dom.title = text;
38348 onTabClick : function(e){
38349 e.preventDefault();
38350 this.tabPanel.activate(this.id);
38353 onTabMouseDown : function(e){
38354 e.preventDefault();
38355 this.tabPanel.activate(this.id);
38358 getWidth : function(){
38359 return this.inner.getWidth();
38362 setWidth : function(width){
38363 var iwidth = width - this.pnode.getPadding("lr");
38364 this.inner.setWidth(iwidth);
38365 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38366 this.pnode.setWidth(width);
38370 * Show or hide the tab
38371 * @param {Boolean} hidden True to hide or false to show.
38373 setHidden : function(hidden){
38374 this.hidden = hidden;
38375 this.pnode.setStyle("display", hidden ? "none" : "");
38379 * Returns true if this tab is "hidden"
38380 * @return {Boolean}
38382 isHidden : function(){
38383 return this.hidden;
38387 * Returns the text for this tab
38390 getText : function(){
38394 autoSize : function(){
38395 //this.el.beginMeasure();
38396 this.textEl.setWidth(1);
38398 * #2804 [new] Tabs in Roojs
38399 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38401 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38402 //this.el.endMeasure();
38406 * Sets the text for the tab (Note: this also sets the tooltip text)
38407 * @param {String} text The tab's text and tooltip
38409 setText : function(text){
38411 this.textEl.update(text);
38412 this.setTooltip(text);
38413 //if(!this.tabPanel.resizeTabs){
38414 // this.autoSize();
38418 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38420 activate : function(){
38421 this.tabPanel.activate(this.id);
38425 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38427 disable : function(){
38428 if(this.tabPanel.active != this){
38429 this.disabled = true;
38430 this.pnode.addClass("disabled");
38435 * Enables this TabPanelItem if it was previously disabled.
38437 enable : function(){
38438 this.disabled = false;
38439 this.pnode.removeClass("disabled");
38443 * Sets the content for this TabPanelItem.
38444 * @param {String} content The content
38445 * @param {Boolean} loadScripts true to look for and load scripts
38447 setContent : function(content, loadScripts){
38448 this.bodyEl.update(content, loadScripts);
38452 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38453 * @return {Roo.UpdateManager} The UpdateManager
38455 getUpdateManager : function(){
38456 return this.bodyEl.getUpdateManager();
38460 * Set a URL to be used to load the content for this TabPanelItem.
38461 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38462 * @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)
38463 * @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)
38464 * @return {Roo.UpdateManager} The UpdateManager
38466 setUrl : function(url, params, loadOnce){
38467 if(this.refreshDelegate){
38468 this.un('activate', this.refreshDelegate);
38470 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38471 this.on("activate", this.refreshDelegate);
38472 return this.bodyEl.getUpdateManager();
38476 _handleRefresh : function(url, params, loadOnce){
38477 if(!loadOnce || !this.loaded){
38478 var updater = this.bodyEl.getUpdateManager();
38479 updater.update(url, params, this._setLoaded.createDelegate(this));
38484 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38485 * Will fail silently if the setUrl method has not been called.
38486 * This does not activate the panel, just updates its content.
38488 refresh : function(){
38489 if(this.refreshDelegate){
38490 this.loaded = false;
38491 this.refreshDelegate();
38496 _setLoaded : function(){
38497 this.loaded = true;
38501 closeClick : function(e){
38504 this.fireEvent("beforeclose", this, o);
38505 if(o.cancel !== true){
38506 this.tabPanel.removeTab(this.id);
38510 * The text displayed in the tooltip for the close icon.
38513 closeText : "Close this tab"
38516 * This script refer to:
38517 * Title: International Telephone Input
38518 * Author: Jack O'Connor
38519 * Code version: v12.1.12
38520 * Availability: https://github.com/jackocnr/intl-tel-input.git
38523 Roo.bootstrap.PhoneInputData = function() {
38526 "Afghanistan (افغانستان)",
38531 "Albania (Shqipëri)",
38536 "Algeria (الجزائر)",
38561 "Antigua and Barbuda",
38571 "Armenia (Հայաստան)",
38587 "Austria (Österreich)",
38592 "Azerbaijan (Azərbaycan)",
38602 "Bahrain (البحرين)",
38607 "Bangladesh (বাংলাদেশ)",
38617 "Belarus (Беларусь)",
38622 "Belgium (België)",
38652 "Bosnia and Herzegovina (Босна и Херцеговина)",
38667 "British Indian Ocean Territory",
38672 "British Virgin Islands",
38682 "Bulgaria (България)",
38692 "Burundi (Uburundi)",
38697 "Cambodia (កម្ពុជា)",
38702 "Cameroon (Cameroun)",
38711 ["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"]
38714 "Cape Verde (Kabu Verdi)",
38719 "Caribbean Netherlands",
38730 "Central African Republic (République centrafricaine)",
38750 "Christmas Island",
38756 "Cocos (Keeling) Islands",
38767 "Comoros (جزر القمر)",
38772 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38777 "Congo (Republic) (Congo-Brazzaville)",
38797 "Croatia (Hrvatska)",
38818 "Czech Republic (Česká republika)",
38823 "Denmark (Danmark)",
38838 "Dominican Republic (República Dominicana)",
38842 ["809", "829", "849"]
38860 "Equatorial Guinea (Guinea Ecuatorial)",
38880 "Falkland Islands (Islas Malvinas)",
38885 "Faroe Islands (Føroyar)",
38906 "French Guiana (Guyane française)",
38911 "French Polynesia (Polynésie française)",
38926 "Georgia (საქართველო)",
38931 "Germany (Deutschland)",
38951 "Greenland (Kalaallit Nunaat)",
38988 "Guinea-Bissau (Guiné Bissau)",
39013 "Hungary (Magyarország)",
39018 "Iceland (Ísland)",
39038 "Iraq (العراق)",
39054 "Israel (ישראל)",
39081 "Jordan (الأردن)",
39086 "Kazakhstan (Казахстан)",
39107 "Kuwait (الكويت)",
39112 "Kyrgyzstan (Кыргызстан)",
39122 "Latvia (Latvija)",
39127 "Lebanon (لبنان)",
39142 "Libya (ليبيا)",
39152 "Lithuania (Lietuva)",
39167 "Macedonia (FYROM) (Македонија)",
39172 "Madagascar (Madagasikara)",
39202 "Marshall Islands",
39212 "Mauritania (موريتانيا)",
39217 "Mauritius (Moris)",
39238 "Moldova (Republica Moldova)",
39248 "Mongolia (Монгол)",
39253 "Montenegro (Crna Gora)",
39263 "Morocco (المغرب)",
39269 "Mozambique (Moçambique)",
39274 "Myanmar (Burma) (မြန်မာ)",
39279 "Namibia (Namibië)",
39294 "Netherlands (Nederland)",
39299 "New Caledonia (Nouvelle-Calédonie)",
39334 "North Korea (조선 민주주의 인민 공화국)",
39339 "Northern Mariana Islands",
39355 "Pakistan (پاکستان)",
39365 "Palestine (فلسطين)",
39375 "Papua New Guinea",
39417 "Réunion (La Réunion)",
39423 "Romania (România)",
39439 "Saint Barthélemy",
39450 "Saint Kitts and Nevis",
39460 "Saint Martin (Saint-Martin (partie française))",
39466 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39471 "Saint Vincent and the Grenadines",
39486 "São Tomé and Príncipe (São Tomé e Príncipe)",
39491 "Saudi Arabia (المملكة العربية السعودية)",
39496 "Senegal (Sénégal)",
39526 "Slovakia (Slovensko)",
39531 "Slovenia (Slovenija)",
39541 "Somalia (Soomaaliya)",
39551 "South Korea (대한민국)",
39556 "South Sudan (جنوب السودان)",
39566 "Sri Lanka (ශ්රී ලංකාව)",
39571 "Sudan (السودان)",
39581 "Svalbard and Jan Mayen",
39592 "Sweden (Sverige)",
39597 "Switzerland (Schweiz)",
39602 "Syria (سوريا)",
39647 "Trinidad and Tobago",
39652 "Tunisia (تونس)",
39657 "Turkey (Türkiye)",
39667 "Turks and Caicos Islands",
39677 "U.S. Virgin Islands",
39687 "Ukraine (Україна)",
39692 "United Arab Emirates (الإمارات العربية المتحدة)",
39714 "Uzbekistan (Oʻzbekiston)",
39724 "Vatican City (Città del Vaticano)",
39735 "Vietnam (Việt Nam)",
39740 "Wallis and Futuna (Wallis-et-Futuna)",
39745 "Western Sahara (الصحراء الغربية)",
39751 "Yemen (اليمن)",
39775 * This script refer to:
39776 * Title: International Telephone Input
39777 * Author: Jack O'Connor
39778 * Code version: v12.1.12
39779 * Availability: https://github.com/jackocnr/intl-tel-input.git
39783 * @class Roo.bootstrap.PhoneInput
39784 * @extends Roo.bootstrap.TriggerField
39785 * An input with International dial-code selection
39787 * @cfg {String} defaultDialCode default '+852'
39788 * @cfg {Array} preferedCountries default []
39791 * Create a new PhoneInput.
39792 * @param {Object} config Configuration options
39795 Roo.bootstrap.PhoneInput = function(config) {
39796 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39799 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39801 listWidth: undefined,
39803 selectedClass: 'active',
39805 invalidClass : "has-warning",
39807 validClass: 'has-success',
39809 allowed: '0123456789',
39812 * @cfg {String} defaultDialCode The default dial code when initializing the input
39814 defaultDialCode: '+852',
39817 * @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
39819 preferedCountries: false,
39821 getAutoCreate : function()
39823 var data = Roo.bootstrap.PhoneInputData();
39824 var align = this.labelAlign || this.parentLabelAlign();
39827 this.allCountries = [];
39828 this.dialCodeMapping = [];
39830 for (var i = 0; i < data.length; i++) {
39832 this.allCountries[i] = {
39836 priority: c[3] || 0,
39837 areaCodes: c[4] || null
39839 this.dialCodeMapping[c[2]] = {
39842 priority: c[3] || 0,
39843 areaCodes: c[4] || null
39855 cls : 'form-control tel-input',
39856 autocomplete: 'new-password'
39859 var hiddenInput = {
39862 cls: 'hidden-tel-input'
39866 hiddenInput.name = this.name;
39869 if (this.disabled) {
39870 input.disabled = true;
39873 var flag_container = {
39890 cls: this.hasFeedback ? 'has-feedback' : '',
39896 cls: 'dial-code-holder',
39903 cls: 'roo-select2-container input-group',
39910 if (this.fieldLabel.length) {
39913 tooltip: 'This field is required'
39919 cls: 'control-label',
39925 html: this.fieldLabel
39928 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39934 if(this.indicatorpos == 'right') {
39935 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39942 if(align == 'left') {
39950 if(this.labelWidth > 12){
39951 label.style = "width: " + this.labelWidth + 'px';
39953 if(this.labelWidth < 13 && this.labelmd == 0){
39954 this.labelmd = this.labelWidth;
39956 if(this.labellg > 0){
39957 label.cls += ' col-lg-' + this.labellg;
39958 input.cls += ' col-lg-' + (12 - this.labellg);
39960 if(this.labelmd > 0){
39961 label.cls += ' col-md-' + this.labelmd;
39962 container.cls += ' col-md-' + (12 - this.labelmd);
39964 if(this.labelsm > 0){
39965 label.cls += ' col-sm-' + this.labelsm;
39966 container.cls += ' col-sm-' + (12 - this.labelsm);
39968 if(this.labelxs > 0){
39969 label.cls += ' col-xs-' + this.labelxs;
39970 container.cls += ' col-xs-' + (12 - this.labelxs);
39980 var settings = this;
39982 ['xs','sm','md','lg'].map(function(size){
39983 if (settings[size]) {
39984 cfg.cls += ' col-' + size + '-' + settings[size];
39988 this.store = new Roo.data.Store({
39989 proxy : new Roo.data.MemoryProxy({}),
39990 reader : new Roo.data.JsonReader({
40001 'name' : 'dialCode',
40005 'name' : 'priority',
40009 'name' : 'areaCodes',
40016 if(!this.preferedCountries) {
40017 this.preferedCountries = [
40024 var p = this.preferedCountries.reverse();
40027 for (var i = 0; i < p.length; i++) {
40028 for (var j = 0; j < this.allCountries.length; j++) {
40029 if(this.allCountries[j].iso2 == p[i]) {
40030 var t = this.allCountries[j];
40031 this.allCountries.splice(j,1);
40032 this.allCountries.unshift(t);
40038 this.store.proxy.data = {
40040 data: this.allCountries
40046 initEvents : function()
40049 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40051 this.indicator = this.indicatorEl();
40052 this.flag = this.flagEl();
40053 this.dialCodeHolder = this.dialCodeHolderEl();
40055 this.trigger = this.el.select('div.flag-box',true).first();
40056 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40061 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40062 _this.list.setWidth(lw);
40065 this.list.on('mouseover', this.onViewOver, this);
40066 this.list.on('mousemove', this.onViewMove, this);
40067 this.inputEl().on("keyup", this.onKeyUp, this);
40069 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40071 this.view = new Roo.View(this.list, this.tpl, {
40072 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40075 this.view.on('click', this.onViewClick, this);
40076 this.setValue(this.defaultDialCode);
40079 onTriggerClick : function(e)
40081 Roo.log('trigger click');
40086 if(this.isExpanded()){
40088 this.hasFocus = false;
40090 this.store.load({});
40091 this.hasFocus = true;
40096 isExpanded : function()
40098 return this.list.isVisible();
40101 collapse : function()
40103 if(!this.isExpanded()){
40107 Roo.get(document).un('mousedown', this.collapseIf, this);
40108 Roo.get(document).un('mousewheel', this.collapseIf, this);
40109 this.fireEvent('collapse', this);
40113 expand : function()
40117 if(this.isExpanded() || !this.hasFocus){
40121 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40122 this.list.setWidth(lw);
40125 this.restrictHeight();
40127 Roo.get(document).on('mousedown', this.collapseIf, this);
40128 Roo.get(document).on('mousewheel', this.collapseIf, this);
40130 this.fireEvent('expand', this);
40133 restrictHeight : function()
40135 this.list.alignTo(this.inputEl(), this.listAlign);
40136 this.list.alignTo(this.inputEl(), this.listAlign);
40139 onViewOver : function(e, t)
40141 if(this.inKeyMode){
40144 var item = this.view.findItemFromChild(t);
40147 var index = this.view.indexOf(item);
40148 this.select(index, false);
40153 onViewClick : function(view, doFocus, el, e)
40155 var index = this.view.getSelectedIndexes()[0];
40157 var r = this.store.getAt(index);
40160 this.onSelect(r, index);
40162 if(doFocus !== false && !this.blockFocus){
40163 this.inputEl().focus();
40167 onViewMove : function(e, t)
40169 this.inKeyMode = false;
40172 select : function(index, scrollIntoView)
40174 this.selectedIndex = index;
40175 this.view.select(index);
40176 if(scrollIntoView !== false){
40177 var el = this.view.getNode(index);
40179 this.list.scrollChildIntoView(el, false);
40184 createList : function()
40186 this.list = Roo.get(document.body).createChild({
40188 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40189 style: 'display:none'
40192 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40195 collapseIf : function(e)
40197 var in_combo = e.within(this.el);
40198 var in_list = e.within(this.list);
40199 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40201 if (in_combo || in_list || is_list) {
40207 onSelect : function(record, index)
40209 if(this.fireEvent('beforeselect', this, record, index) !== false){
40211 this.setFlagClass(record.data.iso2);
40212 this.setDialCode(record.data.dialCode);
40213 this.hasFocus = false;
40215 this.fireEvent('select', this, record, index);
40219 flagEl : function()
40221 var flag = this.el.select('div.flag',true).first();
40228 dialCodeHolderEl : function()
40230 var d = this.el.select('input.dial-code-holder',true).first();
40237 setDialCode : function(v)
40239 this.dialCodeHolder.dom.value = '+'+v;
40242 setFlagClass : function(n)
40244 this.flag.dom.className = 'flag '+n;
40247 getValue : function()
40249 var v = this.inputEl().getValue();
40250 if(this.dialCodeHolder) {
40251 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40256 setValue : function(v)
40258 var d = this.getDialCode(v);
40260 //invalid dial code
40261 if(v.length == 0 || !d || d.length == 0) {
40263 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40264 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40270 this.setFlagClass(this.dialCodeMapping[d].iso2);
40271 this.setDialCode(d);
40272 this.inputEl().dom.value = v.replace('+'+d,'');
40273 this.hiddenEl().dom.value = this.getValue();
40278 getDialCode : function(v)
40282 if (v.length == 0) {
40283 return this.dialCodeHolder.dom.value;
40287 if (v.charAt(0) != "+") {
40290 var numericChars = "";
40291 for (var i = 1; i < v.length; i++) {
40292 var c = v.charAt(i);
40295 if (this.dialCodeMapping[numericChars]) {
40296 dialCode = v.substr(1, i);
40298 if (numericChars.length == 4) {
40308 this.setValue(this.defaultDialCode);
40312 hiddenEl : function()
40314 return this.el.select('input.hidden-tel-input',true).first();
40317 onKeyUp : function(e){
40319 var k = e.getKey();
40320 var c = e.getCharCode();
40323 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40324 this.allowed.indexOf(String.fromCharCode(c)) === -1
40329 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40332 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40336 this.setValue(this.getValue());
40341 * @class Roo.bootstrap.MoneyField
40342 * @extends Roo.bootstrap.ComboBox
40343 * Bootstrap MoneyField class
40346 * Create a new MoneyField.
40347 * @param {Object} config Configuration options
40350 Roo.bootstrap.MoneyField = function(config) {
40352 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40356 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40359 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40361 allowDecimals : true,
40363 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40365 decimalSeparator : ".",
40367 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40369 decimalPrecision : 0,
40371 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40373 allowNegative : true,
40375 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40379 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40381 minValue : Number.NEGATIVE_INFINITY,
40383 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40385 maxValue : Number.MAX_VALUE,
40387 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40389 minText : "The minimum value for this field is {0}",
40391 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40393 maxText : "The maximum value for this field is {0}",
40395 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40396 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40398 nanText : "{0} is not a valid number",
40400 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40404 * @cfg {String} defaults currency of the MoneyField
40405 * value should be in lkey
40407 defaultCurrency : false,
40409 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40411 thousandsDelimiter : false,
40421 getAutoCreate : function()
40423 var align = this.labelAlign || this.parentLabelAlign();
40435 cls : 'form-control roo-money-amount-input',
40436 autocomplete: 'new-password'
40439 var hiddenInput = {
40443 cls: 'hidden-number-input'
40447 hiddenInput.name = this.name;
40450 if (this.disabled) {
40451 input.disabled = true;
40454 var clg = 12 - this.inputlg;
40455 var cmd = 12 - this.inputmd;
40456 var csm = 12 - this.inputsm;
40457 var cxs = 12 - this.inputxs;
40461 cls : 'row roo-money-field',
40465 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40469 cls: 'roo-select2-container input-group',
40473 cls : 'form-control roo-money-currency-input',
40474 autocomplete: 'new-password',
40476 name : this.currencyName
40480 cls : 'input-group-addon',
40494 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40498 cls: this.hasFeedback ? 'has-feedback' : '',
40509 if (this.fieldLabel.length) {
40512 tooltip: 'This field is required'
40518 cls: 'control-label',
40524 html: this.fieldLabel
40527 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40533 if(this.indicatorpos == 'right') {
40534 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40541 if(align == 'left') {
40549 if(this.labelWidth > 12){
40550 label.style = "width: " + this.labelWidth + 'px';
40552 if(this.labelWidth < 13 && this.labelmd == 0){
40553 this.labelmd = this.labelWidth;
40555 if(this.labellg > 0){
40556 label.cls += ' col-lg-' + this.labellg;
40557 input.cls += ' col-lg-' + (12 - this.labellg);
40559 if(this.labelmd > 0){
40560 label.cls += ' col-md-' + this.labelmd;
40561 container.cls += ' col-md-' + (12 - this.labelmd);
40563 if(this.labelsm > 0){
40564 label.cls += ' col-sm-' + this.labelsm;
40565 container.cls += ' col-sm-' + (12 - this.labelsm);
40567 if(this.labelxs > 0){
40568 label.cls += ' col-xs-' + this.labelxs;
40569 container.cls += ' col-xs-' + (12 - this.labelxs);
40580 var settings = this;
40582 ['xs','sm','md','lg'].map(function(size){
40583 if (settings[size]) {
40584 cfg.cls += ' col-' + size + '-' + settings[size];
40591 initEvents : function()
40593 this.indicator = this.indicatorEl();
40595 this.initCurrencyEvent();
40597 this.initNumberEvent();
40600 initCurrencyEvent : function()
40603 throw "can not find store for combo";
40606 this.store = Roo.factory(this.store, Roo.data);
40607 this.store.parent = this;
40611 this.triggerEl = this.el.select('.input-group-addon', true).first();
40613 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40618 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40619 _this.list.setWidth(lw);
40622 this.list.on('mouseover', this.onViewOver, this);
40623 this.list.on('mousemove', this.onViewMove, this);
40624 this.list.on('scroll', this.onViewScroll, this);
40627 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40630 this.view = new Roo.View(this.list, this.tpl, {
40631 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40634 this.view.on('click', this.onViewClick, this);
40636 this.store.on('beforeload', this.onBeforeLoad, this);
40637 this.store.on('load', this.onLoad, this);
40638 this.store.on('loadexception', this.onLoadException, this);
40640 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40641 "up" : function(e){
40642 this.inKeyMode = true;
40646 "down" : function(e){
40647 if(!this.isExpanded()){
40648 this.onTriggerClick();
40650 this.inKeyMode = true;
40655 "enter" : function(e){
40658 if(this.fireEvent("specialkey", this, e)){
40659 this.onViewClick(false);
40665 "esc" : function(e){
40669 "tab" : function(e){
40672 if(this.fireEvent("specialkey", this, e)){
40673 this.onViewClick(false);
40681 doRelay : function(foo, bar, hname){
40682 if(hname == 'down' || this.scope.isExpanded()){
40683 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40691 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40695 initNumberEvent : function(e)
40697 this.inputEl().on("keydown" , this.fireKey, this);
40698 this.inputEl().on("focus", this.onFocus, this);
40699 this.inputEl().on("blur", this.onBlur, this);
40701 this.inputEl().relayEvent('keyup', this);
40703 if(this.indicator){
40704 this.indicator.addClass('invisible');
40707 this.originalValue = this.getValue();
40709 if(this.validationEvent == 'keyup'){
40710 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40711 this.inputEl().on('keyup', this.filterValidation, this);
40713 else if(this.validationEvent !== false){
40714 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40717 if(this.selectOnFocus){
40718 this.on("focus", this.preFocus, this);
40721 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40722 this.inputEl().on("keypress", this.filterKeys, this);
40724 this.inputEl().relayEvent('keypress', this);
40727 var allowed = "0123456789";
40729 if(this.allowDecimals){
40730 allowed += this.decimalSeparator;
40733 if(this.allowNegative){
40737 if(this.thousandsDelimiter) {
40741 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40743 var keyPress = function(e){
40745 var k = e.getKey();
40747 var c = e.getCharCode();
40750 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40751 allowed.indexOf(String.fromCharCode(c)) === -1
40757 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40761 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40766 this.inputEl().on("keypress", keyPress, this);
40770 onTriggerClick : function(e)
40777 this.loadNext = false;
40779 if(this.isExpanded()){
40784 this.hasFocus = true;
40786 if(this.triggerAction == 'all') {
40787 this.doQuery(this.allQuery, true);
40791 this.doQuery(this.getRawValue());
40794 getCurrency : function()
40796 var v = this.currencyEl().getValue();
40801 restrictHeight : function()
40803 this.list.alignTo(this.currencyEl(), this.listAlign);
40804 this.list.alignTo(this.currencyEl(), this.listAlign);
40807 onViewClick : function(view, doFocus, el, e)
40809 var index = this.view.getSelectedIndexes()[0];
40811 var r = this.store.getAt(index);
40814 this.onSelect(r, index);
40818 onSelect : function(record, index){
40820 if(this.fireEvent('beforeselect', this, record, index) !== false){
40822 this.setFromCurrencyData(index > -1 ? record.data : false);
40826 this.fireEvent('select', this, record, index);
40830 setFromCurrencyData : function(o)
40834 this.lastCurrency = o;
40836 if (this.currencyField) {
40837 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40839 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40842 this.lastSelectionText = currency;
40844 //setting default currency
40845 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40846 this.setCurrency(this.defaultCurrency);
40850 this.setCurrency(currency);
40853 setFromData : function(o)
40857 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40859 this.setFromCurrencyData(c);
40864 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40866 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40869 this.setValue(value);
40873 setCurrency : function(v)
40875 this.currencyValue = v;
40878 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40883 setValue : function(v)
40885 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40891 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40893 this.inputEl().dom.value = (v == '') ? '' :
40894 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40896 if(!this.allowZero && v === '0') {
40897 this.hiddenEl().dom.value = '';
40898 this.inputEl().dom.value = '';
40905 getRawValue : function()
40907 var v = this.inputEl().getValue();
40912 getValue : function()
40914 return this.fixPrecision(this.parseValue(this.getRawValue()));
40917 parseValue : function(value)
40919 if(this.thousandsDelimiter) {
40921 r = new RegExp(",", "g");
40922 value = value.replace(r, "");
40925 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40926 return isNaN(value) ? '' : value;
40930 fixPrecision : function(value)
40932 if(this.thousandsDelimiter) {
40934 r = new RegExp(",", "g");
40935 value = value.replace(r, "");
40938 var nan = isNaN(value);
40940 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40941 return nan ? '' : value;
40943 return parseFloat(value).toFixed(this.decimalPrecision);
40946 decimalPrecisionFcn : function(v)
40948 return Math.floor(v);
40951 validateValue : function(value)
40953 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40957 var num = this.parseValue(value);
40960 this.markInvalid(String.format(this.nanText, value));
40964 if(num < this.minValue){
40965 this.markInvalid(String.format(this.minText, this.minValue));
40969 if(num > this.maxValue){
40970 this.markInvalid(String.format(this.maxText, this.maxValue));
40977 validate : function()
40979 if(this.disabled || this.allowBlank){
40984 var currency = this.getCurrency();
40986 if(this.validateValue(this.getRawValue()) && currency.length){
40991 this.markInvalid();
40995 getName: function()
41000 beforeBlur : function()
41006 var v = this.parseValue(this.getRawValue());
41013 onBlur : function()
41017 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41018 //this.el.removeClass(this.focusClass);
41021 this.hasFocus = false;
41023 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41027 var v = this.getValue();
41029 if(String(v) !== String(this.startValue)){
41030 this.fireEvent('change', this, v, this.startValue);
41033 this.fireEvent("blur", this);
41036 inputEl : function()
41038 return this.el.select('.roo-money-amount-input', true).first();
41041 currencyEl : function()
41043 return this.el.select('.roo-money-currency-input', true).first();
41046 hiddenEl : function()
41048 return this.el.select('input.hidden-number-input',true).first();