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;
929 setWeight : function(str)
931 this.el.removeClass(this.weightClass);
932 this.el.addClass('btn-' + str);
946 * @class Roo.bootstrap.Column
947 * @extends Roo.bootstrap.Component
948 * Bootstrap Column class
949 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
959 * @cfg {Boolean} hidden (true|false) hide the element
960 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961 * @cfg {String} fa (ban|check|...) font awesome icon
962 * @cfg {Number} fasize (1|2|....) font awsome size
964 * @cfg {String} icon (info-sign|check|...) glyphicon name
966 * @cfg {String} html content of column.
969 * Create a new Column
970 * @param {Object} config The config object
973 Roo.bootstrap.Column = function(config){
974 Roo.bootstrap.Column.superclass.constructor.call(this, config);
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
995 getAutoCreate : function(){
996 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1004 ['xs','sm','md','lg'].map(function(size){
1005 //Roo.log( size + ':' + settings[size]);
1007 if (settings[size+'off'] !== false) {
1008 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1011 if (settings[size] === false) {
1015 if (!settings[size]) { // 0 = hidden
1016 cfg.cls += ' hidden-' + size;
1019 cfg.cls += ' col-' + size + '-' + settings[size];
1024 cfg.cls += ' hidden';
1027 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028 cfg.cls +=' alert alert-' + this.alert;
1032 if (this.html.length) {
1033 cfg.html = this.html;
1037 if (this.fasize > 1) {
1038 fasize = ' fa-' + this.fasize + 'x';
1040 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1045 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1064 * @class Roo.bootstrap.Container
1065 * @extends Roo.bootstrap.Component
1066 * Bootstrap Container class
1067 * @cfg {Boolean} jumbotron is it a jumbotron element
1068 * @cfg {String} html content of element
1069 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1071 * @cfg {String} header content of header (for panel)
1072 * @cfg {String} footer content of footer (for panel)
1073 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074 * @cfg {String} tag (header|aside|section) type of HTML tag.
1075 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076 * @cfg {String} fa font awesome icon
1077 * @cfg {String} icon (info-sign|check|...) glyphicon name
1078 * @cfg {Boolean} hidden (true|false) hide the element
1079 * @cfg {Boolean} expandable (true|false) default false
1080 * @cfg {Boolean} expanded (true|false) default true
1081 * @cfg {String} rheader contet on the right of header
1082 * @cfg {Boolean} clickable (true|false) default false
1086 * Create a new Container
1087 * @param {Object} config The config object
1090 Roo.bootstrap.Container = function(config){
1091 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1097 * After the panel has been expand
1099 * @param {Roo.bootstrap.Container} this
1104 * After the panel has been collapsed
1106 * @param {Roo.bootstrap.Container} this
1111 * When a element is chick
1112 * @param {Roo.bootstrap.Container} this
1113 * @param {Roo.EventObject} e
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1137 getChildContainer : function() {
1143 if (this.panel.length) {
1144 return this.el.select('.panel-body',true).first();
1151 getAutoCreate : function(){
1154 tag : this.tag || 'div',
1158 if (this.jumbotron) {
1159 cfg.cls = 'jumbotron';
1164 // - this is applied by the parent..
1166 // cfg.cls = this.cls + '';
1169 if (this.sticky.length) {
1171 var bd = Roo.get(document.body);
1172 if (!bd.hasClass('bootstrap-sticky')) {
1173 bd.addClass('bootstrap-sticky');
1174 Roo.select('html',true).setStyle('height', '100%');
1177 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1181 if (this.well.length) {
1182 switch (this.well) {
1185 cfg.cls +=' well well-' +this.well;
1194 cfg.cls += ' hidden';
1198 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199 cfg.cls +=' alert alert-' + this.alert;
1204 if (this.panel.length) {
1205 cfg.cls += ' panel panel-' + this.panel;
1207 if (this.header.length) {
1211 if(this.expandable){
1213 cfg.cls = cfg.cls + ' expandable';
1217 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1225 cls : 'panel-title',
1226 html : (this.expandable ? ' ' : '') + this.header
1230 cls: 'panel-header-right',
1236 cls : 'panel-heading',
1237 style : this.expandable ? 'cursor: pointer' : '',
1245 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1250 if (this.footer.length) {
1252 cls : 'panel-footer',
1261 body.html = this.html || cfg.html;
1262 // prefix with the icons..
1264 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1267 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1272 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273 cfg.cls = 'container';
1279 initEvents: function()
1281 if(this.expandable){
1282 var headerEl = this.headerEl();
1285 headerEl.on('click', this.onToggleClick, this);
1290 this.el.on('click', this.onClick, this);
1295 onToggleClick : function()
1297 var headerEl = this.headerEl();
1313 if(this.fireEvent('expand', this)) {
1315 this.expanded = true;
1317 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1319 this.el.select('.panel-body',true).first().removeClass('hide');
1321 var toggleEl = this.toggleEl();
1327 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1332 collapse : function()
1334 if(this.fireEvent('collapse', this)) {
1336 this.expanded = false;
1338 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339 this.el.select('.panel-body',true).first().addClass('hide');
1341 var toggleEl = this.toggleEl();
1347 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1351 toggleEl : function()
1353 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1357 return this.el.select('.panel-heading .fa',true).first();
1360 headerEl : function()
1362 if(!this.el || !this.panel.length || !this.header.length){
1366 return this.el.select('.panel-heading',true).first()
1371 if(!this.el || !this.panel.length){
1375 return this.el.select('.panel-body',true).first()
1378 titleEl : function()
1380 if(!this.el || !this.panel.length || !this.header.length){
1384 return this.el.select('.panel-title',true).first();
1387 setTitle : function(v)
1389 var titleEl = this.titleEl();
1395 titleEl.dom.innerHTML = v;
1398 getTitle : function()
1401 var titleEl = this.titleEl();
1407 return titleEl.dom.innerHTML;
1410 setRightTitle : function(v)
1412 var t = this.el.select('.panel-header-right',true).first();
1418 t.dom.innerHTML = v;
1421 onClick : function(e)
1425 this.fireEvent('click', this, e);
1438 * @class Roo.bootstrap.Img
1439 * @extends Roo.bootstrap.Component
1440 * Bootstrap Img class
1441 * @cfg {Boolean} imgResponsive false | true
1442 * @cfg {String} border rounded | circle | thumbnail
1443 * @cfg {String} src image source
1444 * @cfg {String} alt image alternative text
1445 * @cfg {String} href a tag href
1446 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447 * @cfg {String} xsUrl xs image source
1448 * @cfg {String} smUrl sm image source
1449 * @cfg {String} mdUrl md image source
1450 * @cfg {String} lgUrl lg image source
1453 * Create a new Input
1454 * @param {Object} config The config object
1457 Roo.bootstrap.Img = function(config){
1458 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1464 * The img click event for the img.
1465 * @param {Roo.EventObject} e
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1473 imgResponsive: true,
1483 getAutoCreate : function()
1485 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486 return this.createSingleImg();
1491 cls: 'roo-image-responsive-group',
1496 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1498 if(!_this[size + 'Url']){
1504 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505 html: _this.html || cfg.html,
1506 src: _this[size + 'Url']
1509 img.cls += ' roo-image-responsive-' + size;
1511 var s = ['xs', 'sm', 'md', 'lg'];
1513 s.splice(s.indexOf(size), 1);
1515 Roo.each(s, function(ss){
1516 img.cls += ' hidden-' + ss;
1519 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520 cfg.cls += ' img-' + _this.border;
1524 cfg.alt = _this.alt;
1537 a.target = _this.target;
1541 cfg.cn.push((_this.href) ? a : img);
1548 createSingleImg : function()
1552 cls: (this.imgResponsive) ? 'img-responsive' : '',
1554 src : 'about:blank' // just incase src get's set to undefined?!?
1557 cfg.html = this.html || cfg.html;
1559 cfg.src = this.src || cfg.src;
1561 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562 cfg.cls += ' img-' + this.border;
1579 a.target = this.target;
1584 return (this.href) ? a : cfg;
1587 initEvents: function()
1590 this.el.on('click', this.onClick, this);
1595 onClick : function(e)
1597 Roo.log('img onclick');
1598 this.fireEvent('click', this, e);
1601 * Sets the url of the image - used to update it
1602 * @param {String} url the url of the image
1605 setSrc : function(url)
1609 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610 this.el.dom.src = url;
1614 this.el.select('img', true).first().dom.src = url;
1630 * @class Roo.bootstrap.Link
1631 * @extends Roo.bootstrap.Component
1632 * Bootstrap Link Class
1633 * @cfg {String} alt image alternative text
1634 * @cfg {String} href a tag href
1635 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636 * @cfg {String} html the content of the link.
1637 * @cfg {String} anchor name for the anchor link
1638 * @cfg {String} fa - favicon
1640 * @cfg {Boolean} preventDefault (true | false) default false
1644 * Create a new Input
1645 * @param {Object} config The config object
1648 Roo.bootstrap.Link = function(config){
1649 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1655 * The img click event for the img.
1656 * @param {Roo.EventObject} e
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1666 preventDefault: false,
1672 getAutoCreate : function()
1674 var html = this.html || '';
1676 if (this.fa !== false) {
1677 html = '<i class="fa fa-' + this.fa + '"></i>';
1682 // anchor's do not require html/href...
1683 if (this.anchor === false) {
1685 cfg.href = this.href || '#';
1687 cfg.name = this.anchor;
1688 if (this.html !== false || this.fa !== false) {
1691 if (this.href !== false) {
1692 cfg.href = this.href;
1696 if(this.alt !== false){
1701 if(this.target !== false) {
1702 cfg.target = this.target;
1708 initEvents: function() {
1710 if(!this.href || this.preventDefault){
1711 this.el.on('click', this.onClick, this);
1715 onClick : function(e)
1717 if(this.preventDefault){
1720 //Roo.log('img onclick');
1721 this.fireEvent('click', this, e);
1734 * @class Roo.bootstrap.Header
1735 * @extends Roo.bootstrap.Component
1736 * Bootstrap Header class
1737 * @cfg {String} html content of header
1738 * @cfg {Number} level (1|2|3|4|5|6) default 1
1741 * Create a new Header
1742 * @param {Object} config The config object
1746 Roo.bootstrap.Header = function(config){
1747 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1758 getAutoCreate : function(){
1763 tag: 'h' + (1 *this.level),
1764 html: this.html || ''
1776 * Ext JS Library 1.1.1
1777 * Copyright(c) 2006-2007, Ext JS, LLC.
1779 * Originally Released Under LGPL - original licence link has changed is not relivant.
1782 * <script type="text/javascript">
1786 * @class Roo.bootstrap.MenuMgr
1787 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1790 Roo.bootstrap.MenuMgr = function(){
1791 var menus, active, groups = {}, attached = false, lastShow = new Date();
1793 // private - called when first menu is created
1796 active = new Roo.util.MixedCollection();
1797 Roo.get(document).addKeyListener(27, function(){
1798 if(active.length > 0){
1806 if(active && active.length > 0){
1807 var c = active.clone();
1817 if(active.length < 1){
1818 Roo.get(document).un("mouseup", onMouseDown);
1826 var last = active.last();
1827 lastShow = new Date();
1830 Roo.get(document).on("mouseup", onMouseDown);
1835 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836 m.parentMenu.activeChild = m;
1837 }else if(last && last.isVisible()){
1838 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1843 function onBeforeHide(m){
1845 m.activeChild.hide();
1847 if(m.autoHideTimer){
1848 clearTimeout(m.autoHideTimer);
1849 delete m.autoHideTimer;
1854 function onBeforeShow(m){
1855 var pm = m.parentMenu;
1856 if(!pm && !m.allowOtherMenus){
1858 }else if(pm && pm.activeChild && active != m){
1859 pm.activeChild.hide();
1863 // private this should really trigger on mouseup..
1864 function onMouseDown(e){
1865 Roo.log("on Mouse Up");
1867 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868 Roo.log("MenuManager hideAll");
1877 function onBeforeCheck(mi, state){
1879 var g = groups[mi.group];
1880 for(var i = 0, l = g.length; i < l; i++){
1882 g[i].setChecked(false);
1891 * Hides all menus that are currently visible
1893 hideAll : function(){
1898 register : function(menu){
1902 menus[menu.id] = menu;
1903 menu.on("beforehide", onBeforeHide);
1904 menu.on("hide", onHide);
1905 menu.on("beforeshow", onBeforeShow);
1906 menu.on("show", onShow);
1908 if(g && menu.events["checkchange"]){
1912 groups[g].push(menu);
1913 menu.on("checkchange", onCheck);
1918 * Returns a {@link Roo.menu.Menu} object
1919 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920 * be used to generate and return a new Menu instance.
1922 get : function(menu){
1923 if(typeof menu == "string"){ // menu id
1925 }else if(menu.events){ // menu instance
1928 /*else if(typeof menu.length == 'number'){ // array of menu items?
1929 return new Roo.bootstrap.Menu({items:menu});
1930 }else{ // otherwise, must be a config
1931 return new Roo.bootstrap.Menu(menu);
1938 unregister : function(menu){
1939 delete menus[menu.id];
1940 menu.un("beforehide", onBeforeHide);
1941 menu.un("hide", onHide);
1942 menu.un("beforeshow", onBeforeShow);
1943 menu.un("show", onShow);
1945 if(g && menu.events["checkchange"]){
1946 groups[g].remove(menu);
1947 menu.un("checkchange", onCheck);
1952 registerCheckable : function(menuItem){
1953 var g = menuItem.group;
1958 groups[g].push(menuItem);
1959 menuItem.on("beforecheckchange", onBeforeCheck);
1964 unregisterCheckable : function(menuItem){
1965 var g = menuItem.group;
1967 groups[g].remove(menuItem);
1968 menuItem.un("beforecheckchange", onBeforeCheck);
1980 * @class Roo.bootstrap.Menu
1981 * @extends Roo.bootstrap.Component
1982 * Bootstrap Menu class - container for MenuItems
1983 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984 * @cfg {bool} hidden if the menu should be hidden when rendered.
1985 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1986 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1990 * @param {Object} config The config object
1994 Roo.bootstrap.Menu = function(config){
1995 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996 if (this.registerMenu && this.type != 'treeview') {
1997 Roo.bootstrap.MenuMgr.register(this);
2002 * Fires before this menu is displayed
2003 * @param {Roo.menu.Menu} this
2008 * Fires before this menu is hidden
2009 * @param {Roo.menu.Menu} this
2014 * Fires after this menu is displayed
2015 * @param {Roo.menu.Menu} this
2020 * Fires after this menu is hidden
2021 * @param {Roo.menu.Menu} this
2026 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029 * @param {Roo.EventObject} e
2034 * Fires when the mouse is hovering over 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 the mouse exits this menu
2043 * @param {Roo.menu.Menu} this
2044 * @param {Roo.EventObject} e
2045 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2050 * Fires when a menu item contained in this menu is clicked
2051 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052 * @param {Roo.EventObject} e
2056 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2063 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2066 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2068 registerMenu : true,
2070 menuItems :false, // stores the menu items..
2080 getChildContainer : function() {
2084 getAutoCreate : function(){
2086 //if (['right'].indexOf(this.align)!==-1) {
2087 // cfg.cn[1].cls += ' pull-right'
2093 cls : 'dropdown-menu' ,
2094 style : 'z-index:1000'
2098 if (this.type === 'submenu') {
2099 cfg.cls = 'submenu active';
2101 if (this.type === 'treeview') {
2102 cfg.cls = 'treeview-menu';
2107 initEvents : function() {
2109 // Roo.log("ADD event");
2110 // Roo.log(this.triggerEl.dom);
2112 this.triggerEl.on('click', this.onTriggerClick, this);
2114 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2116 this.triggerEl.addClass('dropdown-toggle');
2119 this.el.on('touchstart' , this.onTouch, this);
2121 this.el.on('click' , this.onClick, this);
2123 this.el.on("mouseover", this.onMouseOver, this);
2124 this.el.on("mouseout", this.onMouseOut, this);
2128 findTargetItem : function(e)
2130 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2134 //Roo.log(t); Roo.log(t.id);
2136 //Roo.log(this.menuitems);
2137 return this.menuitems.get(t.id);
2139 //return this.items.get(t.menuItemId);
2145 onTouch : function(e)
2147 Roo.log("menu.onTouch");
2148 //e.stopEvent(); this make the user popdown broken
2152 onClick : function(e)
2154 Roo.log("menu.onClick");
2156 var t = this.findTargetItem(e);
2157 if(!t || t.isContainer){
2162 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2163 if(t == this.activeItem && t.shouldDeactivate(e)){
2164 this.activeItem.deactivate();
2165 delete this.activeItem;
2169 this.setActiveItem(t, true);
2177 Roo.log('pass click event');
2181 this.fireEvent("click", this, t, e);
2185 if(!t.href.length || t.href == '#'){
2186 (function() { _this.hide(); }).defer(100);
2191 onMouseOver : function(e){
2192 var t = this.findTargetItem(e);
2195 // if(t.canActivate && !t.disabled){
2196 // this.setActiveItem(t, true);
2200 this.fireEvent("mouseover", this, e, t);
2202 isVisible : function(){
2203 return !this.hidden;
2205 onMouseOut : function(e){
2206 var t = this.findTargetItem(e);
2209 // if(t == this.activeItem && t.shouldDeactivate(e)){
2210 // this.activeItem.deactivate();
2211 // delete this.activeItem;
2214 this.fireEvent("mouseout", this, e, t);
2219 * Displays this menu relative to another element
2220 * @param {String/HTMLElement/Roo.Element} element The element to align to
2221 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222 * the element (defaults to this.defaultAlign)
2223 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2225 show : function(el, pos, parentMenu){
2226 this.parentMenu = parentMenu;
2230 this.fireEvent("beforeshow", this);
2231 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2234 * Displays this menu at a specific xy position
2235 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2238 showAt : function(xy, parentMenu, /* private: */_e){
2239 this.parentMenu = parentMenu;
2244 this.fireEvent("beforeshow", this);
2245 //xy = this.el.adjustForConstraints(xy);
2249 this.hideMenuItems();
2250 this.hidden = false;
2251 this.triggerEl.addClass('open');
2253 // reassign x when hitting right
2254 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2258 // reassign y when hitting bottom
2259 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2263 // but the list may align on trigger left or trigger top... should it be a properity?
2265 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2270 this.fireEvent("show", this);
2276 this.doFocus.defer(50, this);
2280 doFocus : function(){
2282 this.focusEl.focus();
2287 * Hides this menu and optionally all parent menus
2288 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2290 hide : function(deep)
2293 this.hideMenuItems();
2294 if(this.el && this.isVisible()){
2295 this.fireEvent("beforehide", this);
2296 if(this.activeItem){
2297 this.activeItem.deactivate();
2298 this.activeItem = null;
2300 this.triggerEl.removeClass('open');;
2302 this.fireEvent("hide", this);
2304 if(deep === true && this.parentMenu){
2305 this.parentMenu.hide(true);
2309 onTriggerClick : function(e)
2311 Roo.log('trigger click');
2313 var target = e.getTarget();
2315 Roo.log(target.nodeName.toLowerCase());
2317 if(target.nodeName.toLowerCase() === 'i'){
2323 onTriggerPress : function(e)
2325 Roo.log('trigger press');
2326 //Roo.log(e.getTarget());
2327 // Roo.log(this.triggerEl.dom);
2329 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330 var pel = Roo.get(e.getTarget());
2331 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332 Roo.log('is treeview or dropdown?');
2336 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2340 if (this.isVisible()) {
2345 this.show(this.triggerEl, false, false);
2348 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2355 hideMenuItems : function()
2357 Roo.log("hide Menu Items");
2361 //$(backdrop).remove()
2362 this.el.select('.open',true).each(function(aa) {
2364 aa.removeClass('open');
2365 //var parent = getParent($(this))
2366 //var relatedTarget = { relatedTarget: this }
2368 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369 //if (e.isDefaultPrevented()) return
2370 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2373 addxtypeChild : function (tree, cntr) {
2374 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2376 this.menuitems.add(comp);
2388 this.getEl().dom.innerHTML = '';
2389 this.menuitems.clear();
2403 * @class Roo.bootstrap.MenuItem
2404 * @extends Roo.bootstrap.Component
2405 * Bootstrap MenuItem class
2406 * @cfg {String} html the menu label
2407 * @cfg {String} href the link
2408 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410 * @cfg {Boolean} active used on sidebars to highlight active itesm
2411 * @cfg {String} fa favicon to show on left of menu item.
2412 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2416 * Create a new MenuItem
2417 * @param {Object} config The config object
2421 Roo.bootstrap.MenuItem = function(config){
2422 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2427 * The raw click event for the entire grid.
2428 * @param {Roo.bootstrap.MenuItem} this
2429 * @param {Roo.EventObject} e
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2439 preventDefault: false,
2440 isContainer : false,
2444 getAutoCreate : function(){
2446 if(this.isContainer){
2449 cls: 'dropdown-menu-item'
2463 if (this.fa !== false) {
2466 cls : 'fa fa-' + this.fa
2475 cls: 'dropdown-menu-item',
2478 if (this.parent().type == 'treeview') {
2479 cfg.cls = 'treeview-menu';
2482 cfg.cls += ' active';
2487 anc.href = this.href || cfg.cn[0].href ;
2488 ctag.html = this.html || cfg.cn[0].html ;
2492 initEvents: function()
2494 if (this.parent().type == 'treeview') {
2495 this.el.select('a').on('click', this.onClick, this);
2499 this.menu.parentType = this.xtype;
2500 this.menu.triggerEl = this.el;
2501 this.menu = this.addxtype(Roo.apply({}, this.menu));
2505 onClick : function(e)
2507 Roo.log('item on click ');
2509 if(this.preventDefault){
2512 //this.parent().hideMenuItems();
2514 this.fireEvent('click', this, e);
2533 * @class Roo.bootstrap.MenuSeparator
2534 * @extends Roo.bootstrap.Component
2535 * Bootstrap MenuSeparator class
2538 * Create a new MenuItem
2539 * @param {Object} config The config object
2543 Roo.bootstrap.MenuSeparator = function(config){
2544 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2549 getAutoCreate : function(){
2568 * @class Roo.bootstrap.Modal
2569 * @extends Roo.bootstrap.Component
2570 * Bootstrap Modal class
2571 * @cfg {String} title Title of dialog
2572 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2574 * @cfg {Boolean} specificTitle default false
2575 * @cfg {Array} buttons Array of buttons or standard button set..
2576 * @cfg {String} buttonPosition (left|right|center) default right
2577 * @cfg {Boolean} animate default true
2578 * @cfg {Boolean} allow_close default true
2579 * @cfg {Boolean} fitwindow default false
2580 * @cfg {String} size (sm|lg) default empty
2581 * @cfg {Number} max_width set the max width of modal
2585 * Create a new Modal Dialog
2586 * @param {Object} config The config object
2589 Roo.bootstrap.Modal = function(config){
2590 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2595 * The raw btnclick event for the button
2596 * @param {Roo.EventObject} e
2601 * Fire when dialog resize
2602 * @param {Roo.bootstrap.Modal} this
2603 * @param {Roo.EventObject} e
2607 this.buttons = this.buttons || [];
2610 this.tmpl = Roo.factory(this.tmpl);
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2617 title : 'test dialog',
2627 specificTitle: false,
2629 buttonPosition: 'right',
2650 onRender : function(ct, position)
2652 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2655 var cfg = Roo.apply({}, this.getAutoCreate());
2658 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2660 //if (!cfg.name.length) {
2664 cfg.cls += ' ' + this.cls;
2667 cfg.style = this.style;
2669 this.el = Roo.get(document.body).createChild(cfg, position);
2671 //var type = this.el.dom.type;
2674 if(this.tabIndex !== undefined){
2675 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2678 this.dialogEl = this.el.select('.modal-dialog',true).first();
2679 this.bodyEl = this.el.select('.modal-body',true).first();
2680 this.closeEl = this.el.select('.modal-header .close', true).first();
2681 this.headerEl = this.el.select('.modal-header',true).first();
2682 this.titleEl = this.el.select('.modal-title',true).first();
2683 this.footerEl = this.el.select('.modal-footer',true).first();
2685 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2687 //this.el.addClass("x-dlg-modal");
2689 if (this.buttons.length) {
2690 Roo.each(this.buttons, function(bb) {
2691 var b = Roo.apply({}, bb);
2692 b.xns = b.xns || Roo.bootstrap;
2693 b.xtype = b.xtype || 'Button';
2694 if (typeof(b.listeners) == 'undefined') {
2695 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2698 var btn = Roo.factory(b);
2700 btn.render(this.el.select('.modal-footer div').first());
2704 // render the children.
2707 if(typeof(this.items) != 'undefined'){
2708 var items = this.items;
2711 for(var i =0;i < items.length;i++) {
2712 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2716 this.items = nitems;
2718 // where are these used - they used to be body/close/footer
2722 //this.el.addClass([this.fieldClass, this.cls]);
2726 getAutoCreate : function()
2730 html : this.html || ''
2735 cls : 'modal-title',
2739 if(this.specificTitle){
2745 if (this.allow_close) {
2757 if(this.size.length){
2758 size = 'modal-' + this.size;
2765 cls: "modal-dialog " + size,
2768 cls : "modal-content",
2771 cls : 'modal-header',
2776 cls : 'modal-footer',
2780 cls: 'btn-' + this.buttonPosition
2797 modal.cls += ' fade';
2803 getChildContainer : function() {
2808 getButtonContainer : function() {
2809 return this.el.select('.modal-footer div',true).first();
2812 initEvents : function()
2814 if (this.allow_close) {
2815 this.closeEl.on('click', this.hide, this);
2817 Roo.EventManager.onWindowResize(this.resize, this, true);
2824 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2826 if (this.fitwindow) {
2827 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2828 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2832 if(!this.fitwindow && this.max_width !== 0){
2833 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2834 // var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2835 this.setSize(w, this.height || Roo.lib.Dom.getViewportHeight(true) - 60);
2836 Roo.log(this.bodyEl.dom.offsetHeight);
2837 var view_height = Roo.lib.Dom.getViewportHeight(true) - 60;
2841 // this.headerEl.getHeight() +
2842 // this.bodyEl.getHeight() +
2843 // this.footerEl.getHeight()
2844 // ) > view_height) {
2846 // this.setSize(w,view_height);
2852 setSize : function(w,h)
2862 if (!this.rendered) {
2866 //this.el.setStyle('display', 'block');
2867 this.el.removeClass('hideing');
2868 this.el.addClass('show');
2870 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2873 this.el.addClass('in');
2876 this.el.addClass('in');
2879 // not sure how we can show data in here..
2881 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2884 Roo.get(document.body).addClass("x-body-masked");
2886 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2887 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2888 this.maskEl.addClass('show');
2892 this.fireEvent('show', this);
2894 // set zindex here - otherwise it appears to be ignored...
2895 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2898 this.items.forEach( function(e) {
2899 e.layout ? e.layout() : false;
2907 if(this.fireEvent("beforehide", this) !== false){
2908 this.maskEl.removeClass('show');
2909 Roo.get(document.body).removeClass("x-body-masked");
2910 this.el.removeClass('in');
2911 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2913 if(this.animate){ // why
2914 this.el.addClass('hideing');
2916 if (!this.el.hasClass('hideing')) {
2917 return; // it's been shown again...
2919 this.el.removeClass('show');
2920 this.el.removeClass('hideing');
2924 this.el.removeClass('show');
2926 this.fireEvent('hide', this);
2929 isVisible : function()
2932 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2936 addButton : function(str, cb)
2940 var b = Roo.apply({}, { html : str } );
2941 b.xns = b.xns || Roo.bootstrap;
2942 b.xtype = b.xtype || 'Button';
2943 if (typeof(b.listeners) == 'undefined') {
2944 b.listeners = { click : cb.createDelegate(this) };
2947 var btn = Roo.factory(b);
2949 btn.render(this.el.select('.modal-footer div').first());
2955 setDefaultButton : function(btn)
2957 //this.el.select('.modal-footer').()
2961 resizeTo: function(w,h)
2965 this.dialogEl.setWidth(w);
2966 if (this.diff === false) {
2967 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2970 this.bodyEl.setHeight(h-this.diff);
2972 this.fireEvent('resize', this);
2975 setContentSize : function(w, h)
2979 onButtonClick: function(btn,e)
2982 this.fireEvent('btnclick', btn.name, e);
2985 * Set the title of the Dialog
2986 * @param {String} str new Title
2988 setTitle: function(str) {
2989 this.titleEl.dom.innerHTML = str;
2992 * Set the body of the Dialog
2993 * @param {String} str new Title
2995 setBody: function(str) {
2996 this.bodyEl.dom.innerHTML = str;
2999 * Set the body of the Dialog using the template
3000 * @param {Obj} data - apply this data to the template and replace the body contents.
3002 applyBody: function(obj)
3005 Roo.log("Error - using apply Body without a template");
3008 this.tmpl.overwrite(this.bodyEl, obj);
3014 Roo.apply(Roo.bootstrap.Modal, {
3016 * Button config that displays a single OK button
3025 * Button config that displays Yes and No buttons
3041 * Button config that displays OK and Cancel buttons
3056 * Button config that displays Yes, No and Cancel buttons
3080 * messagebox - can be used as a replace
3084 * @class Roo.MessageBox
3085 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3089 Roo.Msg.alert('Status', 'Changes saved successfully.');
3091 // Prompt for user data:
3092 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3094 // process text value...
3098 // Show a dialog using config options:
3100 title:'Save Changes?',
3101 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3102 buttons: Roo.Msg.YESNOCANCEL,
3109 Roo.bootstrap.MessageBox = function(){
3110 var dlg, opt, mask, waitTimer;
3111 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3112 var buttons, activeTextEl, bwidth;
3116 var handleButton = function(button){
3118 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3122 var handleHide = function(){
3124 dlg.el.removeClass(opt.cls);
3127 // Roo.TaskMgr.stop(waitTimer);
3128 // waitTimer = null;
3133 var updateButtons = function(b){
3136 buttons["ok"].hide();
3137 buttons["cancel"].hide();
3138 buttons["yes"].hide();
3139 buttons["no"].hide();
3140 //dlg.footer.dom.style.display = 'none';
3143 dlg.footerEl.dom.style.display = '';
3144 for(var k in buttons){
3145 if(typeof buttons[k] != "function"){
3148 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3149 width += buttons[k].el.getWidth()+15;
3159 var handleEsc = function(d, k, e){
3160 if(opt && opt.closable !== false){
3170 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3171 * @return {Roo.BasicDialog} The BasicDialog element
3173 getDialog : function(){
3175 dlg = new Roo.bootstrap.Modal( {
3178 //constraintoviewport:false,
3180 //collapsible : false,
3185 //buttonAlign:"center",
3186 closeClick : function(){
3187 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3190 handleButton("cancel");
3195 dlg.on("hide", handleHide);
3197 //dlg.addKeyListener(27, handleEsc);
3199 this.buttons = buttons;
3200 var bt = this.buttonText;
3201 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3202 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3203 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3204 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3206 bodyEl = dlg.bodyEl.createChild({
3208 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3209 '<textarea class="roo-mb-textarea"></textarea>' +
3210 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3212 msgEl = bodyEl.dom.firstChild;
3213 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3214 textboxEl.enableDisplayMode();
3215 textboxEl.addKeyListener([10,13], function(){
3216 if(dlg.isVisible() && opt && opt.buttons){
3219 }else if(opt.buttons.yes){
3220 handleButton("yes");
3224 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3225 textareaEl.enableDisplayMode();
3226 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3227 progressEl.enableDisplayMode();
3229 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3230 var pf = progressEl.dom.firstChild;
3232 pp = Roo.get(pf.firstChild);
3233 pp.setHeight(pf.offsetHeight);
3241 * Updates the message box body text
3242 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3243 * the XHTML-compliant non-breaking space character '&#160;')
3244 * @return {Roo.MessageBox} This message box
3246 updateText : function(text)
3248 if(!dlg.isVisible() && !opt.width){
3249 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3250 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3252 msgEl.innerHTML = text || ' ';
3254 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3255 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3257 Math.min(opt.width || cw , this.maxWidth),
3258 Math.max(opt.minWidth || this.minWidth, bwidth)
3261 activeTextEl.setWidth(w);
3263 if(dlg.isVisible()){
3264 dlg.fixedcenter = false;
3266 // to big, make it scroll. = But as usual stupid IE does not support
3269 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3270 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3271 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3273 bodyEl.dom.style.height = '';
3274 bodyEl.dom.style.overflowY = '';
3277 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3279 bodyEl.dom.style.overflowX = '';
3282 dlg.setContentSize(w, bodyEl.getHeight());
3283 if(dlg.isVisible()){
3284 dlg.fixedcenter = true;
3290 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3291 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3292 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3293 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3294 * @return {Roo.MessageBox} This message box
3296 updateProgress : function(value, text){
3298 this.updateText(text);
3301 if (pp) { // weird bug on my firefox - for some reason this is not defined
3302 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3303 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3309 * Returns true if the message box is currently displayed
3310 * @return {Boolean} True if the message box is visible, else false
3312 isVisible : function(){
3313 return dlg && dlg.isVisible();
3317 * Hides the message box if it is displayed
3320 if(this.isVisible()){
3326 * Displays a new message box, or reinitializes an existing message box, based on the config options
3327 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3328 * The following config object properties are supported:
3330 Property Type Description
3331 ---------- --------------- ------------------------------------------------------------------------------------
3332 animEl String/Element An id or Element from which the message box should animate as it opens and
3333 closes (defaults to undefined)
3334 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3335 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3336 closable Boolean False to hide the top-right close button (defaults to true). Note that
3337 progress and wait dialogs will ignore this property and always hide the
3338 close button as they can only be closed programmatically.
3339 cls String A custom CSS class to apply to the message box element
3340 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3341 displayed (defaults to 75)
3342 fn Function A callback function to execute after closing the dialog. The arguments to the
3343 function will be btn (the name of the button that was clicked, if applicable,
3344 e.g. "ok"), and text (the value of the active text field, if applicable).
3345 Progress and wait dialogs will ignore this option since they do not respond to
3346 user actions and can only be closed programmatically, so any required function
3347 should be called by the same code after it closes the dialog.
3348 icon String A CSS class that provides a background image to be used as an icon for
3349 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3350 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3351 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3352 modal Boolean False to allow user interaction with the page while the message box is
3353 displayed (defaults to true)
3354 msg String A string that will replace the existing message box body text (defaults
3355 to the XHTML-compliant non-breaking space character ' ')
3356 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3357 progress Boolean True to display a progress bar (defaults to false)
3358 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3359 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3360 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3361 title String The title text
3362 value String The string value to set into the active textbox element if displayed
3363 wait Boolean True to display a progress bar (defaults to false)
3364 width Number The width of the dialog in pixels
3371 msg: 'Please enter your address:',
3373 buttons: Roo.MessageBox.OKCANCEL,
3376 animEl: 'addAddressBtn'
3379 * @param {Object} config Configuration options
3380 * @return {Roo.MessageBox} This message box
3382 show : function(options)
3385 // this causes nightmares if you show one dialog after another
3386 // especially on callbacks..
3388 if(this.isVisible()){
3391 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3392 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3393 Roo.log("New Dialog Message:" + options.msg )
3394 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3395 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3398 var d = this.getDialog();
3400 d.setTitle(opt.title || " ");
3401 d.closeEl.setDisplayed(opt.closable !== false);
3402 activeTextEl = textboxEl;
3403 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3408 textareaEl.setHeight(typeof opt.multiline == "number" ?
3409 opt.multiline : this.defaultTextHeight);
3410 activeTextEl = textareaEl;
3419 progressEl.setDisplayed(opt.progress === true);
3420 this.updateProgress(0);
3421 activeTextEl.dom.value = opt.value || "";
3423 dlg.setDefaultButton(activeTextEl);
3425 var bs = opt.buttons;
3429 }else if(bs && bs.yes){
3430 db = buttons["yes"];
3432 dlg.setDefaultButton(db);
3434 bwidth = updateButtons(opt.buttons);
3435 this.updateText(opt.msg);
3437 d.el.addClass(opt.cls);
3439 d.proxyDrag = opt.proxyDrag === true;
3440 d.modal = opt.modal !== false;
3441 d.mask = opt.modal !== false ? mask : false;
3443 // force it to the end of the z-index stack so it gets a cursor in FF
3444 document.body.appendChild(dlg.el.dom);
3445 d.animateTarget = null;
3446 d.show(options.animEl);
3452 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3453 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3454 * and closing the message box when the process is complete.
3455 * @param {String} title The title bar text
3456 * @param {String} msg The message box body text
3457 * @return {Roo.MessageBox} This message box
3459 progress : function(title, msg){
3466 minWidth: this.minProgressWidth,
3473 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3474 * If a callback function is passed it will be called after the user clicks the button, and the
3475 * id of the button that was clicked will be passed as the only parameter to the callback
3476 * (could also be the top-right close button).
3477 * @param {String} title The title bar text
3478 * @param {String} msg The message box body text
3479 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3480 * @param {Object} scope (optional) The scope of the callback function
3481 * @return {Roo.MessageBox} This message box
3483 alert : function(title, msg, fn, scope)
3498 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3499 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3500 * You are responsible for closing the message box when the process is complete.
3501 * @param {String} msg The message box body text
3502 * @param {String} title (optional) The title bar text
3503 * @return {Roo.MessageBox} This message box
3505 wait : function(msg, title){
3516 waitTimer = Roo.TaskMgr.start({
3518 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3526 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3527 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3528 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3529 * @param {String} title The title bar text
3530 * @param {String} msg The message box body text
3531 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3532 * @param {Object} scope (optional) The scope of the callback function
3533 * @return {Roo.MessageBox} This message box
3535 confirm : function(title, msg, fn, scope){
3539 buttons: this.YESNO,
3548 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3549 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3550 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3551 * (could also be the top-right close button) and the text that was entered will be passed as the two
3552 * parameters to the callback.
3553 * @param {String} title The title bar text
3554 * @param {String} msg The message box body text
3555 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3556 * @param {Object} scope (optional) The scope of the callback function
3557 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3558 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3559 * @return {Roo.MessageBox} This message box
3561 prompt : function(title, msg, fn, scope, multiline){
3565 buttons: this.OKCANCEL,
3570 multiline: multiline,
3577 * Button config that displays a single OK button
3582 * Button config that displays Yes and No buttons
3585 YESNO : {yes:true, no:true},
3587 * Button config that displays OK and Cancel buttons
3590 OKCANCEL : {ok:true, cancel:true},
3592 * Button config that displays Yes, No and Cancel buttons
3595 YESNOCANCEL : {yes:true, no:true, cancel:true},
3598 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3601 defaultTextHeight : 75,
3603 * The maximum width in pixels of the message box (defaults to 600)
3608 * The minimum width in pixels of the message box (defaults to 100)
3613 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3614 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3617 minProgressWidth : 250,
3619 * An object containing the default button text strings that can be overriden for localized language support.
3620 * Supported properties are: ok, cancel, yes and no.
3621 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3634 * Shorthand for {@link Roo.MessageBox}
3636 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3637 Roo.Msg = Roo.Msg || Roo.MessageBox;
3646 * @class Roo.bootstrap.Navbar
3647 * @extends Roo.bootstrap.Component
3648 * Bootstrap Navbar class
3651 * Create a new Navbar
3652 * @param {Object} config The config object
3656 Roo.bootstrap.Navbar = function(config){
3657 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3661 * @event beforetoggle
3662 * Fire before toggle the menu
3663 * @param {Roo.EventObject} e
3665 "beforetoggle" : true
3669 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3678 getAutoCreate : function(){
3681 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3685 initEvents :function ()
3687 //Roo.log(this.el.select('.navbar-toggle',true));
3688 this.el.select('.navbar-toggle',true).on('click', function() {
3689 if(this.fireEvent('beforetoggle', this) !== false){
3690 this.el.select('.navbar-collapse',true).toggleClass('in');
3700 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3702 var size = this.el.getSize();
3703 this.maskEl.setSize(size.width, size.height);
3704 this.maskEl.enableDisplayMode("block");
3713 getChildContainer : function()
3715 if (this.el.select('.collapse').getCount()) {
3716 return this.el.select('.collapse',true).first();
3749 * @class Roo.bootstrap.NavSimplebar
3750 * @extends Roo.bootstrap.Navbar
3751 * Bootstrap Sidebar class
3753 * @cfg {Boolean} inverse is inverted color
3755 * @cfg {String} type (nav | pills | tabs)
3756 * @cfg {Boolean} arrangement stacked | justified
3757 * @cfg {String} align (left | right) alignment
3759 * @cfg {Boolean} main (true|false) main nav bar? default false
3760 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3762 * @cfg {String} tag (header|footer|nav|div) default is nav
3768 * Create a new Sidebar
3769 * @param {Object} config The config object
3773 Roo.bootstrap.NavSimplebar = function(config){
3774 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3777 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3793 getAutoCreate : function(){
3797 tag : this.tag || 'div',
3810 this.type = this.type || 'nav';
3811 if (['tabs','pills'].indexOf(this.type)!==-1) {
3812 cfg.cn[0].cls += ' nav-' + this.type
3816 if (this.type!=='nav') {
3817 Roo.log('nav type must be nav/tabs/pills')
3819 cfg.cn[0].cls += ' navbar-nav'
3825 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3826 cfg.cn[0].cls += ' nav-' + this.arrangement;
3830 if (this.align === 'right') {
3831 cfg.cn[0].cls += ' navbar-right';
3835 cfg.cls += ' navbar-inverse';
3862 * @class Roo.bootstrap.NavHeaderbar
3863 * @extends Roo.bootstrap.NavSimplebar
3864 * Bootstrap Sidebar class
3866 * @cfg {String} brand what is brand
3867 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3868 * @cfg {String} brand_href href of the brand
3869 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3870 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3871 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3872 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3875 * Create a new Sidebar
3876 * @param {Object} config The config object
3880 Roo.bootstrap.NavHeaderbar = function(config){
3881 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3885 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3892 desktopCenter : false,
3895 getAutoCreate : function(){
3898 tag: this.nav || 'nav',
3905 if (this.desktopCenter) {
3906 cn.push({cls : 'container', cn : []});
3913 cls: 'navbar-header',
3918 cls: 'navbar-toggle',
3919 'data-toggle': 'collapse',
3924 html: 'Toggle navigation'
3946 cls: 'collapse navbar-collapse',
3950 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3952 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3953 cfg.cls += ' navbar-' + this.position;
3955 // tag can override this..
3957 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3960 if (this.brand !== '') {
3963 href: this.brand_href ? this.brand_href : '#',
3964 cls: 'navbar-brand',
3972 cfg.cls += ' main-nav';
3980 getHeaderChildContainer : function()
3982 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3983 return this.el.select('.navbar-header',true).first();
3986 return this.getChildContainer();
3990 initEvents : function()
3992 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3994 if (this.autohide) {
3999 Roo.get(document).on('scroll',function(e) {
4000 var ns = Roo.get(document).getScroll().top;
4001 var os = prevScroll;
4005 ft.removeClass('slideDown');
4006 ft.addClass('slideUp');
4009 ft.removeClass('slideUp');
4010 ft.addClass('slideDown');
4031 * @class Roo.bootstrap.NavSidebar
4032 * @extends Roo.bootstrap.Navbar
4033 * Bootstrap Sidebar class
4036 * Create a new Sidebar
4037 * @param {Object} config The config object
4041 Roo.bootstrap.NavSidebar = function(config){
4042 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4045 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4047 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4049 getAutoCreate : function(){
4054 cls: 'sidebar sidebar-nav'
4076 * @class Roo.bootstrap.NavGroup
4077 * @extends Roo.bootstrap.Component
4078 * Bootstrap NavGroup class
4079 * @cfg {String} align (left|right)
4080 * @cfg {Boolean} inverse
4081 * @cfg {String} type (nav|pills|tab) default nav
4082 * @cfg {String} navId - reference Id for navbar.
4086 * Create a new nav group
4087 * @param {Object} config The config object
4090 Roo.bootstrap.NavGroup = function(config){
4091 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4094 Roo.bootstrap.NavGroup.register(this);
4098 * Fires when the active item changes
4099 * @param {Roo.bootstrap.NavGroup} this
4100 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4101 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4108 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4119 getAutoCreate : function()
4121 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4128 if (['tabs','pills'].indexOf(this.type)!==-1) {
4129 cfg.cls += ' nav-' + this.type
4131 if (this.type!=='nav') {
4132 Roo.log('nav type must be nav/tabs/pills')
4134 cfg.cls += ' navbar-nav'
4137 if (this.parent() && this.parent().sidebar) {
4140 cls: 'dashboard-menu sidebar-menu'
4146 if (this.form === true) {
4152 if (this.align === 'right') {
4153 cfg.cls += ' navbar-right';
4155 cfg.cls += ' navbar-left';
4159 if (this.align === 'right') {
4160 cfg.cls += ' navbar-right';
4164 cfg.cls += ' navbar-inverse';
4172 * sets the active Navigation item
4173 * @param {Roo.bootstrap.NavItem} the new current navitem
4175 setActiveItem : function(item)
4178 Roo.each(this.navItems, function(v){
4183 v.setActive(false, true);
4190 item.setActive(true, true);
4191 this.fireEvent('changed', this, item, prev);
4196 * gets the active Navigation item
4197 * @return {Roo.bootstrap.NavItem} the current navitem
4199 getActive : function()
4203 Roo.each(this.navItems, function(v){
4214 indexOfNav : function()
4218 Roo.each(this.navItems, function(v,i){
4229 * adds a Navigation item
4230 * @param {Roo.bootstrap.NavItem} the navitem to add
4232 addItem : function(cfg)
4234 var cn = new Roo.bootstrap.NavItem(cfg);
4236 cn.parentId = this.id;
4237 cn.onRender(this.el, null);
4241 * register a Navigation item
4242 * @param {Roo.bootstrap.NavItem} the navitem to add
4244 register : function(item)
4246 this.navItems.push( item);
4247 item.navId = this.navId;
4252 * clear all the Navigation item
4255 clearAll : function()
4258 this.el.dom.innerHTML = '';
4261 getNavItem: function(tabId)
4264 Roo.each(this.navItems, function(e) {
4265 if (e.tabId == tabId) {
4275 setActiveNext : function()
4277 var i = this.indexOfNav(this.getActive());
4278 if (i > this.navItems.length) {
4281 this.setActiveItem(this.navItems[i+1]);
4283 setActivePrev : function()
4285 var i = this.indexOfNav(this.getActive());
4289 this.setActiveItem(this.navItems[i-1]);
4291 clearWasActive : function(except) {
4292 Roo.each(this.navItems, function(e) {
4293 if (e.tabId != except.tabId && e.was_active) {
4294 e.was_active = false;
4301 getWasActive : function ()
4304 Roo.each(this.navItems, function(e) {
4319 Roo.apply(Roo.bootstrap.NavGroup, {
4323 * register a Navigation Group
4324 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4326 register : function(navgrp)
4328 this.groups[navgrp.navId] = navgrp;
4332 * fetch a Navigation Group based on the navigation ID
4333 * @param {string} the navgroup to add
4334 * @returns {Roo.bootstrap.NavGroup} the navgroup
4336 get: function(navId) {
4337 if (typeof(this.groups[navId]) == 'undefined') {
4339 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4341 return this.groups[navId] ;
4356 * @class Roo.bootstrap.NavItem
4357 * @extends Roo.bootstrap.Component
4358 * Bootstrap Navbar.NavItem class
4359 * @cfg {String} href link to
4360 * @cfg {String} html content of button
4361 * @cfg {String} badge text inside badge
4362 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4363 * @cfg {String} glyphicon name of glyphicon
4364 * @cfg {String} icon name of font awesome icon
4365 * @cfg {Boolean} active Is item active
4366 * @cfg {Boolean} disabled Is item disabled
4368 * @cfg {Boolean} preventDefault (true | false) default false
4369 * @cfg {String} tabId the tab that this item activates.
4370 * @cfg {String} tagtype (a|span) render as a href or span?
4371 * @cfg {Boolean} animateRef (true|false) link to element default false
4374 * Create a new Navbar Item
4375 * @param {Object} config The config object
4377 Roo.bootstrap.NavItem = function(config){
4378 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4383 * The raw click event for the entire grid.
4384 * @param {Roo.EventObject} e
4389 * Fires when the active item active state changes
4390 * @param {Roo.bootstrap.NavItem} this
4391 * @param {boolean} state the new state
4397 * Fires when scroll to element
4398 * @param {Roo.bootstrap.NavItem} this
4399 * @param {Object} options
4400 * @param {Roo.EventObject} e
4408 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4416 preventDefault : false,
4423 getAutoCreate : function(){
4432 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4434 if (this.disabled) {
4435 cfg.cls += ' disabled';
4438 if (this.href || this.html || this.glyphicon || this.icon) {
4442 href : this.href || "#",
4443 html: this.html || ''
4448 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4451 if(this.glyphicon) {
4452 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4457 cfg.cn[0].html += " <span class='caret'></span>";
4461 if (this.badge !== '') {
4463 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4471 initEvents: function()
4473 if (typeof (this.menu) != 'undefined') {
4474 this.menu.parentType = this.xtype;
4475 this.menu.triggerEl = this.el;
4476 this.menu = this.addxtype(Roo.apply({}, this.menu));
4479 this.el.select('a',true).on('click', this.onClick, this);
4481 if(this.tagtype == 'span'){
4482 this.el.select('span',true).on('click', this.onClick, this);
4485 // at this point parent should be available..
4486 this.parent().register(this);
4489 onClick : function(e)
4491 if (e.getTarget('.dropdown-menu-item')) {
4492 // did you click on a menu itemm.... - then don't trigger onclick..
4497 this.preventDefault ||
4500 Roo.log("NavItem - prevent Default?");
4504 if (this.disabled) {
4508 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4509 if (tg && tg.transition) {
4510 Roo.log("waiting for the transitionend");
4516 //Roo.log("fire event clicked");
4517 if(this.fireEvent('click', this, e) === false){
4521 if(this.tagtype == 'span'){
4525 //Roo.log(this.href);
4526 var ael = this.el.select('a',true).first();
4529 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4530 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4531 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4532 return; // ignore... - it's a 'hash' to another page.
4534 Roo.log("NavItem - prevent Default?");
4536 this.scrollToElement(e);
4540 var p = this.parent();
4542 if (['tabs','pills'].indexOf(p.type)!==-1) {
4543 if (typeof(p.setActiveItem) !== 'undefined') {
4544 p.setActiveItem(this);
4548 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4549 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4550 // remove the collapsed menu expand...
4551 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4555 isActive: function () {
4558 setActive : function(state, fire, is_was_active)
4560 if (this.active && !state && this.navId) {
4561 this.was_active = true;
4562 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4564 nv.clearWasActive(this);
4568 this.active = state;
4571 this.el.removeClass('active');
4572 } else if (!this.el.hasClass('active')) {
4573 this.el.addClass('active');
4576 this.fireEvent('changed', this, state);
4579 // show a panel if it's registered and related..
4581 if (!this.navId || !this.tabId || !state || is_was_active) {
4585 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4589 var pan = tg.getPanelByName(this.tabId);
4593 // if we can not flip to new panel - go back to old nav highlight..
4594 if (false == tg.showPanel(pan)) {
4595 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4597 var onav = nv.getWasActive();
4599 onav.setActive(true, false, true);
4608 // this should not be here...
4609 setDisabled : function(state)
4611 this.disabled = state;
4613 this.el.removeClass('disabled');
4614 } else if (!this.el.hasClass('disabled')) {
4615 this.el.addClass('disabled');
4621 * Fetch the element to display the tooltip on.
4622 * @return {Roo.Element} defaults to this.el
4624 tooltipEl : function()
4626 return this.el.select('' + this.tagtype + '', true).first();
4629 scrollToElement : function(e)
4631 var c = document.body;
4634 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4636 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4637 c = document.documentElement;
4640 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4646 var o = target.calcOffsetsTo(c);
4653 this.fireEvent('scrollto', this, options, e);
4655 Roo.get(c).scrollTo('top', options.value, true);
4668 * <span> icon </span>
4669 * <span> text </span>
4670 * <span>badge </span>
4674 * @class Roo.bootstrap.NavSidebarItem
4675 * @extends Roo.bootstrap.NavItem
4676 * Bootstrap Navbar.NavSidebarItem class
4677 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4678 * {Boolean} open is the menu open
4679 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4680 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4681 * {String} buttonSize (sm|md|lg)the extra classes for the button
4682 * {Boolean} showArrow show arrow next to the text (default true)
4684 * Create a new Navbar Button
4685 * @param {Object} config The config object
4687 Roo.bootstrap.NavSidebarItem = function(config){
4688 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4693 * The raw click event for the entire grid.
4694 * @param {Roo.EventObject} e
4699 * Fires when the active item active state changes
4700 * @param {Roo.bootstrap.NavSidebarItem} this
4701 * @param {boolean} state the new state
4709 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4711 badgeWeight : 'default',
4717 buttonWeight : 'default',
4723 getAutoCreate : function(){
4728 href : this.href || '#',
4734 if(this.buttonView){
4737 href : this.href || '#',
4738 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4751 cfg.cls += ' active';
4754 if (this.disabled) {
4755 cfg.cls += ' disabled';
4758 cfg.cls += ' open x-open';
4761 if (this.glyphicon || this.icon) {
4762 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4763 a.cn.push({ tag : 'i', cls : c }) ;
4766 if(!this.buttonView){
4769 html : this.html || ''
4776 if (this.badge !== '') {
4777 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4783 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4786 a.cls += ' dropdown-toggle treeview' ;
4792 initEvents : function()
4794 if (typeof (this.menu) != 'undefined') {
4795 this.menu.parentType = this.xtype;
4796 this.menu.triggerEl = this.el;
4797 this.menu = this.addxtype(Roo.apply({}, this.menu));
4800 this.el.on('click', this.onClick, this);
4802 if(this.badge !== ''){
4803 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4808 onClick : function(e)
4815 if(this.preventDefault){
4819 this.fireEvent('click', this);
4822 disable : function()
4824 this.setDisabled(true);
4829 this.setDisabled(false);
4832 setDisabled : function(state)
4834 if(this.disabled == state){
4838 this.disabled = state;
4841 this.el.addClass('disabled');
4845 this.el.removeClass('disabled');
4850 setActive : function(state)
4852 if(this.active == state){
4856 this.active = state;
4859 this.el.addClass('active');
4863 this.el.removeClass('active');
4868 isActive: function ()
4873 setBadge : function(str)
4879 this.badgeEl.dom.innerHTML = str;
4896 * @class Roo.bootstrap.Row
4897 * @extends Roo.bootstrap.Component
4898 * Bootstrap Row class (contains columns...)
4902 * @param {Object} config The config object
4905 Roo.bootstrap.Row = function(config){
4906 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4909 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4911 getAutoCreate : function(){
4930 * @class Roo.bootstrap.Element
4931 * @extends Roo.bootstrap.Component
4932 * Bootstrap Element class
4933 * @cfg {String} html contents of the element
4934 * @cfg {String} tag tag of the element
4935 * @cfg {String} cls class of the element
4936 * @cfg {Boolean} preventDefault (true|false) default false
4937 * @cfg {Boolean} clickable (true|false) default false
4940 * Create a new Element
4941 * @param {Object} config The config object
4944 Roo.bootstrap.Element = function(config){
4945 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4951 * When a element is chick
4952 * @param {Roo.bootstrap.Element} this
4953 * @param {Roo.EventObject} e
4959 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4964 preventDefault: false,
4967 getAutoCreate : function(){
4971 // cls: this.cls, double assign in parent class Component.js :: onRender
4978 initEvents: function()
4980 Roo.bootstrap.Element.superclass.initEvents.call(this);
4983 this.el.on('click', this.onClick, this);
4988 onClick : function(e)
4990 if(this.preventDefault){
4994 this.fireEvent('click', this, e);
4997 getValue : function()
4999 return this.el.dom.innerHTML;
5002 setValue : function(value)
5004 this.el.dom.innerHTML = value;
5019 * @class Roo.bootstrap.Pagination
5020 * @extends Roo.bootstrap.Component
5021 * Bootstrap Pagination class
5022 * @cfg {String} size xs | sm | md | lg
5023 * @cfg {Boolean} inverse false | true
5026 * Create a new Pagination
5027 * @param {Object} config The config object
5030 Roo.bootstrap.Pagination = function(config){
5031 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5034 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5040 getAutoCreate : function(){
5046 cfg.cls += ' inverse';
5052 cfg.cls += " " + this.cls;
5070 * @class Roo.bootstrap.PaginationItem
5071 * @extends Roo.bootstrap.Component
5072 * Bootstrap PaginationItem class
5073 * @cfg {String} html text
5074 * @cfg {String} href the link
5075 * @cfg {Boolean} preventDefault (true | false) default true
5076 * @cfg {Boolean} active (true | false) default false
5077 * @cfg {Boolean} disabled default false
5081 * Create a new PaginationItem
5082 * @param {Object} config The config object
5086 Roo.bootstrap.PaginationItem = function(config){
5087 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5092 * The raw click event for the entire grid.
5093 * @param {Roo.EventObject} e
5099 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5103 preventDefault: true,
5108 getAutoCreate : function(){
5114 href : this.href ? this.href : '#',
5115 html : this.html ? this.html : ''
5125 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5129 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5135 initEvents: function() {
5137 this.el.on('click', this.onClick, this);
5140 onClick : function(e)
5142 Roo.log('PaginationItem on click ');
5143 if(this.preventDefault){
5151 this.fireEvent('click', this, e);
5167 * @class Roo.bootstrap.Slider
5168 * @extends Roo.bootstrap.Component
5169 * Bootstrap Slider class
5172 * Create a new Slider
5173 * @param {Object} config The config object
5176 Roo.bootstrap.Slider = function(config){
5177 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5180 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5182 getAutoCreate : function(){
5186 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5190 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5202 * Ext JS Library 1.1.1
5203 * Copyright(c) 2006-2007, Ext JS, LLC.
5205 * Originally Released Under LGPL - original licence link has changed is not relivant.
5208 * <script type="text/javascript">
5213 * @class Roo.grid.ColumnModel
5214 * @extends Roo.util.Observable
5215 * This is the default implementation of a ColumnModel used by the Grid. It defines
5216 * the columns in the grid.
5219 var colModel = new Roo.grid.ColumnModel([
5220 {header: "Ticker", width: 60, sortable: true, locked: true},
5221 {header: "Company Name", width: 150, sortable: true},
5222 {header: "Market Cap.", width: 100, sortable: true},
5223 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5224 {header: "Employees", width: 100, sortable: true, resizable: false}
5229 * The config options listed for this class are options which may appear in each
5230 * individual column definition.
5231 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5233 * @param {Object} config An Array of column config objects. See this class's
5234 * config objects for details.
5236 Roo.grid.ColumnModel = function(config){
5238 * The config passed into the constructor
5240 this.config = config;
5243 // if no id, create one
5244 // if the column does not have a dataIndex mapping,
5245 // map it to the order it is in the config
5246 for(var i = 0, len = config.length; i < len; i++){
5248 if(typeof c.dataIndex == "undefined"){
5251 if(typeof c.renderer == "string"){
5252 c.renderer = Roo.util.Format[c.renderer];
5254 if(typeof c.id == "undefined"){
5257 if(c.editor && c.editor.xtype){
5258 c.editor = Roo.factory(c.editor, Roo.grid);
5260 if(c.editor && c.editor.isFormField){
5261 c.editor = new Roo.grid.GridEditor(c.editor);
5263 this.lookup[c.id] = c;
5267 * The width of columns which have no width specified (defaults to 100)
5270 this.defaultWidth = 100;
5273 * Default sortable of columns which have no sortable specified (defaults to false)
5276 this.defaultSortable = false;
5280 * @event widthchange
5281 * Fires when the width of a column changes.
5282 * @param {ColumnModel} this
5283 * @param {Number} columnIndex The column index
5284 * @param {Number} newWidth The new width
5286 "widthchange": true,
5288 * @event headerchange
5289 * Fires when the text of a header changes.
5290 * @param {ColumnModel} this
5291 * @param {Number} columnIndex The column index
5292 * @param {Number} newText The new header text
5294 "headerchange": true,
5296 * @event hiddenchange
5297 * Fires when a column is hidden or "unhidden".
5298 * @param {ColumnModel} this
5299 * @param {Number} columnIndex The column index
5300 * @param {Boolean} hidden true if hidden, false otherwise
5302 "hiddenchange": true,
5304 * @event columnmoved
5305 * Fires when a column is moved.
5306 * @param {ColumnModel} this
5307 * @param {Number} oldIndex
5308 * @param {Number} newIndex
5310 "columnmoved" : true,
5312 * @event columlockchange
5313 * Fires when a column's locked state is changed
5314 * @param {ColumnModel} this
5315 * @param {Number} colIndex
5316 * @param {Boolean} locked true if locked
5318 "columnlockchange" : true
5320 Roo.grid.ColumnModel.superclass.constructor.call(this);
5322 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5324 * @cfg {String} header The header text to display in the Grid view.
5327 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5328 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5329 * specified, the column's index is used as an index into the Record's data Array.
5332 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5333 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5336 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5337 * Defaults to the value of the {@link #defaultSortable} property.
5338 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5341 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5344 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5347 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5350 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5353 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5354 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5355 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5356 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5359 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5362 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5365 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5368 * @cfg {String} cursor (Optional)
5371 * @cfg {String} tooltip (Optional)
5374 * @cfg {Number} xs (Optional)
5377 * @cfg {Number} sm (Optional)
5380 * @cfg {Number} md (Optional)
5383 * @cfg {Number} lg (Optional)
5386 * Returns the id of the column at the specified index.
5387 * @param {Number} index The column index
5388 * @return {String} the id
5390 getColumnId : function(index){
5391 return this.config[index].id;
5395 * Returns the column for a specified id.
5396 * @param {String} id The column id
5397 * @return {Object} the column
5399 getColumnById : function(id){
5400 return this.lookup[id];
5405 * Returns the column for a specified dataIndex.
5406 * @param {String} dataIndex The column dataIndex
5407 * @return {Object|Boolean} the column or false if not found
5409 getColumnByDataIndex: function(dataIndex){
5410 var index = this.findColumnIndex(dataIndex);
5411 return index > -1 ? this.config[index] : false;
5415 * Returns the index for a specified column id.
5416 * @param {String} id The column id
5417 * @return {Number} the index, or -1 if not found
5419 getIndexById : function(id){
5420 for(var i = 0, len = this.config.length; i < len; i++){
5421 if(this.config[i].id == id){
5429 * Returns the index for a specified column dataIndex.
5430 * @param {String} dataIndex The column dataIndex
5431 * @return {Number} the index, or -1 if not found
5434 findColumnIndex : function(dataIndex){
5435 for(var i = 0, len = this.config.length; i < len; i++){
5436 if(this.config[i].dataIndex == dataIndex){
5444 moveColumn : function(oldIndex, newIndex){
5445 var c = this.config[oldIndex];
5446 this.config.splice(oldIndex, 1);
5447 this.config.splice(newIndex, 0, c);
5448 this.dataMap = null;
5449 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5452 isLocked : function(colIndex){
5453 return this.config[colIndex].locked === true;
5456 setLocked : function(colIndex, value, suppressEvent){
5457 if(this.isLocked(colIndex) == value){
5460 this.config[colIndex].locked = value;
5462 this.fireEvent("columnlockchange", this, colIndex, value);
5466 getTotalLockedWidth : function(){
5468 for(var i = 0; i < this.config.length; i++){
5469 if(this.isLocked(i) && !this.isHidden(i)){
5470 this.totalWidth += this.getColumnWidth(i);
5476 getLockedCount : function(){
5477 for(var i = 0, len = this.config.length; i < len; i++){
5478 if(!this.isLocked(i)){
5483 return this.config.length;
5487 * Returns the number of columns.
5490 getColumnCount : function(visibleOnly){
5491 if(visibleOnly === true){
5493 for(var i = 0, len = this.config.length; i < len; i++){
5494 if(!this.isHidden(i)){
5500 return this.config.length;
5504 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5505 * @param {Function} fn
5506 * @param {Object} scope (optional)
5507 * @return {Array} result
5509 getColumnsBy : function(fn, scope){
5511 for(var i = 0, len = this.config.length; i < len; i++){
5512 var c = this.config[i];
5513 if(fn.call(scope||this, c, i) === true){
5521 * Returns true if the specified column is sortable.
5522 * @param {Number} col The column index
5525 isSortable : function(col){
5526 if(typeof this.config[col].sortable == "undefined"){
5527 return this.defaultSortable;
5529 return this.config[col].sortable;
5533 * Returns the rendering (formatting) function defined for the column.
5534 * @param {Number} col The column index.
5535 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5537 getRenderer : function(col){
5538 if(!this.config[col].renderer){
5539 return Roo.grid.ColumnModel.defaultRenderer;
5541 return this.config[col].renderer;
5545 * Sets the rendering (formatting) function for a column.
5546 * @param {Number} col The column index
5547 * @param {Function} fn The function to use to process the cell's raw data
5548 * to return HTML markup for the grid view. The render function is called with
5549 * the following parameters:<ul>
5550 * <li>Data value.</li>
5551 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5552 * <li>css A CSS style string to apply to the table cell.</li>
5553 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5554 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5555 * <li>Row index</li>
5556 * <li>Column index</li>
5557 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5559 setRenderer : function(col, fn){
5560 this.config[col].renderer = fn;
5564 * Returns the width for the specified column.
5565 * @param {Number} col The column index
5568 getColumnWidth : function(col){
5569 return this.config[col].width * 1 || this.defaultWidth;
5573 * Sets the width for a column.
5574 * @param {Number} col The column index
5575 * @param {Number} width The new width
5577 setColumnWidth : function(col, width, suppressEvent){
5578 this.config[col].width = width;
5579 this.totalWidth = null;
5581 this.fireEvent("widthchange", this, col, width);
5586 * Returns the total width of all columns.
5587 * @param {Boolean} includeHidden True to include hidden column widths
5590 getTotalWidth : function(includeHidden){
5591 if(!this.totalWidth){
5592 this.totalWidth = 0;
5593 for(var i = 0, len = this.config.length; i < len; i++){
5594 if(includeHidden || !this.isHidden(i)){
5595 this.totalWidth += this.getColumnWidth(i);
5599 return this.totalWidth;
5603 * Returns the header for the specified column.
5604 * @param {Number} col The column index
5607 getColumnHeader : function(col){
5608 return this.config[col].header;
5612 * Sets the header for a column.
5613 * @param {Number} col The column index
5614 * @param {String} header The new header
5616 setColumnHeader : function(col, header){
5617 this.config[col].header = header;
5618 this.fireEvent("headerchange", this, col, header);
5622 * Returns the tooltip for the specified column.
5623 * @param {Number} col The column index
5626 getColumnTooltip : function(col){
5627 return this.config[col].tooltip;
5630 * Sets the tooltip for a column.
5631 * @param {Number} col The column index
5632 * @param {String} tooltip The new tooltip
5634 setColumnTooltip : function(col, tooltip){
5635 this.config[col].tooltip = tooltip;
5639 * Returns the dataIndex for the specified column.
5640 * @param {Number} col The column index
5643 getDataIndex : function(col){
5644 return this.config[col].dataIndex;
5648 * Sets the dataIndex for a column.
5649 * @param {Number} col The column index
5650 * @param {Number} dataIndex The new dataIndex
5652 setDataIndex : function(col, dataIndex){
5653 this.config[col].dataIndex = dataIndex;
5659 * Returns true if the cell is editable.
5660 * @param {Number} colIndex The column index
5661 * @param {Number} rowIndex The row index - this is nto actually used..?
5664 isCellEditable : function(colIndex, rowIndex){
5665 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5669 * Returns the editor defined for the cell/column.
5670 * return false or null to disable editing.
5671 * @param {Number} colIndex The column index
5672 * @param {Number} rowIndex The row index
5675 getCellEditor : function(colIndex, rowIndex){
5676 return this.config[colIndex].editor;
5680 * Sets if a column is editable.
5681 * @param {Number} col The column index
5682 * @param {Boolean} editable True if the column is editable
5684 setEditable : function(col, editable){
5685 this.config[col].editable = editable;
5690 * Returns true if the column is hidden.
5691 * @param {Number} colIndex The column index
5694 isHidden : function(colIndex){
5695 return this.config[colIndex].hidden;
5700 * Returns true if the column width cannot be changed
5702 isFixed : function(colIndex){
5703 return this.config[colIndex].fixed;
5707 * Returns true if the column can be resized
5710 isResizable : function(colIndex){
5711 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5714 * Sets if a column is hidden.
5715 * @param {Number} colIndex The column index
5716 * @param {Boolean} hidden True if the column is hidden
5718 setHidden : function(colIndex, hidden){
5719 this.config[colIndex].hidden = hidden;
5720 this.totalWidth = null;
5721 this.fireEvent("hiddenchange", this, colIndex, hidden);
5725 * Sets the editor for a column.
5726 * @param {Number} col The column index
5727 * @param {Object} editor The editor object
5729 setEditor : function(col, editor){
5730 this.config[col].editor = editor;
5734 Roo.grid.ColumnModel.defaultRenderer = function(value)
5736 if(typeof value == "object") {
5739 if(typeof value == "string" && value.length < 1){
5743 return String.format("{0}", value);
5746 // Alias for backwards compatibility
5747 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5750 * Ext JS Library 1.1.1
5751 * Copyright(c) 2006-2007, Ext JS, LLC.
5753 * Originally Released Under LGPL - original licence link has changed is not relivant.
5756 * <script type="text/javascript">
5760 * @class Roo.LoadMask
5761 * A simple utility class for generically masking elements while loading data. If the element being masked has
5762 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5763 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5764 * element's UpdateManager load indicator and will be destroyed after the initial load.
5766 * Create a new LoadMask
5767 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5768 * @param {Object} config The config object
5770 Roo.LoadMask = function(el, config){
5771 this.el = Roo.get(el);
5772 Roo.apply(this, config);
5774 this.store.on('beforeload', this.onBeforeLoad, this);
5775 this.store.on('load', this.onLoad, this);
5776 this.store.on('loadexception', this.onLoadException, this);
5777 this.removeMask = false;
5779 var um = this.el.getUpdateManager();
5780 um.showLoadIndicator = false; // disable the default indicator
5781 um.on('beforeupdate', this.onBeforeLoad, this);
5782 um.on('update', this.onLoad, this);
5783 um.on('failure', this.onLoad, this);
5784 this.removeMask = true;
5788 Roo.LoadMask.prototype = {
5790 * @cfg {Boolean} removeMask
5791 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5792 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5796 * The text to display in a centered loading message box (defaults to 'Loading...')
5800 * @cfg {String} msgCls
5801 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5803 msgCls : 'x-mask-loading',
5806 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5812 * Disables the mask to prevent it from being displayed
5814 disable : function(){
5815 this.disabled = true;
5819 * Enables the mask so that it can be displayed
5821 enable : function(){
5822 this.disabled = false;
5825 onLoadException : function()
5829 if (typeof(arguments[3]) != 'undefined') {
5830 Roo.MessageBox.alert("Error loading",arguments[3]);
5834 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5835 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5842 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5847 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5851 onBeforeLoad : function(){
5853 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5858 destroy : function(){
5860 this.store.un('beforeload', this.onBeforeLoad, this);
5861 this.store.un('load', this.onLoad, this);
5862 this.store.un('loadexception', this.onLoadException, this);
5864 var um = this.el.getUpdateManager();
5865 um.un('beforeupdate', this.onBeforeLoad, this);
5866 um.un('update', this.onLoad, this);
5867 um.un('failure', this.onLoad, this);
5878 * @class Roo.bootstrap.Table
5879 * @extends Roo.bootstrap.Component
5880 * Bootstrap Table class
5881 * @cfg {String} cls table class
5882 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5883 * @cfg {String} bgcolor Specifies the background color for a table
5884 * @cfg {Number} border Specifies whether the table cells should have borders or not
5885 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5886 * @cfg {Number} cellspacing Specifies the space between cells
5887 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5888 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5889 * @cfg {String} sortable Specifies that the table should be sortable
5890 * @cfg {String} summary Specifies a summary of the content of a table
5891 * @cfg {Number} width Specifies the width of a table
5892 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5894 * @cfg {boolean} striped Should the rows be alternative striped
5895 * @cfg {boolean} bordered Add borders to the table
5896 * @cfg {boolean} hover Add hover highlighting
5897 * @cfg {boolean} condensed Format condensed
5898 * @cfg {boolean} responsive Format condensed
5899 * @cfg {Boolean} loadMask (true|false) default false
5900 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5901 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5902 * @cfg {Boolean} rowSelection (true|false) default false
5903 * @cfg {Boolean} cellSelection (true|false) default false
5904 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5905 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5906 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5907 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5911 * Create a new Table
5912 * @param {Object} config The config object
5915 Roo.bootstrap.Table = function(config){
5916 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5921 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5922 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5923 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5924 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5926 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5928 this.sm.grid = this;
5929 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5930 this.sm = this.selModel;
5931 this.sm.xmodule = this.xmodule || false;
5934 if (this.cm && typeof(this.cm.config) == 'undefined') {
5935 this.colModel = new Roo.grid.ColumnModel(this.cm);
5936 this.cm = this.colModel;
5937 this.cm.xmodule = this.xmodule || false;
5940 this.store= Roo.factory(this.store, Roo.data);
5941 this.ds = this.store;
5942 this.ds.xmodule = this.xmodule || false;
5945 if (this.footer && this.store) {
5946 this.footer.dataSource = this.ds;
5947 this.footer = Roo.factory(this.footer);
5954 * Fires when a cell is clicked
5955 * @param {Roo.bootstrap.Table} this
5956 * @param {Roo.Element} el
5957 * @param {Number} rowIndex
5958 * @param {Number} columnIndex
5959 * @param {Roo.EventObject} e
5963 * @event celldblclick
5964 * Fires when a cell is double clicked
5965 * @param {Roo.bootstrap.Table} this
5966 * @param {Roo.Element} el
5967 * @param {Number} rowIndex
5968 * @param {Number} columnIndex
5969 * @param {Roo.EventObject} e
5971 "celldblclick" : true,
5974 * Fires when a row is clicked
5975 * @param {Roo.bootstrap.Table} this
5976 * @param {Roo.Element} el
5977 * @param {Number} rowIndex
5978 * @param {Roo.EventObject} e
5982 * @event rowdblclick
5983 * Fires when a row is double clicked
5984 * @param {Roo.bootstrap.Table} this
5985 * @param {Roo.Element} el
5986 * @param {Number} rowIndex
5987 * @param {Roo.EventObject} e
5989 "rowdblclick" : true,
5992 * Fires when a mouseover occur
5993 * @param {Roo.bootstrap.Table} this
5994 * @param {Roo.Element} el
5995 * @param {Number} rowIndex
5996 * @param {Number} columnIndex
5997 * @param {Roo.EventObject} e
6002 * Fires when a mouseout occur
6003 * @param {Roo.bootstrap.Table} this
6004 * @param {Roo.Element} el
6005 * @param {Number} rowIndex
6006 * @param {Number} columnIndex
6007 * @param {Roo.EventObject} e
6012 * Fires when a row is rendered, so you can change add a style to it.
6013 * @param {Roo.bootstrap.Table} this
6014 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6018 * @event rowsrendered
6019 * Fires when all the rows have been rendered
6020 * @param {Roo.bootstrap.Table} this
6022 'rowsrendered' : true,
6024 * @event contextmenu
6025 * The raw contextmenu event for the entire grid.
6026 * @param {Roo.EventObject} e
6028 "contextmenu" : true,
6030 * @event rowcontextmenu
6031 * Fires when a row is right clicked
6032 * @param {Roo.bootstrap.Table} this
6033 * @param {Number} rowIndex
6034 * @param {Roo.EventObject} e
6036 "rowcontextmenu" : true,
6038 * @event cellcontextmenu
6039 * Fires when a cell is right clicked
6040 * @param {Roo.bootstrap.Table} this
6041 * @param {Number} rowIndex
6042 * @param {Number} cellIndex
6043 * @param {Roo.EventObject} e
6045 "cellcontextmenu" : true,
6047 * @event headercontextmenu
6048 * Fires when a header is right clicked
6049 * @param {Roo.bootstrap.Table} this
6050 * @param {Number} columnIndex
6051 * @param {Roo.EventObject} e
6053 "headercontextmenu" : true
6057 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6083 rowSelection : false,
6084 cellSelection : false,
6087 // Roo.Element - the tbody
6089 // Roo.Element - thead element
6092 container: false, // used by gridpanel...
6098 auto_hide_footer : false,
6100 getAutoCreate : function()
6102 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6109 if (this.scrollBody) {
6110 cfg.cls += ' table-body-fixed';
6113 cfg.cls += ' table-striped';
6117 cfg.cls += ' table-hover';
6119 if (this.bordered) {
6120 cfg.cls += ' table-bordered';
6122 if (this.condensed) {
6123 cfg.cls += ' table-condensed';
6125 if (this.responsive) {
6126 cfg.cls += ' table-responsive';
6130 cfg.cls+= ' ' +this.cls;
6133 // this lot should be simplifed...
6146 ].forEach(function(k) {
6154 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6157 if(this.store || this.cm){
6158 if(this.headerShow){
6159 cfg.cn.push(this.renderHeader());
6162 cfg.cn.push(this.renderBody());
6164 if(this.footerShow){
6165 cfg.cn.push(this.renderFooter());
6167 // where does this come from?
6168 //cfg.cls+= ' TableGrid';
6171 return { cn : [ cfg ] };
6174 initEvents : function()
6176 if(!this.store || !this.cm){
6179 if (this.selModel) {
6180 this.selModel.initEvents();
6184 //Roo.log('initEvents with ds!!!!');
6186 this.mainBody = this.el.select('tbody', true).first();
6187 this.mainHead = this.el.select('thead', true).first();
6188 this.mainFoot = this.el.select('tfoot', true).first();
6194 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6195 e.on('click', _this.sort, _this);
6198 this.mainBody.on("click", this.onClick, this);
6199 this.mainBody.on("dblclick", this.onDblClick, this);
6201 // why is this done????? = it breaks dialogs??
6202 //this.parent().el.setStyle('position', 'relative');
6206 this.footer.parentId = this.id;
6207 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6210 this.el.select('tfoot tr td').first().addClass('hide');
6215 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6218 this.store.on('load', this.onLoad, this);
6219 this.store.on('beforeload', this.onBeforeLoad, this);
6220 this.store.on('update', this.onUpdate, this);
6221 this.store.on('add', this.onAdd, this);
6222 this.store.on("clear", this.clear, this);
6224 this.el.on("contextmenu", this.onContextMenu, this);
6226 this.mainBody.on('scroll', this.onBodyScroll, this);
6228 this.cm.on("headerchange", this.onHeaderChange, this);
6230 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6234 onContextMenu : function(e, t)
6236 this.processEvent("contextmenu", e);
6239 processEvent : function(name, e)
6241 if (name != 'touchstart' ) {
6242 this.fireEvent(name, e);
6245 var t = e.getTarget();
6247 var cell = Roo.get(t);
6253 if(cell.findParent('tfoot', false, true)){
6257 if(cell.findParent('thead', false, true)){
6259 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6260 cell = Roo.get(t).findParent('th', false, true);
6262 Roo.log("failed to find th in thead?");
6263 Roo.log(e.getTarget());
6268 var cellIndex = cell.dom.cellIndex;
6270 var ename = name == 'touchstart' ? 'click' : name;
6271 this.fireEvent("header" + ename, this, cellIndex, e);
6276 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6277 cell = Roo.get(t).findParent('td', false, true);
6279 Roo.log("failed to find th in tbody?");
6280 Roo.log(e.getTarget());
6285 var row = cell.findParent('tr', false, true);
6286 var cellIndex = cell.dom.cellIndex;
6287 var rowIndex = row.dom.rowIndex - 1;
6291 this.fireEvent("row" + name, this, rowIndex, e);
6295 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6301 onMouseover : function(e, el)
6303 var cell = Roo.get(el);
6309 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6310 cell = cell.findParent('td', false, true);
6313 var row = cell.findParent('tr', false, true);
6314 var cellIndex = cell.dom.cellIndex;
6315 var rowIndex = row.dom.rowIndex - 1; // start from 0
6317 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6321 onMouseout : function(e, el)
6323 var cell = Roo.get(el);
6329 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6330 cell = cell.findParent('td', false, true);
6333 var row = cell.findParent('tr', false, true);
6334 var cellIndex = cell.dom.cellIndex;
6335 var rowIndex = row.dom.rowIndex - 1; // start from 0
6337 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6341 onClick : function(e, el)
6343 var cell = Roo.get(el);
6345 if(!cell || (!this.cellSelection && !this.rowSelection)){
6349 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6350 cell = cell.findParent('td', false, true);
6353 if(!cell || typeof(cell) == 'undefined'){
6357 var row = cell.findParent('tr', false, true);
6359 if(!row || typeof(row) == 'undefined'){
6363 var cellIndex = cell.dom.cellIndex;
6364 var rowIndex = this.getRowIndex(row);
6366 // why??? - should these not be based on SelectionModel?
6367 if(this.cellSelection){
6368 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6371 if(this.rowSelection){
6372 this.fireEvent('rowclick', this, row, rowIndex, e);
6378 onDblClick : function(e,el)
6380 var cell = Roo.get(el);
6382 if(!cell || (!this.cellSelection && !this.rowSelection)){
6386 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6387 cell = cell.findParent('td', false, true);
6390 if(!cell || typeof(cell) == 'undefined'){
6394 var row = cell.findParent('tr', false, true);
6396 if(!row || typeof(row) == 'undefined'){
6400 var cellIndex = cell.dom.cellIndex;
6401 var rowIndex = this.getRowIndex(row);
6403 if(this.cellSelection){
6404 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6407 if(this.rowSelection){
6408 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6412 sort : function(e,el)
6414 var col = Roo.get(el);
6416 if(!col.hasClass('sortable')){
6420 var sort = col.attr('sort');
6423 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6427 this.store.sortInfo = {field : sort, direction : dir};
6430 Roo.log("calling footer first");
6431 this.footer.onClick('first');
6434 this.store.load({ params : { start : 0 } });
6438 renderHeader : function()
6446 this.totalWidth = 0;
6448 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6450 var config = cm.config[i];
6454 cls : 'x-hcol-' + i,
6456 html: cm.getColumnHeader(i)
6461 if(typeof(config.sortable) != 'undefined' && config.sortable){
6463 c.html = '<i class="glyphicon"></i>' + c.html;
6466 if(typeof(config.lgHeader) != 'undefined'){
6467 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6470 if(typeof(config.mdHeader) != 'undefined'){
6471 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6474 if(typeof(config.smHeader) != 'undefined'){
6475 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6478 if(typeof(config.xsHeader) != 'undefined'){
6479 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6486 if(typeof(config.tooltip) != 'undefined'){
6487 c.tooltip = config.tooltip;
6490 if(typeof(config.colspan) != 'undefined'){
6491 c.colspan = config.colspan;
6494 if(typeof(config.hidden) != 'undefined' && config.hidden){
6495 c.style += ' display:none;';
6498 if(typeof(config.dataIndex) != 'undefined'){
6499 c.sort = config.dataIndex;
6504 if(typeof(config.align) != 'undefined' && config.align.length){
6505 c.style += ' text-align:' + config.align + ';';
6508 if(typeof(config.width) != 'undefined'){
6509 c.style += ' width:' + config.width + 'px;';
6510 this.totalWidth += config.width;
6512 this.totalWidth += 100; // assume minimum of 100 per column?
6515 if(typeof(config.cls) != 'undefined'){
6516 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6519 ['xs','sm','md','lg'].map(function(size){
6521 if(typeof(config[size]) == 'undefined'){
6525 if (!config[size]) { // 0 = hidden
6526 c.cls += ' hidden-' + size;
6530 c.cls += ' col-' + size + '-' + config[size];
6540 renderBody : function()
6550 colspan : this.cm.getColumnCount()
6560 renderFooter : function()
6570 colspan : this.cm.getColumnCount()
6584 // Roo.log('ds onload');
6589 var ds = this.store;
6591 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6592 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6593 if (_this.store.sortInfo) {
6595 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6596 e.select('i', true).addClass(['glyphicon-arrow-up']);
6599 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6600 e.select('i', true).addClass(['glyphicon-arrow-down']);
6605 var tbody = this.mainBody;
6607 if(ds.getCount() > 0){
6608 ds.data.each(function(d,rowIndex){
6609 var row = this.renderRow(cm, ds, rowIndex);
6611 tbody.createChild(row);
6615 if(row.cellObjects.length){
6616 Roo.each(row.cellObjects, function(r){
6617 _this.renderCellObject(r);
6624 var tfoot = this.el.select('tfoot', true).first();
6626 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6628 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6630 var total = this.ds.getTotalCount();
6632 if(this.footer.pageSize < total){
6633 this.mainFoot.show();
6637 Roo.each(this.el.select('tbody td', true).elements, function(e){
6638 e.on('mouseover', _this.onMouseover, _this);
6641 Roo.each(this.el.select('tbody td', true).elements, function(e){
6642 e.on('mouseout', _this.onMouseout, _this);
6644 this.fireEvent('rowsrendered', this);
6650 onUpdate : function(ds,record)
6652 this.refreshRow(record);
6656 onRemove : function(ds, record, index, isUpdate){
6657 if(isUpdate !== true){
6658 this.fireEvent("beforerowremoved", this, index, record);
6660 var bt = this.mainBody.dom;
6662 var rows = this.el.select('tbody > tr', true).elements;
6664 if(typeof(rows[index]) != 'undefined'){
6665 bt.removeChild(rows[index].dom);
6668 // if(bt.rows[index]){
6669 // bt.removeChild(bt.rows[index]);
6672 if(isUpdate !== true){
6673 //this.stripeRows(index);
6674 //this.syncRowHeights(index, index);
6676 this.fireEvent("rowremoved", this, index, record);
6680 onAdd : function(ds, records, rowIndex)
6682 //Roo.log('on Add called');
6683 // - note this does not handle multiple adding very well..
6684 var bt = this.mainBody.dom;
6685 for (var i =0 ; i < records.length;i++) {
6686 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6687 //Roo.log(records[i]);
6688 //Roo.log(this.store.getAt(rowIndex+i));
6689 this.insertRow(this.store, rowIndex + i, false);
6696 refreshRow : function(record){
6697 var ds = this.store, index;
6698 if(typeof record == 'number'){
6700 record = ds.getAt(index);
6702 index = ds.indexOf(record);
6704 this.insertRow(ds, index, true);
6706 this.onRemove(ds, record, index+1, true);
6708 //this.syncRowHeights(index, index);
6710 this.fireEvent("rowupdated", this, index, record);
6713 insertRow : function(dm, rowIndex, isUpdate){
6716 this.fireEvent("beforerowsinserted", this, rowIndex);
6718 //var s = this.getScrollState();
6719 var row = this.renderRow(this.cm, this.store, rowIndex);
6720 // insert before rowIndex..
6721 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6725 if(row.cellObjects.length){
6726 Roo.each(row.cellObjects, function(r){
6727 _this.renderCellObject(r);
6732 this.fireEvent("rowsinserted", this, rowIndex);
6733 //this.syncRowHeights(firstRow, lastRow);
6734 //this.stripeRows(firstRow);
6741 getRowDom : function(rowIndex)
6743 var rows = this.el.select('tbody > tr', true).elements;
6745 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6748 // returns the object tree for a tr..
6751 renderRow : function(cm, ds, rowIndex)
6753 var d = ds.getAt(rowIndex);
6757 cls : 'x-row-' + rowIndex,
6761 var cellObjects = [];
6763 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6764 var config = cm.config[i];
6766 var renderer = cm.getRenderer(i);
6770 if(typeof(renderer) !== 'undefined'){
6771 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6773 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6774 // and are rendered into the cells after the row is rendered - using the id for the element.
6776 if(typeof(value) === 'object'){
6786 rowIndex : rowIndex,
6791 this.fireEvent('rowclass', this, rowcfg);
6795 cls : rowcfg.rowClass + ' x-col-' + i,
6797 html: (typeof(value) === 'object') ? '' : value
6804 if(typeof(config.colspan) != 'undefined'){
6805 td.colspan = config.colspan;
6808 if(typeof(config.hidden) != 'undefined' && config.hidden){
6809 td.style += ' display:none;';
6812 if(typeof(config.align) != 'undefined' && config.align.length){
6813 td.style += ' text-align:' + config.align + ';';
6815 if(typeof(config.valign) != 'undefined' && config.valign.length){
6816 td.style += ' vertical-align:' + config.valign + ';';
6819 if(typeof(config.width) != 'undefined'){
6820 td.style += ' width:' + config.width + 'px;';
6823 if(typeof(config.cursor) != 'undefined'){
6824 td.style += ' cursor:' + config.cursor + ';';
6827 if(typeof(config.cls) != 'undefined'){
6828 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6831 ['xs','sm','md','lg'].map(function(size){
6833 if(typeof(config[size]) == 'undefined'){
6837 if (!config[size]) { // 0 = hidden
6838 td.cls += ' hidden-' + size;
6842 td.cls += ' col-' + size + '-' + config[size];
6850 row.cellObjects = cellObjects;
6858 onBeforeLoad : function()
6867 this.el.select('tbody', true).first().dom.innerHTML = '';
6870 * Show or hide a row.
6871 * @param {Number} rowIndex to show or hide
6872 * @param {Boolean} state hide
6874 setRowVisibility : function(rowIndex, state)
6876 var bt = this.mainBody.dom;
6878 var rows = this.el.select('tbody > tr', true).elements;
6880 if(typeof(rows[rowIndex]) == 'undefined'){
6883 rows[rowIndex].dom.style.display = state ? '' : 'none';
6887 getSelectionModel : function(){
6889 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6891 return this.selModel;
6894 * Render the Roo.bootstrap object from renderder
6896 renderCellObject : function(r)
6900 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6902 var t = r.cfg.render(r.container);
6905 Roo.each(r.cfg.cn, function(c){
6907 container: t.getChildContainer(),
6910 _this.renderCellObject(child);
6915 getRowIndex : function(row)
6919 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6930 * Returns the grid's underlying element = used by panel.Grid
6931 * @return {Element} The element
6933 getGridEl : function(){
6937 * Forces a resize - used by panel.Grid
6938 * @return {Element} The element
6940 autoSize : function()
6942 //var ctr = Roo.get(this.container.dom.parentElement);
6943 var ctr = Roo.get(this.el.dom);
6945 var thd = this.getGridEl().select('thead',true).first();
6946 var tbd = this.getGridEl().select('tbody', true).first();
6947 var tfd = this.getGridEl().select('tfoot', true).first();
6949 var cw = ctr.getWidth();
6953 tbd.setSize(ctr.getWidth(),
6954 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6956 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6959 cw = Math.max(cw, this.totalWidth);
6960 this.getGridEl().select('tr',true).setWidth(cw);
6961 // resize 'expandable coloumn?
6963 return; // we doe not have a view in this design..
6966 onBodyScroll: function()
6968 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6970 this.mainHead.setStyle({
6971 'position' : 'relative',
6972 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6978 var scrollHeight = this.mainBody.dom.scrollHeight;
6980 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6982 var height = this.mainBody.getHeight();
6984 if(scrollHeight - height == scrollTop) {
6986 var total = this.ds.getTotalCount();
6988 if(this.footer.cursor + this.footer.pageSize < total){
6990 this.footer.ds.load({
6992 start : this.footer.cursor + this.footer.pageSize,
6993 limit : this.footer.pageSize
7003 onHeaderChange : function()
7005 var header = this.renderHeader();
7006 var table = this.el.select('table', true).first();
7008 this.mainHead.remove();
7009 this.mainHead = table.createChild(header, this.mainBody, false);
7012 onHiddenChange : function(colModel, colIndex, hidden)
7014 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7015 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7017 this.CSS.updateRule(thSelector, "display", "");
7018 this.CSS.updateRule(tdSelector, "display", "");
7021 this.CSS.updateRule(thSelector, "display", "none");
7022 this.CSS.updateRule(tdSelector, "display", "none");
7025 this.onHeaderChange();
7042 * @class Roo.bootstrap.TableCell
7043 * @extends Roo.bootstrap.Component
7044 * Bootstrap TableCell class
7045 * @cfg {String} html cell contain text
7046 * @cfg {String} cls cell class
7047 * @cfg {String} tag cell tag (td|th) default td
7048 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7049 * @cfg {String} align Aligns the content in a cell
7050 * @cfg {String} axis Categorizes cells
7051 * @cfg {String} bgcolor Specifies the background color of a cell
7052 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7053 * @cfg {Number} colspan Specifies the number of columns a cell should span
7054 * @cfg {String} headers Specifies one or more header cells a cell is related to
7055 * @cfg {Number} height Sets the height of a cell
7056 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7057 * @cfg {Number} rowspan Sets the number of rows a cell should span
7058 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7059 * @cfg {String} valign Vertical aligns the content in a cell
7060 * @cfg {Number} width Specifies the width of a cell
7063 * Create a new TableCell
7064 * @param {Object} config The config object
7067 Roo.bootstrap.TableCell = function(config){
7068 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7071 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7091 getAutoCreate : function(){
7092 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7112 cfg.align=this.align
7118 cfg.bgcolor=this.bgcolor
7121 cfg.charoff=this.charoff
7124 cfg.colspan=this.colspan
7127 cfg.headers=this.headers
7130 cfg.height=this.height
7133 cfg.nowrap=this.nowrap
7136 cfg.rowspan=this.rowspan
7139 cfg.scope=this.scope
7142 cfg.valign=this.valign
7145 cfg.width=this.width
7164 * @class Roo.bootstrap.TableRow
7165 * @extends Roo.bootstrap.Component
7166 * Bootstrap TableRow class
7167 * @cfg {String} cls row class
7168 * @cfg {String} align Aligns the content in a table row
7169 * @cfg {String} bgcolor Specifies a background color for a table row
7170 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7171 * @cfg {String} valign Vertical aligns the content in a table row
7174 * Create a new TableRow
7175 * @param {Object} config The config object
7178 Roo.bootstrap.TableRow = function(config){
7179 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7182 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7190 getAutoCreate : function(){
7191 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7201 cfg.align = this.align;
7204 cfg.bgcolor = this.bgcolor;
7207 cfg.charoff = this.charoff;
7210 cfg.valign = this.valign;
7228 * @class Roo.bootstrap.TableBody
7229 * @extends Roo.bootstrap.Component
7230 * Bootstrap TableBody class
7231 * @cfg {String} cls element class
7232 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7233 * @cfg {String} align Aligns the content inside the element
7234 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7235 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7238 * Create a new TableBody
7239 * @param {Object} config The config object
7242 Roo.bootstrap.TableBody = function(config){
7243 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7246 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7254 getAutoCreate : function(){
7255 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7269 cfg.align = this.align;
7272 cfg.charoff = this.charoff;
7275 cfg.valign = this.valign;
7282 // initEvents : function()
7289 // this.store = Roo.factory(this.store, Roo.data);
7290 // this.store.on('load', this.onLoad, this);
7292 // this.store.load();
7296 // onLoad: function ()
7298 // this.fireEvent('load', this);
7308 * Ext JS Library 1.1.1
7309 * Copyright(c) 2006-2007, Ext JS, LLC.
7311 * Originally Released Under LGPL - original licence link has changed is not relivant.
7314 * <script type="text/javascript">
7317 // as we use this in bootstrap.
7318 Roo.namespace('Roo.form');
7320 * @class Roo.form.Action
7321 * Internal Class used to handle form actions
7323 * @param {Roo.form.BasicForm} el The form element or its id
7324 * @param {Object} config Configuration options
7329 // define the action interface
7330 Roo.form.Action = function(form, options){
7332 this.options = options || {};
7335 * Client Validation Failed
7338 Roo.form.Action.CLIENT_INVALID = 'client';
7340 * Server Validation Failed
7343 Roo.form.Action.SERVER_INVALID = 'server';
7345 * Connect to Server Failed
7348 Roo.form.Action.CONNECT_FAILURE = 'connect';
7350 * Reading Data from Server Failed
7353 Roo.form.Action.LOAD_FAILURE = 'load';
7355 Roo.form.Action.prototype = {
7357 failureType : undefined,
7358 response : undefined,
7362 run : function(options){
7367 success : function(response){
7372 handleResponse : function(response){
7376 // default connection failure
7377 failure : function(response){
7379 this.response = response;
7380 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7381 this.form.afterAction(this, false);
7384 processResponse : function(response){
7385 this.response = response;
7386 if(!response.responseText){
7389 this.result = this.handleResponse(response);
7393 // utility functions used internally
7394 getUrl : function(appendParams){
7395 var url = this.options.url || this.form.url || this.form.el.dom.action;
7397 var p = this.getParams();
7399 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7405 getMethod : function(){
7406 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7409 getParams : function(){
7410 var bp = this.form.baseParams;
7411 var p = this.options.params;
7413 if(typeof p == "object"){
7414 p = Roo.urlEncode(Roo.applyIf(p, bp));
7415 }else if(typeof p == 'string' && bp){
7416 p += '&' + Roo.urlEncode(bp);
7419 p = Roo.urlEncode(bp);
7424 createCallback : function(){
7426 success: this.success,
7427 failure: this.failure,
7429 timeout: (this.form.timeout*1000),
7430 upload: this.form.fileUpload ? this.success : undefined
7435 Roo.form.Action.Submit = function(form, options){
7436 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7439 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7442 haveProgress : false,
7443 uploadComplete : false,
7445 // uploadProgress indicator.
7446 uploadProgress : function()
7448 if (!this.form.progressUrl) {
7452 if (!this.haveProgress) {
7453 Roo.MessageBox.progress("Uploading", "Uploading");
7455 if (this.uploadComplete) {
7456 Roo.MessageBox.hide();
7460 this.haveProgress = true;
7462 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7464 var c = new Roo.data.Connection();
7466 url : this.form.progressUrl,
7471 success : function(req){
7472 //console.log(data);
7476 rdata = Roo.decode(req.responseText)
7478 Roo.log("Invalid data from server..");
7482 if (!rdata || !rdata.success) {
7484 Roo.MessageBox.alert(Roo.encode(rdata));
7487 var data = rdata.data;
7489 if (this.uploadComplete) {
7490 Roo.MessageBox.hide();
7495 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7496 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7499 this.uploadProgress.defer(2000,this);
7502 failure: function(data) {
7503 Roo.log('progress url failed ');
7514 // run get Values on the form, so it syncs any secondary forms.
7515 this.form.getValues();
7517 var o = this.options;
7518 var method = this.getMethod();
7519 var isPost = method == 'POST';
7520 if(o.clientValidation === false || this.form.isValid()){
7522 if (this.form.progressUrl) {
7523 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7524 (new Date() * 1) + '' + Math.random());
7529 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7530 form:this.form.el.dom,
7531 url:this.getUrl(!isPost),
7533 params:isPost ? this.getParams() : null,
7534 isUpload: this.form.fileUpload
7537 this.uploadProgress();
7539 }else if (o.clientValidation !== false){ // client validation failed
7540 this.failureType = Roo.form.Action.CLIENT_INVALID;
7541 this.form.afterAction(this, false);
7545 success : function(response)
7547 this.uploadComplete= true;
7548 if (this.haveProgress) {
7549 Roo.MessageBox.hide();
7553 var result = this.processResponse(response);
7554 if(result === true || result.success){
7555 this.form.afterAction(this, true);
7559 this.form.markInvalid(result.errors);
7560 this.failureType = Roo.form.Action.SERVER_INVALID;
7562 this.form.afterAction(this, false);
7564 failure : function(response)
7566 this.uploadComplete= true;
7567 if (this.haveProgress) {
7568 Roo.MessageBox.hide();
7571 this.response = response;
7572 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7573 this.form.afterAction(this, false);
7576 handleResponse : function(response){
7577 if(this.form.errorReader){
7578 var rs = this.form.errorReader.read(response);
7581 for(var i = 0, len = rs.records.length; i < len; i++) {
7582 var r = rs.records[i];
7586 if(errors.length < 1){
7590 success : rs.success,
7596 ret = Roo.decode(response.responseText);
7600 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7610 Roo.form.Action.Load = function(form, options){
7611 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7612 this.reader = this.form.reader;
7615 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7620 Roo.Ajax.request(Roo.apply(
7621 this.createCallback(), {
7622 method:this.getMethod(),
7623 url:this.getUrl(false),
7624 params:this.getParams()
7628 success : function(response){
7630 var result = this.processResponse(response);
7631 if(result === true || !result.success || !result.data){
7632 this.failureType = Roo.form.Action.LOAD_FAILURE;
7633 this.form.afterAction(this, false);
7636 this.form.clearInvalid();
7637 this.form.setValues(result.data);
7638 this.form.afterAction(this, true);
7641 handleResponse : function(response){
7642 if(this.form.reader){
7643 var rs = this.form.reader.read(response);
7644 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7646 success : rs.success,
7650 return Roo.decode(response.responseText);
7654 Roo.form.Action.ACTION_TYPES = {
7655 'load' : Roo.form.Action.Load,
7656 'submit' : Roo.form.Action.Submit
7665 * @class Roo.bootstrap.Form
7666 * @extends Roo.bootstrap.Component
7667 * Bootstrap Form class
7668 * @cfg {String} method GET | POST (default POST)
7669 * @cfg {String} labelAlign top | left (default top)
7670 * @cfg {String} align left | right - for navbars
7671 * @cfg {Boolean} loadMask load mask when submit (default true)
7676 * @param {Object} config The config object
7680 Roo.bootstrap.Form = function(config){
7682 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7684 Roo.bootstrap.Form.popover.apply();
7688 * @event clientvalidation
7689 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7690 * @param {Form} this
7691 * @param {Boolean} valid true if the form has passed client-side validation
7693 clientvalidation: true,
7695 * @event beforeaction
7696 * Fires before any action is performed. Return false to cancel the action.
7697 * @param {Form} this
7698 * @param {Action} action The action to be performed
7702 * @event actionfailed
7703 * Fires when an action fails.
7704 * @param {Form} this
7705 * @param {Action} action The action that failed
7707 actionfailed : true,
7709 * @event actioncomplete
7710 * Fires when an action is completed.
7711 * @param {Form} this
7712 * @param {Action} action The action that completed
7714 actioncomplete : true
7718 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7721 * @cfg {String} method
7722 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7727 * The URL to use for form actions if one isn't supplied in the action options.
7730 * @cfg {Boolean} fileUpload
7731 * Set to true if this form is a file upload.
7735 * @cfg {Object} baseParams
7736 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7740 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7744 * @cfg {Sting} align (left|right) for navbar forms
7749 activeAction : null,
7752 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7753 * element by passing it or its id or mask the form itself by passing in true.
7756 waitMsgTarget : false,
7761 * @cfg {Boolean} errorMask (true|false) default false
7766 * @cfg {Number} maskOffset Default 100
7771 * @cfg {Boolean} maskBody
7775 getAutoCreate : function(){
7779 method : this.method || 'POST',
7780 id : this.id || Roo.id(),
7783 if (this.parent().xtype.match(/^Nav/)) {
7784 cfg.cls = 'navbar-form navbar-' + this.align;
7788 if (this.labelAlign == 'left' ) {
7789 cfg.cls += ' form-horizontal';
7795 initEvents : function()
7797 this.el.on('submit', this.onSubmit, this);
7798 // this was added as random key presses on the form where triggering form submit.
7799 this.el.on('keypress', function(e) {
7800 if (e.getCharCode() != 13) {
7803 // we might need to allow it for textareas.. and some other items.
7804 // check e.getTarget().
7806 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7810 Roo.log("keypress blocked");
7818 onSubmit : function(e){
7823 * Returns true if client-side validation on the form is successful.
7826 isValid : function(){
7827 var items = this.getItems();
7831 items.each(function(f){
7839 if(!target && f.el.isVisible(true)){
7845 if(this.errorMask && !valid){
7846 Roo.bootstrap.Form.popover.mask(this, target);
7853 * Returns true if any fields in this form have changed since their original load.
7856 isDirty : function(){
7858 var items = this.getItems();
7859 items.each(function(f){
7869 * Performs a predefined action (submit or load) or custom actions you define on this form.
7870 * @param {String} actionName The name of the action type
7871 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7872 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7873 * accept other config options):
7875 Property Type Description
7876 ---------------- --------------- ----------------------------------------------------------------------------------
7877 url String The url for the action (defaults to the form's url)
7878 method String The form method to use (defaults to the form's method, or POST if not defined)
7879 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7880 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7881 validate the form on the client (defaults to false)
7883 * @return {BasicForm} this
7885 doAction : function(action, options){
7886 if(typeof action == 'string'){
7887 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7889 if(this.fireEvent('beforeaction', this, action) !== false){
7890 this.beforeAction(action);
7891 action.run.defer(100, action);
7897 beforeAction : function(action){
7898 var o = action.options;
7903 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7905 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7908 // not really supported yet.. ??
7910 //if(this.waitMsgTarget === true){
7911 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7912 //}else if(this.waitMsgTarget){
7913 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7914 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7916 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7922 afterAction : function(action, success){
7923 this.activeAction = null;
7924 var o = action.options;
7929 Roo.get(document.body).unmask();
7935 //if(this.waitMsgTarget === true){
7936 // this.el.unmask();
7937 //}else if(this.waitMsgTarget){
7938 // this.waitMsgTarget.unmask();
7940 // Roo.MessageBox.updateProgress(1);
7941 // Roo.MessageBox.hide();
7948 Roo.callback(o.success, o.scope, [this, action]);
7949 this.fireEvent('actioncomplete', this, action);
7953 // failure condition..
7954 // we have a scenario where updates need confirming.
7955 // eg. if a locking scenario exists..
7956 // we look for { errors : { needs_confirm : true }} in the response.
7958 (typeof(action.result) != 'undefined') &&
7959 (typeof(action.result.errors) != 'undefined') &&
7960 (typeof(action.result.errors.needs_confirm) != 'undefined')
7963 Roo.log("not supported yet");
7966 Roo.MessageBox.confirm(
7967 "Change requires confirmation",
7968 action.result.errorMsg,
7973 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7983 Roo.callback(o.failure, o.scope, [this, action]);
7984 // show an error message if no failed handler is set..
7985 if (!this.hasListener('actionfailed')) {
7986 Roo.log("need to add dialog support");
7988 Roo.MessageBox.alert("Error",
7989 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7990 action.result.errorMsg :
7991 "Saving Failed, please check your entries or try again"
7996 this.fireEvent('actionfailed', this, action);
8001 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8002 * @param {String} id The value to search for
8005 findField : function(id){
8006 var items = this.getItems();
8007 var field = items.get(id);
8009 items.each(function(f){
8010 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8017 return field || null;
8020 * Mark fields in this form invalid in bulk.
8021 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8022 * @return {BasicForm} this
8024 markInvalid : function(errors){
8025 if(errors instanceof Array){
8026 for(var i = 0, len = errors.length; i < len; i++){
8027 var fieldError = errors[i];
8028 var f = this.findField(fieldError.id);
8030 f.markInvalid(fieldError.msg);
8036 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8037 field.markInvalid(errors[id]);
8041 //Roo.each(this.childForms || [], function (f) {
8042 // f.markInvalid(errors);
8049 * Set values for fields in this form in bulk.
8050 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8051 * @return {BasicForm} this
8053 setValues : function(values){
8054 if(values instanceof Array){ // array of objects
8055 for(var i = 0, len = values.length; i < len; i++){
8057 var f = this.findField(v.id);
8059 f.setValue(v.value);
8060 if(this.trackResetOnLoad){
8061 f.originalValue = f.getValue();
8065 }else{ // object hash
8068 if(typeof values[id] != 'function' && (field = this.findField(id))){
8070 if (field.setFromData &&
8072 field.displayField &&
8073 // combos' with local stores can
8074 // be queried via setValue()
8075 // to set their value..
8076 (field.store && !field.store.isLocal)
8080 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8081 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8082 field.setFromData(sd);
8084 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8086 field.setFromData(values);
8089 field.setValue(values[id]);
8093 if(this.trackResetOnLoad){
8094 field.originalValue = field.getValue();
8100 //Roo.each(this.childForms || [], function (f) {
8101 // f.setValues(values);
8108 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8109 * they are returned as an array.
8110 * @param {Boolean} asString
8113 getValues : function(asString){
8114 //if (this.childForms) {
8115 // copy values from the child forms
8116 // Roo.each(this.childForms, function (f) {
8117 // this.setValues(f.getValues());
8123 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8124 if(asString === true){
8127 return Roo.urlDecode(fs);
8131 * Returns the fields in this form as an object with key/value pairs.
8132 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8135 getFieldValues : function(with_hidden)
8137 var items = this.getItems();
8139 items.each(function(f){
8145 var v = f.getValue();
8147 if (f.inputType =='radio') {
8148 if (typeof(ret[f.getName()]) == 'undefined') {
8149 ret[f.getName()] = ''; // empty..
8152 if (!f.el.dom.checked) {
8160 if(f.xtype == 'MoneyField'){
8161 ret[f.currencyName] = f.getCurrency();
8164 // not sure if this supported any more..
8165 if ((typeof(v) == 'object') && f.getRawValue) {
8166 v = f.getRawValue() ; // dates..
8168 // combo boxes where name != hiddenName...
8169 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8170 ret[f.name] = f.getRawValue();
8172 ret[f.getName()] = v;
8179 * Clears all invalid messages in this form.
8180 * @return {BasicForm} this
8182 clearInvalid : function(){
8183 var items = this.getItems();
8185 items.each(function(f){
8194 * @return {BasicForm} this
8197 var items = this.getItems();
8198 items.each(function(f){
8202 Roo.each(this.childForms || [], function (f) {
8210 getItems : function()
8212 var r=new Roo.util.MixedCollection(false, function(o){
8213 return o.id || (o.id = Roo.id());
8215 var iter = function(el) {
8222 Roo.each(el.items,function(e) {
8231 hideFields : function(items)
8233 Roo.each(items, function(i){
8235 var f = this.findField(i);
8241 if(f.xtype == 'DateField'){
8242 f.setVisible(false);
8251 showFields : function(items)
8253 Roo.each(items, function(i){
8255 var f = this.findField(i);
8261 if(f.xtype == 'DateField'){
8273 Roo.apply(Roo.bootstrap.Form, {
8300 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8301 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8302 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8303 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8306 this.maskEl.top.enableDisplayMode("block");
8307 this.maskEl.left.enableDisplayMode("block");
8308 this.maskEl.bottom.enableDisplayMode("block");
8309 this.maskEl.right.enableDisplayMode("block");
8311 this.toolTip = new Roo.bootstrap.Tooltip({
8312 cls : 'roo-form-error-popover',
8314 'left' : ['r-l', [-2,0], 'right'],
8315 'right' : ['l-r', [2,0], 'left'],
8316 'bottom' : ['tl-bl', [0,2], 'top'],
8317 'top' : [ 'bl-tl', [0,-2], 'bottom']
8321 this.toolTip.render(Roo.get(document.body));
8323 this.toolTip.el.enableDisplayMode("block");
8325 Roo.get(document.body).on('click', function(){
8329 Roo.get(document.body).on('touchstart', function(){
8333 this.isApplied = true
8336 mask : function(form, target)
8340 this.target = target;
8342 if(!this.form.errorMask || !target.el){
8346 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8348 Roo.log(scrollable);
8350 var ot = this.target.el.calcOffsetsTo(scrollable);
8352 var scrollTo = ot[1] - this.form.maskOffset;
8354 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8356 scrollable.scrollTo('top', scrollTo);
8358 var box = this.target.el.getBox();
8360 var zIndex = Roo.bootstrap.Modal.zIndex++;
8363 this.maskEl.top.setStyle('position', 'absolute');
8364 this.maskEl.top.setStyle('z-index', zIndex);
8365 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8366 this.maskEl.top.setLeft(0);
8367 this.maskEl.top.setTop(0);
8368 this.maskEl.top.show();
8370 this.maskEl.left.setStyle('position', 'absolute');
8371 this.maskEl.left.setStyle('z-index', zIndex);
8372 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8373 this.maskEl.left.setLeft(0);
8374 this.maskEl.left.setTop(box.y - this.padding);
8375 this.maskEl.left.show();
8377 this.maskEl.bottom.setStyle('position', 'absolute');
8378 this.maskEl.bottom.setStyle('z-index', zIndex);
8379 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8380 this.maskEl.bottom.setLeft(0);
8381 this.maskEl.bottom.setTop(box.bottom + this.padding);
8382 this.maskEl.bottom.show();
8384 this.maskEl.right.setStyle('position', 'absolute');
8385 this.maskEl.right.setStyle('z-index', zIndex);
8386 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8387 this.maskEl.right.setLeft(box.right + this.padding);
8388 this.maskEl.right.setTop(box.y - this.padding);
8389 this.maskEl.right.show();
8391 this.toolTip.bindEl = this.target.el;
8393 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8395 var tip = this.target.blankText;
8397 if(this.target.getValue() !== '' ) {
8399 if (this.target.invalidText.length) {
8400 tip = this.target.invalidText;
8401 } else if (this.target.regexText.length){
8402 tip = this.target.regexText;
8406 this.toolTip.show(tip);
8408 this.intervalID = window.setInterval(function() {
8409 Roo.bootstrap.Form.popover.unmask();
8412 window.onwheel = function(){ return false;};
8414 (function(){ this.isMasked = true; }).defer(500, this);
8420 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8424 this.maskEl.top.setStyle('position', 'absolute');
8425 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8426 this.maskEl.top.hide();
8428 this.maskEl.left.setStyle('position', 'absolute');
8429 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8430 this.maskEl.left.hide();
8432 this.maskEl.bottom.setStyle('position', 'absolute');
8433 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8434 this.maskEl.bottom.hide();
8436 this.maskEl.right.setStyle('position', 'absolute');
8437 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8438 this.maskEl.right.hide();
8440 this.toolTip.hide();
8442 this.toolTip.el.hide();
8444 window.onwheel = function(){ return true;};
8446 if(this.intervalID){
8447 window.clearInterval(this.intervalID);
8448 this.intervalID = false;
8451 this.isMasked = false;
8461 * Ext JS Library 1.1.1
8462 * Copyright(c) 2006-2007, Ext JS, LLC.
8464 * Originally Released Under LGPL - original licence link has changed is not relivant.
8467 * <script type="text/javascript">
8470 * @class Roo.form.VTypes
8471 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8474 Roo.form.VTypes = function(){
8475 // closure these in so they are only created once.
8476 var alpha = /^[a-zA-Z_]+$/;
8477 var alphanum = /^[a-zA-Z0-9_]+$/;
8478 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8479 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8481 // All these messages and functions are configurable
8484 * The function used to validate email addresses
8485 * @param {String} value The email address
8487 'email' : function(v){
8488 return email.test(v);
8491 * The error text to display when the email validation function returns false
8494 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8496 * The keystroke filter mask to be applied on email input
8499 'emailMask' : /[a-z0-9_\.\-@]/i,
8502 * The function used to validate URLs
8503 * @param {String} value The URL
8505 'url' : function(v){
8509 * The error text to display when the url validation function returns false
8512 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8515 * The function used to validate alpha values
8516 * @param {String} value The value
8518 'alpha' : function(v){
8519 return alpha.test(v);
8522 * The error text to display when the alpha validation function returns false
8525 'alphaText' : 'This field should only contain letters and _',
8527 * The keystroke filter mask to be applied on alpha input
8530 'alphaMask' : /[a-z_]/i,
8533 * The function used to validate alphanumeric values
8534 * @param {String} value The value
8536 'alphanum' : function(v){
8537 return alphanum.test(v);
8540 * The error text to display when the alphanumeric validation function returns false
8543 'alphanumText' : 'This field should only contain letters, numbers and _',
8545 * The keystroke filter mask to be applied on alphanumeric input
8548 'alphanumMask' : /[a-z0-9_]/i
8558 * @class Roo.bootstrap.Input
8559 * @extends Roo.bootstrap.Component
8560 * Bootstrap Input class
8561 * @cfg {Boolean} disabled is it disabled
8562 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8563 * @cfg {String} name name of the input
8564 * @cfg {string} fieldLabel - the label associated
8565 * @cfg {string} placeholder - placeholder to put in text.
8566 * @cfg {string} before - input group add on before
8567 * @cfg {string} after - input group add on after
8568 * @cfg {string} size - (lg|sm) or leave empty..
8569 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8570 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8571 * @cfg {Number} md colspan out of 12 for computer-sized screens
8572 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8573 * @cfg {string} value default value of the input
8574 * @cfg {Number} labelWidth set the width of label
8575 * @cfg {Number} labellg set the width of label (1-12)
8576 * @cfg {Number} labelmd set the width of label (1-12)
8577 * @cfg {Number} labelsm set the width of label (1-12)
8578 * @cfg {Number} labelxs set the width of label (1-12)
8579 * @cfg {String} labelAlign (top|left)
8580 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8581 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8582 * @cfg {String} indicatorpos (left|right) default left
8583 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8584 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8586 * @cfg {String} align (left|center|right) Default left
8587 * @cfg {Boolean} forceFeedback (true|false) Default false
8590 * Create a new Input
8591 * @param {Object} config The config object
8594 Roo.bootstrap.Input = function(config){
8596 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8601 * Fires when this field receives input focus.
8602 * @param {Roo.form.Field} this
8607 * Fires when this field loses input focus.
8608 * @param {Roo.form.Field} this
8613 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8614 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8615 * @param {Roo.form.Field} this
8616 * @param {Roo.EventObject} e The event object
8621 * Fires just before the field blurs if the field value has changed.
8622 * @param {Roo.form.Field} this
8623 * @param {Mixed} newValue The new value
8624 * @param {Mixed} oldValue The original value
8629 * Fires after the field has been marked as invalid.
8630 * @param {Roo.form.Field} this
8631 * @param {String} msg The validation message
8636 * Fires after the field has been validated with no errors.
8637 * @param {Roo.form.Field} this
8642 * Fires after the key up
8643 * @param {Roo.form.Field} this
8644 * @param {Roo.EventObject} e The event Object
8650 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8652 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8653 automatic validation (defaults to "keyup").
8655 validationEvent : "keyup",
8657 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8659 validateOnBlur : true,
8661 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8663 validationDelay : 250,
8665 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8667 focusClass : "x-form-focus", // not needed???
8671 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8673 invalidClass : "has-warning",
8676 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8678 validClass : "has-success",
8681 * @cfg {Boolean} hasFeedback (true|false) default true
8686 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8688 invalidFeedbackClass : "glyphicon-warning-sign",
8691 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8693 validFeedbackClass : "glyphicon-ok",
8696 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8698 selectOnFocus : false,
8701 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8705 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8710 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8712 disableKeyFilter : false,
8715 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8719 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8723 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8725 blankText : "Please complete this mandatory field",
8728 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8732 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8734 maxLength : Number.MAX_VALUE,
8736 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8738 minLengthText : "The minimum length for this field is {0}",
8740 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8742 maxLengthText : "The maximum length for this field is {0}",
8746 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8747 * If available, this function will be called only after the basic validators all return true, and will be passed the
8748 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8752 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8753 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8754 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8758 * @cfg {String} regexText -- Depricated - use Invalid Text
8763 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8769 autocomplete: false,
8788 formatedValue : false,
8789 forceFeedback : false,
8791 indicatorpos : 'left',
8801 parentLabelAlign : function()
8804 while (parent.parent()) {
8805 parent = parent.parent();
8806 if (typeof(parent.labelAlign) !='undefined') {
8807 return parent.labelAlign;
8814 getAutoCreate : function()
8816 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8822 if(this.inputType != 'hidden'){
8823 cfg.cls = 'form-group' //input-group
8829 type : this.inputType,
8831 cls : 'form-control',
8832 placeholder : this.placeholder || '',
8833 autocomplete : this.autocomplete || 'new-password'
8836 if(this.capture.length){
8837 input.capture = this.capture;
8840 if(this.accept.length){
8841 input.accept = this.accept + "/*";
8845 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8848 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8849 input.maxLength = this.maxLength;
8852 if (this.disabled) {
8853 input.disabled=true;
8856 if (this.readOnly) {
8857 input.readonly=true;
8861 input.name = this.name;
8865 input.cls += ' input-' + this.size;
8869 ['xs','sm','md','lg'].map(function(size){
8870 if (settings[size]) {
8871 cfg.cls += ' col-' + size + '-' + settings[size];
8875 var inputblock = input;
8879 cls: 'glyphicon form-control-feedback'
8882 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8885 cls : 'has-feedback',
8893 if (this.before || this.after) {
8896 cls : 'input-group',
8900 if (this.before && typeof(this.before) == 'string') {
8902 inputblock.cn.push({
8904 cls : 'roo-input-before input-group-addon',
8908 if (this.before && typeof(this.before) == 'object') {
8909 this.before = Roo.factory(this.before);
8911 inputblock.cn.push({
8913 cls : 'roo-input-before input-group-' +
8914 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8918 inputblock.cn.push(input);
8920 if (this.after && typeof(this.after) == 'string') {
8921 inputblock.cn.push({
8923 cls : 'roo-input-after input-group-addon',
8927 if (this.after && typeof(this.after) == 'object') {
8928 this.after = Roo.factory(this.after);
8930 inputblock.cn.push({
8932 cls : 'roo-input-after input-group-' +
8933 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8937 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8938 inputblock.cls += ' has-feedback';
8939 inputblock.cn.push(feedback);
8943 if (align ==='left' && this.fieldLabel.length) {
8945 cfg.cls += ' roo-form-group-label-left';
8950 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8951 tooltip : 'This field is required'
8956 cls : 'control-label',
8957 html : this.fieldLabel
8968 var labelCfg = cfg.cn[1];
8969 var contentCfg = cfg.cn[2];
8971 if(this.indicatorpos == 'right'){
8976 cls : 'control-label',
8980 html : this.fieldLabel
8984 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8985 tooltip : 'This field is required'
8998 labelCfg = cfg.cn[0];
8999 contentCfg = cfg.cn[1];
9003 if(this.labelWidth > 12){
9004 labelCfg.style = "width: " + this.labelWidth + 'px';
9007 if(this.labelWidth < 13 && this.labelmd == 0){
9008 this.labelmd = this.labelWidth;
9011 if(this.labellg > 0){
9012 labelCfg.cls += ' col-lg-' + this.labellg;
9013 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9016 if(this.labelmd > 0){
9017 labelCfg.cls += ' col-md-' + this.labelmd;
9018 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9021 if(this.labelsm > 0){
9022 labelCfg.cls += ' col-sm-' + this.labelsm;
9023 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9026 if(this.labelxs > 0){
9027 labelCfg.cls += ' col-xs-' + this.labelxs;
9028 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9032 } else if ( this.fieldLabel.length) {
9037 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9038 tooltip : 'This field is required'
9042 //cls : 'input-group-addon',
9043 html : this.fieldLabel
9051 if(this.indicatorpos == 'right'){
9056 //cls : 'input-group-addon',
9057 html : this.fieldLabel
9062 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9063 tooltip : 'This field is required'
9083 if (this.parentType === 'Navbar' && this.parent().bar) {
9084 cfg.cls += ' navbar-form';
9087 if (this.parentType === 'NavGroup') {
9088 cfg.cls += ' navbar-form';
9096 * return the real input element.
9098 inputEl: function ()
9100 return this.el.select('input.form-control',true).first();
9103 tooltipEl : function()
9105 return this.inputEl();
9108 indicatorEl : function()
9110 var indicator = this.el.select('i.roo-required-indicator',true).first();
9120 setDisabled : function(v)
9122 var i = this.inputEl().dom;
9124 i.removeAttribute('disabled');
9128 i.setAttribute('disabled','true');
9130 initEvents : function()
9133 this.inputEl().on("keydown" , this.fireKey, this);
9134 this.inputEl().on("focus", this.onFocus, this);
9135 this.inputEl().on("blur", this.onBlur, this);
9137 this.inputEl().relayEvent('keyup', this);
9139 this.indicator = this.indicatorEl();
9142 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9145 // reference to original value for reset
9146 this.originalValue = this.getValue();
9147 //Roo.form.TextField.superclass.initEvents.call(this);
9148 if(this.validationEvent == 'keyup'){
9149 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9150 this.inputEl().on('keyup', this.filterValidation, this);
9152 else if(this.validationEvent !== false){
9153 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9156 if(this.selectOnFocus){
9157 this.on("focus", this.preFocus, this);
9160 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9161 this.inputEl().on("keypress", this.filterKeys, this);
9163 this.inputEl().relayEvent('keypress', this);
9166 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9167 this.el.on("click", this.autoSize, this);
9170 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9171 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9174 if (typeof(this.before) == 'object') {
9175 this.before.render(this.el.select('.roo-input-before',true).first());
9177 if (typeof(this.after) == 'object') {
9178 this.after.render(this.el.select('.roo-input-after',true).first());
9181 this.inputEl().on('change', this.onChange, this);
9184 filterValidation : function(e){
9185 if(!e.isNavKeyPress()){
9186 this.validationTask.delay(this.validationDelay);
9190 * Validates the field value
9191 * @return {Boolean} True if the value is valid, else false
9193 validate : function(){
9194 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9195 if(this.disabled || this.validateValue(this.getRawValue())){
9206 * Validates a value according to the field's validation rules and marks the field as invalid
9207 * if the validation fails
9208 * @param {Mixed} value The value to validate
9209 * @return {Boolean} True if the value is valid, else false
9211 validateValue : function(value)
9213 if(this.getVisibilityEl().hasClass('hidden')){
9217 if(value.length < 1) { // if it's blank
9218 if(this.allowBlank){
9224 if(value.length < this.minLength){
9227 if(value.length > this.maxLength){
9231 var vt = Roo.form.VTypes;
9232 if(!vt[this.vtype](value, this)){
9236 if(typeof this.validator == "function"){
9237 var msg = this.validator(value);
9241 if (typeof(msg) == 'string') {
9242 this.invalidText = msg;
9246 if(this.regex && !this.regex.test(value)){
9254 fireKey : function(e){
9255 //Roo.log('field ' + e.getKey());
9256 if(e.isNavKeyPress()){
9257 this.fireEvent("specialkey", this, e);
9260 focus : function (selectText){
9262 this.inputEl().focus();
9263 if(selectText === true){
9264 this.inputEl().dom.select();
9270 onFocus : function(){
9271 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9272 // this.el.addClass(this.focusClass);
9275 this.hasFocus = true;
9276 this.startValue = this.getValue();
9277 this.fireEvent("focus", this);
9281 beforeBlur : Roo.emptyFn,
9285 onBlur : function(){
9287 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9288 //this.el.removeClass(this.focusClass);
9290 this.hasFocus = false;
9291 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9294 var v = this.getValue();
9295 if(String(v) !== String(this.startValue)){
9296 this.fireEvent('change', this, v, this.startValue);
9298 this.fireEvent("blur", this);
9301 onChange : function(e)
9303 var v = this.getValue();
9304 if(String(v) !== String(this.startValue)){
9305 this.fireEvent('change', this, v, this.startValue);
9311 * Resets the current field value to the originally loaded value and clears any validation messages
9314 this.setValue(this.originalValue);
9318 * Returns the name of the field
9319 * @return {Mixed} name The name field
9321 getName: function(){
9325 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9326 * @return {Mixed} value The field value
9328 getValue : function(){
9330 var v = this.inputEl().getValue();
9335 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9336 * @return {Mixed} value The field value
9338 getRawValue : function(){
9339 var v = this.inputEl().getValue();
9345 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9346 * @param {Mixed} value The value to set
9348 setRawValue : function(v){
9349 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9352 selectText : function(start, end){
9353 var v = this.getRawValue();
9355 start = start === undefined ? 0 : start;
9356 end = end === undefined ? v.length : end;
9357 var d = this.inputEl().dom;
9358 if(d.setSelectionRange){
9359 d.setSelectionRange(start, end);
9360 }else if(d.createTextRange){
9361 var range = d.createTextRange();
9362 range.moveStart("character", start);
9363 range.moveEnd("character", v.length-end);
9370 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9371 * @param {Mixed} value The value to set
9373 setValue : function(v){
9376 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9382 processValue : function(value){
9383 if(this.stripCharsRe){
9384 var newValue = value.replace(this.stripCharsRe, '');
9385 if(newValue !== value){
9386 this.setRawValue(newValue);
9393 preFocus : function(){
9395 if(this.selectOnFocus){
9396 this.inputEl().dom.select();
9399 filterKeys : function(e){
9401 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9404 var c = e.getCharCode(), cc = String.fromCharCode(c);
9405 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9408 if(!this.maskRe.test(cc)){
9413 * Clear any invalid styles/messages for this field
9415 clearInvalid : function(){
9417 if(!this.el || this.preventMark){ // not rendered
9422 this.el.removeClass(this.invalidClass);
9424 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9426 var feedback = this.el.select('.form-control-feedback', true).first();
9429 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9435 this.indicator.removeClass('visible');
9436 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9439 this.fireEvent('valid', this);
9443 * Mark this field as valid
9445 markValid : function()
9447 if(!this.el || this.preventMark){ // not rendered...
9451 this.el.removeClass([this.invalidClass, this.validClass]);
9453 var feedback = this.el.select('.form-control-feedback', true).first();
9456 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9460 this.indicator.removeClass('visible');
9461 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9468 if(this.allowBlank && !this.getRawValue().length){
9472 this.el.addClass(this.validClass);
9474 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9476 var feedback = this.el.select('.form-control-feedback', true).first();
9479 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9480 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9485 this.fireEvent('valid', this);
9489 * Mark this field as invalid
9490 * @param {String} msg The validation message
9492 markInvalid : function(msg)
9494 if(!this.el || this.preventMark){ // not rendered
9498 this.el.removeClass([this.invalidClass, this.validClass]);
9500 var feedback = this.el.select('.form-control-feedback', true).first();
9503 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9510 if(this.allowBlank && !this.getRawValue().length){
9515 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9516 this.indicator.addClass('visible');
9519 this.el.addClass(this.invalidClass);
9521 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9523 var feedback = this.el.select('.form-control-feedback', true).first();
9526 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9528 if(this.getValue().length || this.forceFeedback){
9529 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9536 this.fireEvent('invalid', this, msg);
9539 SafariOnKeyDown : function(event)
9541 // this is a workaround for a password hang bug on chrome/ webkit.
9542 if (this.inputEl().dom.type != 'password') {
9546 var isSelectAll = false;
9548 if(this.inputEl().dom.selectionEnd > 0){
9549 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9551 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9552 event.preventDefault();
9557 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9559 event.preventDefault();
9560 // this is very hacky as keydown always get's upper case.
9562 var cc = String.fromCharCode(event.getCharCode());
9563 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9567 adjustWidth : function(tag, w){
9568 tag = tag.toLowerCase();
9569 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9570 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9574 if(tag == 'textarea'){
9577 }else if(Roo.isOpera){
9581 if(tag == 'textarea'){
9589 setFieldLabel : function(v)
9596 var ar = this.el.select('label > span',true);
9598 if (ar.elements.length) {
9599 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9600 this.fieldLabel = v;
9604 var br = this.el.select('label',true);
9606 if(br.elements.length) {
9607 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9608 this.fieldLabel = v;
9612 Roo.log('Cannot Found any of label > span || label in input');
9616 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9617 this.fieldLabel = v;
9632 * @class Roo.bootstrap.TextArea
9633 * @extends Roo.bootstrap.Input
9634 * Bootstrap TextArea class
9635 * @cfg {Number} cols Specifies the visible width of a text area
9636 * @cfg {Number} rows Specifies the visible number of lines in a text area
9637 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9638 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9639 * @cfg {string} html text
9642 * Create a new TextArea
9643 * @param {Object} config The config object
9646 Roo.bootstrap.TextArea = function(config){
9647 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9651 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9661 getAutoCreate : function(){
9663 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9669 if(this.inputType != 'hidden'){
9670 cfg.cls = 'form-group' //input-group
9678 value : this.value || '',
9679 html: this.html || '',
9680 cls : 'form-control',
9681 placeholder : this.placeholder || ''
9685 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9686 input.maxLength = this.maxLength;
9690 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9694 input.cols = this.cols;
9697 if (this.readOnly) {
9698 input.readonly = true;
9702 input.name = this.name;
9706 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9710 ['xs','sm','md','lg'].map(function(size){
9711 if (settings[size]) {
9712 cfg.cls += ' col-' + size + '-' + settings[size];
9716 var inputblock = input;
9718 if(this.hasFeedback && !this.allowBlank){
9722 cls: 'glyphicon form-control-feedback'
9726 cls : 'has-feedback',
9735 if (this.before || this.after) {
9738 cls : 'input-group',
9742 inputblock.cn.push({
9744 cls : 'input-group-addon',
9749 inputblock.cn.push(input);
9751 if(this.hasFeedback && !this.allowBlank){
9752 inputblock.cls += ' has-feedback';
9753 inputblock.cn.push(feedback);
9757 inputblock.cn.push({
9759 cls : 'input-group-addon',
9766 if (align ==='left' && this.fieldLabel.length) {
9771 cls : 'control-label',
9772 html : this.fieldLabel
9783 if(this.labelWidth > 12){
9784 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9787 if(this.labelWidth < 13 && this.labelmd == 0){
9788 this.labelmd = this.labelWidth;
9791 if(this.labellg > 0){
9792 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9793 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9796 if(this.labelmd > 0){
9797 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9798 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9801 if(this.labelsm > 0){
9802 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9803 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9806 if(this.labelxs > 0){
9807 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9808 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9811 } else if ( this.fieldLabel.length) {
9816 //cls : 'input-group-addon',
9817 html : this.fieldLabel
9835 if (this.disabled) {
9836 input.disabled=true;
9843 * return the real textarea element.
9845 inputEl: function ()
9847 return this.el.select('textarea.form-control',true).first();
9851 * Clear any invalid styles/messages for this field
9853 clearInvalid : function()
9856 if(!this.el || this.preventMark){ // not rendered
9860 var label = this.el.select('label', true).first();
9861 var icon = this.el.select('i.fa-star', true).first();
9867 this.el.removeClass(this.invalidClass);
9869 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9871 var feedback = this.el.select('.form-control-feedback', true).first();
9874 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9879 this.fireEvent('valid', this);
9883 * Mark this field as valid
9885 markValid : function()
9887 if(!this.el || this.preventMark){ // not rendered
9891 this.el.removeClass([this.invalidClass, this.validClass]);
9893 var feedback = this.el.select('.form-control-feedback', true).first();
9896 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9899 if(this.disabled || this.allowBlank){
9903 var label = this.el.select('label', true).first();
9904 var icon = this.el.select('i.fa-star', true).first();
9910 this.el.addClass(this.validClass);
9912 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9914 var feedback = this.el.select('.form-control-feedback', true).first();
9917 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9918 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9923 this.fireEvent('valid', this);
9927 * Mark this field as invalid
9928 * @param {String} msg The validation message
9930 markInvalid : function(msg)
9932 if(!this.el || this.preventMark){ // not rendered
9936 this.el.removeClass([this.invalidClass, this.validClass]);
9938 var feedback = this.el.select('.form-control-feedback', true).first();
9941 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9944 if(this.disabled || this.allowBlank){
9948 var label = this.el.select('label', true).first();
9949 var icon = this.el.select('i.fa-star', true).first();
9951 if(!this.getValue().length && label && !icon){
9952 this.el.createChild({
9954 cls : 'text-danger fa fa-lg fa-star',
9955 tooltip : 'This field is required',
9956 style : 'margin-right:5px;'
9960 this.el.addClass(this.invalidClass);
9962 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9964 var feedback = this.el.select('.form-control-feedback', true).first();
9967 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9969 if(this.getValue().length || this.forceFeedback){
9970 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9977 this.fireEvent('invalid', this, msg);
9985 * trigger field - base class for combo..
9990 * @class Roo.bootstrap.TriggerField
9991 * @extends Roo.bootstrap.Input
9992 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9993 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9994 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9995 * for which you can provide a custom implementation. For example:
9997 var trigger = new Roo.bootstrap.TriggerField();
9998 trigger.onTriggerClick = myTriggerFn;
9999 trigger.applyTo('my-field');
10002 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10003 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10004 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10005 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10006 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10009 * Create a new TriggerField.
10010 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10011 * to the base TextField)
10013 Roo.bootstrap.TriggerField = function(config){
10014 this.mimicing = false;
10015 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10018 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10020 * @cfg {String} triggerClass A CSS class to apply to the trigger
10023 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10028 * @cfg {Boolean} removable (true|false) special filter default false
10032 /** @cfg {Boolean} grow @hide */
10033 /** @cfg {Number} growMin @hide */
10034 /** @cfg {Number} growMax @hide */
10040 autoSize: Roo.emptyFn,
10044 deferHeight : true,
10047 actionMode : 'wrap',
10052 getAutoCreate : function(){
10054 var align = this.labelAlign || this.parentLabelAlign();
10059 cls: 'form-group' //input-group
10066 type : this.inputType,
10067 cls : 'form-control',
10068 autocomplete: 'new-password',
10069 placeholder : this.placeholder || ''
10073 input.name = this.name;
10076 input.cls += ' input-' + this.size;
10079 if (this.disabled) {
10080 input.disabled=true;
10083 var inputblock = input;
10085 if(this.hasFeedback && !this.allowBlank){
10089 cls: 'glyphicon form-control-feedback'
10092 if(this.removable && !this.editable && !this.tickable){
10094 cls : 'has-feedback',
10100 cls : 'roo-combo-removable-btn close'
10107 cls : 'has-feedback',
10116 if(this.removable && !this.editable && !this.tickable){
10118 cls : 'roo-removable',
10124 cls : 'roo-combo-removable-btn close'
10131 if (this.before || this.after) {
10134 cls : 'input-group',
10138 inputblock.cn.push({
10140 cls : 'input-group-addon',
10145 inputblock.cn.push(input);
10147 if(this.hasFeedback && !this.allowBlank){
10148 inputblock.cls += ' has-feedback';
10149 inputblock.cn.push(feedback);
10153 inputblock.cn.push({
10155 cls : 'input-group-addon',
10168 cls: 'form-hidden-field'
10182 cls: 'form-hidden-field'
10186 cls: 'roo-select2-choices',
10190 cls: 'roo-select2-search-field',
10203 cls: 'roo-select2-container input-group',
10208 // cls: 'typeahead typeahead-long dropdown-menu',
10209 // style: 'display:none'
10214 if(!this.multiple && this.showToggleBtn){
10220 if (this.caret != false) {
10223 cls: 'fa fa-' + this.caret
10230 cls : 'input-group-addon btn dropdown-toggle',
10235 cls: 'combobox-clear',
10249 combobox.cls += ' roo-select2-container-multi';
10252 if (align ==='left' && this.fieldLabel.length) {
10254 cfg.cls += ' roo-form-group-label-left';
10259 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10260 tooltip : 'This field is required'
10265 cls : 'control-label',
10266 html : this.fieldLabel
10278 var labelCfg = cfg.cn[1];
10279 var contentCfg = cfg.cn[2];
10281 if(this.indicatorpos == 'right'){
10286 cls : 'control-label',
10290 html : this.fieldLabel
10294 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10295 tooltip : 'This field is required'
10308 labelCfg = cfg.cn[0];
10309 contentCfg = cfg.cn[1];
10312 if(this.labelWidth > 12){
10313 labelCfg.style = "width: " + this.labelWidth + 'px';
10316 if(this.labelWidth < 13 && this.labelmd == 0){
10317 this.labelmd = this.labelWidth;
10320 if(this.labellg > 0){
10321 labelCfg.cls += ' col-lg-' + this.labellg;
10322 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10325 if(this.labelmd > 0){
10326 labelCfg.cls += ' col-md-' + this.labelmd;
10327 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10330 if(this.labelsm > 0){
10331 labelCfg.cls += ' col-sm-' + this.labelsm;
10332 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10335 if(this.labelxs > 0){
10336 labelCfg.cls += ' col-xs-' + this.labelxs;
10337 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10340 } else if ( this.fieldLabel.length) {
10341 // Roo.log(" label");
10345 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10346 tooltip : 'This field is required'
10350 //cls : 'input-group-addon',
10351 html : this.fieldLabel
10359 if(this.indicatorpos == 'right'){
10367 html : this.fieldLabel
10371 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10372 tooltip : 'This field is required'
10385 // Roo.log(" no label && no align");
10392 ['xs','sm','md','lg'].map(function(size){
10393 if (settings[size]) {
10394 cfg.cls += ' col-' + size + '-' + settings[size];
10405 onResize : function(w, h){
10406 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10407 // if(typeof w == 'number'){
10408 // var x = w - this.trigger.getWidth();
10409 // this.inputEl().setWidth(this.adjustWidth('input', x));
10410 // this.trigger.setStyle('left', x+'px');
10415 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10418 getResizeEl : function(){
10419 return this.inputEl();
10423 getPositionEl : function(){
10424 return this.inputEl();
10428 alignErrorIcon : function(){
10429 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10433 initEvents : function(){
10437 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10438 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10439 if(!this.multiple && this.showToggleBtn){
10440 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10441 if(this.hideTrigger){
10442 this.trigger.setDisplayed(false);
10444 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10448 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10451 if(this.removable && !this.editable && !this.tickable){
10452 var close = this.closeTriggerEl();
10455 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10456 close.on('click', this.removeBtnClick, this, close);
10460 //this.trigger.addClassOnOver('x-form-trigger-over');
10461 //this.trigger.addClassOnClick('x-form-trigger-click');
10464 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10468 closeTriggerEl : function()
10470 var close = this.el.select('.roo-combo-removable-btn', true).first();
10471 return close ? close : false;
10474 removeBtnClick : function(e, h, el)
10476 e.preventDefault();
10478 if(this.fireEvent("remove", this) !== false){
10480 this.fireEvent("afterremove", this)
10484 createList : function()
10486 this.list = Roo.get(document.body).createChild({
10488 cls: 'typeahead typeahead-long dropdown-menu',
10489 style: 'display:none'
10492 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10497 initTrigger : function(){
10502 onDestroy : function(){
10504 this.trigger.removeAllListeners();
10505 // this.trigger.remove();
10508 // this.wrap.remove();
10510 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10514 onFocus : function(){
10515 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10517 if(!this.mimicing){
10518 this.wrap.addClass('x-trigger-wrap-focus');
10519 this.mimicing = true;
10520 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10521 if(this.monitorTab){
10522 this.el.on("keydown", this.checkTab, this);
10529 checkTab : function(e){
10530 if(e.getKey() == e.TAB){
10531 this.triggerBlur();
10536 onBlur : function(){
10541 mimicBlur : function(e, t){
10543 if(!this.wrap.contains(t) && this.validateBlur()){
10544 this.triggerBlur();
10550 triggerBlur : function(){
10551 this.mimicing = false;
10552 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10553 if(this.monitorTab){
10554 this.el.un("keydown", this.checkTab, this);
10556 //this.wrap.removeClass('x-trigger-wrap-focus');
10557 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10561 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10562 validateBlur : function(e, t){
10567 onDisable : function(){
10568 this.inputEl().dom.disabled = true;
10569 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10571 // this.wrap.addClass('x-item-disabled');
10576 onEnable : function(){
10577 this.inputEl().dom.disabled = false;
10578 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10580 // this.el.removeClass('x-item-disabled');
10585 onShow : function(){
10586 var ae = this.getActionEl();
10589 ae.dom.style.display = '';
10590 ae.dom.style.visibility = 'visible';
10596 onHide : function(){
10597 var ae = this.getActionEl();
10598 ae.dom.style.display = 'none';
10602 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10603 * by an implementing function.
10605 * @param {EventObject} e
10607 onTriggerClick : Roo.emptyFn
10611 * Ext JS Library 1.1.1
10612 * Copyright(c) 2006-2007, Ext JS, LLC.
10614 * Originally Released Under LGPL - original licence link has changed is not relivant.
10617 * <script type="text/javascript">
10622 * @class Roo.data.SortTypes
10624 * Defines the default sorting (casting?) comparison functions used when sorting data.
10626 Roo.data.SortTypes = {
10628 * Default sort that does nothing
10629 * @param {Mixed} s The value being converted
10630 * @return {Mixed} The comparison value
10632 none : function(s){
10637 * The regular expression used to strip tags
10641 stripTagsRE : /<\/?[^>]+>/gi,
10644 * Strips all HTML tags to sort on text only
10645 * @param {Mixed} s The value being converted
10646 * @return {String} The comparison value
10648 asText : function(s){
10649 return String(s).replace(this.stripTagsRE, "");
10653 * Strips all HTML tags to sort on text only - Case insensitive
10654 * @param {Mixed} s The value being converted
10655 * @return {String} The comparison value
10657 asUCText : function(s){
10658 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10662 * Case insensitive string
10663 * @param {Mixed} s The value being converted
10664 * @return {String} The comparison value
10666 asUCString : function(s) {
10667 return String(s).toUpperCase();
10672 * @param {Mixed} s The value being converted
10673 * @return {Number} The comparison value
10675 asDate : function(s) {
10679 if(s instanceof Date){
10680 return s.getTime();
10682 return Date.parse(String(s));
10687 * @param {Mixed} s The value being converted
10688 * @return {Float} The comparison value
10690 asFloat : function(s) {
10691 var val = parseFloat(String(s).replace(/,/g, ""));
10700 * @param {Mixed} s The value being converted
10701 * @return {Number} The comparison value
10703 asInt : function(s) {
10704 var val = parseInt(String(s).replace(/,/g, ""));
10712 * Ext JS Library 1.1.1
10713 * Copyright(c) 2006-2007, Ext JS, LLC.
10715 * Originally Released Under LGPL - original licence link has changed is not relivant.
10718 * <script type="text/javascript">
10722 * @class Roo.data.Record
10723 * Instances of this class encapsulate both record <em>definition</em> information, and record
10724 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10725 * to access Records cached in an {@link Roo.data.Store} object.<br>
10727 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10728 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10731 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10733 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10734 * {@link #create}. The parameters are the same.
10735 * @param {Array} data An associative Array of data values keyed by the field name.
10736 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10737 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10738 * not specified an integer id is generated.
10740 Roo.data.Record = function(data, id){
10741 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10746 * Generate a constructor for a specific record layout.
10747 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10748 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10749 * Each field definition object may contain the following properties: <ul>
10750 * <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,
10751 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10752 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10753 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10754 * is being used, then this is a string containing the javascript expression to reference the data relative to
10755 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10756 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10757 * this may be omitted.</p></li>
10758 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10759 * <ul><li>auto (Default, implies no conversion)</li>
10764 * <li>date</li></ul></p></li>
10765 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10766 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10767 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10768 * by the Reader into an object that will be stored in the Record. It is passed the
10769 * following parameters:<ul>
10770 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10772 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10774 * <br>usage:<br><pre><code>
10775 var TopicRecord = Roo.data.Record.create(
10776 {name: 'title', mapping: 'topic_title'},
10777 {name: 'author', mapping: 'username'},
10778 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10779 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10780 {name: 'lastPoster', mapping: 'user2'},
10781 {name: 'excerpt', mapping: 'post_text'}
10784 var myNewRecord = new TopicRecord({
10785 title: 'Do my job please',
10788 lastPost: new Date(),
10789 lastPoster: 'Animal',
10790 excerpt: 'No way dude!'
10792 myStore.add(myNewRecord);
10797 Roo.data.Record.create = function(o){
10798 var f = function(){
10799 f.superclass.constructor.apply(this, arguments);
10801 Roo.extend(f, Roo.data.Record);
10802 var p = f.prototype;
10803 p.fields = new Roo.util.MixedCollection(false, function(field){
10806 for(var i = 0, len = o.length; i < len; i++){
10807 p.fields.add(new Roo.data.Field(o[i]));
10809 f.getField = function(name){
10810 return p.fields.get(name);
10815 Roo.data.Record.AUTO_ID = 1000;
10816 Roo.data.Record.EDIT = 'edit';
10817 Roo.data.Record.REJECT = 'reject';
10818 Roo.data.Record.COMMIT = 'commit';
10820 Roo.data.Record.prototype = {
10822 * Readonly flag - true if this record has been modified.
10831 join : function(store){
10832 this.store = store;
10836 * Set the named field to the specified value.
10837 * @param {String} name The name of the field to set.
10838 * @param {Object} value The value to set the field to.
10840 set : function(name, value){
10841 if(this.data[name] == value){
10845 if(!this.modified){
10846 this.modified = {};
10848 if(typeof this.modified[name] == 'undefined'){
10849 this.modified[name] = this.data[name];
10851 this.data[name] = value;
10852 if(!this.editing && this.store){
10853 this.store.afterEdit(this);
10858 * Get the value of the named field.
10859 * @param {String} name The name of the field to get the value of.
10860 * @return {Object} The value of the field.
10862 get : function(name){
10863 return this.data[name];
10867 beginEdit : function(){
10868 this.editing = true;
10869 this.modified = {};
10873 cancelEdit : function(){
10874 this.editing = false;
10875 delete this.modified;
10879 endEdit : function(){
10880 this.editing = false;
10881 if(this.dirty && this.store){
10882 this.store.afterEdit(this);
10887 * Usually called by the {@link Roo.data.Store} which owns the Record.
10888 * Rejects all changes made to the Record since either creation, or the last commit operation.
10889 * Modified fields are reverted to their original values.
10891 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10892 * of reject operations.
10894 reject : function(){
10895 var m = this.modified;
10897 if(typeof m[n] != "function"){
10898 this.data[n] = m[n];
10901 this.dirty = false;
10902 delete this.modified;
10903 this.editing = false;
10905 this.store.afterReject(this);
10910 * Usually called by the {@link Roo.data.Store} which owns the Record.
10911 * Commits all changes made to the Record since either creation, or the last commit operation.
10913 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10914 * of commit operations.
10916 commit : function(){
10917 this.dirty = false;
10918 delete this.modified;
10919 this.editing = false;
10921 this.store.afterCommit(this);
10926 hasError : function(){
10927 return this.error != null;
10931 clearError : function(){
10936 * Creates a copy of this record.
10937 * @param {String} id (optional) A new record id if you don't want to use this record's id
10940 copy : function(newId) {
10941 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10945 * Ext JS Library 1.1.1
10946 * Copyright(c) 2006-2007, Ext JS, LLC.
10948 * Originally Released Under LGPL - original licence link has changed is not relivant.
10951 * <script type="text/javascript">
10957 * @class Roo.data.Store
10958 * @extends Roo.util.Observable
10959 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10960 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10962 * 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
10963 * has no knowledge of the format of the data returned by the Proxy.<br>
10965 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10966 * instances from the data object. These records are cached and made available through accessor functions.
10968 * Creates a new Store.
10969 * @param {Object} config A config object containing the objects needed for the Store to access data,
10970 * and read the data into Records.
10972 Roo.data.Store = function(config){
10973 this.data = new Roo.util.MixedCollection(false);
10974 this.data.getKey = function(o){
10977 this.baseParams = {};
10979 this.paramNames = {
10984 "multisort" : "_multisort"
10987 if(config && config.data){
10988 this.inlineData = config.data;
10989 delete config.data;
10992 Roo.apply(this, config);
10994 if(this.reader){ // reader passed
10995 this.reader = Roo.factory(this.reader, Roo.data);
10996 this.reader.xmodule = this.xmodule || false;
10997 if(!this.recordType){
10998 this.recordType = this.reader.recordType;
11000 if(this.reader.onMetaChange){
11001 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11005 if(this.recordType){
11006 this.fields = this.recordType.prototype.fields;
11008 this.modified = [];
11012 * @event datachanged
11013 * Fires when the data cache has changed, and a widget which is using this Store
11014 * as a Record cache should refresh its view.
11015 * @param {Store} this
11017 datachanged : true,
11019 * @event metachange
11020 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11021 * @param {Store} this
11022 * @param {Object} meta The JSON metadata
11027 * Fires when Records have been added to the Store
11028 * @param {Store} this
11029 * @param {Roo.data.Record[]} records The array of Records added
11030 * @param {Number} index The index at which the record(s) were added
11035 * Fires when a Record has been removed from the Store
11036 * @param {Store} this
11037 * @param {Roo.data.Record} record The Record that was removed
11038 * @param {Number} index The index at which the record was removed
11043 * Fires when a Record has been updated
11044 * @param {Store} this
11045 * @param {Roo.data.Record} record The Record that was updated
11046 * @param {String} operation The update operation being performed. Value may be one of:
11048 Roo.data.Record.EDIT
11049 Roo.data.Record.REJECT
11050 Roo.data.Record.COMMIT
11056 * Fires when the data cache has been cleared.
11057 * @param {Store} this
11061 * @event beforeload
11062 * Fires before a request is made for a new data object. If the beforeload handler returns false
11063 * the load action will be canceled.
11064 * @param {Store} this
11065 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11069 * @event beforeloadadd
11070 * Fires after a new set of Records has been loaded.
11071 * @param {Store} this
11072 * @param {Roo.data.Record[]} records The Records that were loaded
11073 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11075 beforeloadadd : true,
11078 * Fires after a new set of Records has been loaded, before they are added to the store.
11079 * @param {Store} this
11080 * @param {Roo.data.Record[]} records The Records that were loaded
11081 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11082 * @params {Object} return from reader
11086 * @event loadexception
11087 * Fires if an exception occurs in the Proxy during loading.
11088 * Called with the signature of the Proxy's "loadexception" event.
11089 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11092 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11093 * @param {Object} load options
11094 * @param {Object} jsonData from your request (normally this contains the Exception)
11096 loadexception : true
11100 this.proxy = Roo.factory(this.proxy, Roo.data);
11101 this.proxy.xmodule = this.xmodule || false;
11102 this.relayEvents(this.proxy, ["loadexception"]);
11104 this.sortToggle = {};
11105 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11107 Roo.data.Store.superclass.constructor.call(this);
11109 if(this.inlineData){
11110 this.loadData(this.inlineData);
11111 delete this.inlineData;
11115 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11117 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11118 * without a remote query - used by combo/forms at present.
11122 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11125 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11128 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11129 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11132 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11133 * on any HTTP request
11136 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11139 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11143 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11144 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11146 remoteSort : false,
11149 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11150 * loaded or when a record is removed. (defaults to false).
11152 pruneModifiedRecords : false,
11155 lastOptions : null,
11158 * Add Records to the Store and fires the add event.
11159 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11161 add : function(records){
11162 records = [].concat(records);
11163 for(var i = 0, len = records.length; i < len; i++){
11164 records[i].join(this);
11166 var index = this.data.length;
11167 this.data.addAll(records);
11168 this.fireEvent("add", this, records, index);
11172 * Remove a Record from the Store and fires the remove event.
11173 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11175 remove : function(record){
11176 var index = this.data.indexOf(record);
11177 this.data.removeAt(index);
11179 if(this.pruneModifiedRecords){
11180 this.modified.remove(record);
11182 this.fireEvent("remove", this, record, index);
11186 * Remove all Records from the Store and fires the clear event.
11188 removeAll : function(){
11190 if(this.pruneModifiedRecords){
11191 this.modified = [];
11193 this.fireEvent("clear", this);
11197 * Inserts Records to the Store at the given index and fires the add event.
11198 * @param {Number} index The start index at which to insert the passed Records.
11199 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11201 insert : function(index, records){
11202 records = [].concat(records);
11203 for(var i = 0, len = records.length; i < len; i++){
11204 this.data.insert(index, records[i]);
11205 records[i].join(this);
11207 this.fireEvent("add", this, records, index);
11211 * Get the index within the cache of the passed Record.
11212 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11213 * @return {Number} The index of the passed Record. Returns -1 if not found.
11215 indexOf : function(record){
11216 return this.data.indexOf(record);
11220 * Get the index within the cache of the Record with the passed id.
11221 * @param {String} id The id of the Record to find.
11222 * @return {Number} The index of the Record. Returns -1 if not found.
11224 indexOfId : function(id){
11225 return this.data.indexOfKey(id);
11229 * Get the Record with the specified id.
11230 * @param {String} id The id of the Record to find.
11231 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11233 getById : function(id){
11234 return this.data.key(id);
11238 * Get the Record at the specified index.
11239 * @param {Number} index The index of the Record to find.
11240 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11242 getAt : function(index){
11243 return this.data.itemAt(index);
11247 * Returns a range of Records between specified indices.
11248 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11249 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11250 * @return {Roo.data.Record[]} An array of Records
11252 getRange : function(start, end){
11253 return this.data.getRange(start, end);
11257 storeOptions : function(o){
11258 o = Roo.apply({}, o);
11261 this.lastOptions = o;
11265 * Loads the Record cache from the configured Proxy using the configured Reader.
11267 * If using remote paging, then the first load call must specify the <em>start</em>
11268 * and <em>limit</em> properties in the options.params property to establish the initial
11269 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11271 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11272 * and this call will return before the new data has been loaded. Perform any post-processing
11273 * in a callback function, or in a "load" event handler.</strong>
11275 * @param {Object} options An object containing properties which control loading options:<ul>
11276 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11277 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11278 * passed the following arguments:<ul>
11279 * <li>r : Roo.data.Record[]</li>
11280 * <li>options: Options object from the load call</li>
11281 * <li>success: Boolean success indicator</li></ul></li>
11282 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11283 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11286 load : function(options){
11287 options = options || {};
11288 if(this.fireEvent("beforeload", this, options) !== false){
11289 this.storeOptions(options);
11290 var p = Roo.apply(options.params || {}, this.baseParams);
11291 // if meta was not loaded from remote source.. try requesting it.
11292 if (!this.reader.metaFromRemote) {
11293 p._requestMeta = 1;
11295 if(this.sortInfo && this.remoteSort){
11296 var pn = this.paramNames;
11297 p[pn["sort"]] = this.sortInfo.field;
11298 p[pn["dir"]] = this.sortInfo.direction;
11300 if (this.multiSort) {
11301 var pn = this.paramNames;
11302 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11305 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11310 * Reloads the Record cache from the configured Proxy using the configured Reader and
11311 * the options from the last load operation performed.
11312 * @param {Object} options (optional) An object containing properties which may override the options
11313 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11314 * the most recently used options are reused).
11316 reload : function(options){
11317 this.load(Roo.applyIf(options||{}, this.lastOptions));
11321 // Called as a callback by the Reader during a load operation.
11322 loadRecords : function(o, options, success){
11323 if(!o || success === false){
11324 if(success !== false){
11325 this.fireEvent("load", this, [], options, o);
11327 if(options.callback){
11328 options.callback.call(options.scope || this, [], options, false);
11332 // if data returned failure - throw an exception.
11333 if (o.success === false) {
11334 // show a message if no listener is registered.
11335 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11336 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11338 // loadmask wil be hooked into this..
11339 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11342 var r = o.records, t = o.totalRecords || r.length;
11344 this.fireEvent("beforeloadadd", this, r, options, o);
11346 if(!options || options.add !== true){
11347 if(this.pruneModifiedRecords){
11348 this.modified = [];
11350 for(var i = 0, len = r.length; i < len; i++){
11354 this.data = this.snapshot;
11355 delete this.snapshot;
11358 this.data.addAll(r);
11359 this.totalLength = t;
11361 this.fireEvent("datachanged", this);
11363 this.totalLength = Math.max(t, this.data.length+r.length);
11367 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11369 var e = new Roo.data.Record({});
11371 e.set(this.parent.displayField, this.parent.emptyTitle);
11372 e.set(this.parent.valueField, '');
11377 this.fireEvent("load", this, r, options, o);
11378 if(options.callback){
11379 options.callback.call(options.scope || this, r, options, true);
11385 * Loads data from a passed data block. A Reader which understands the format of the data
11386 * must have been configured in the constructor.
11387 * @param {Object} data The data block from which to read the Records. The format of the data expected
11388 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11389 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11391 loadData : function(o, append){
11392 var r = this.reader.readRecords(o);
11393 this.loadRecords(r, {add: append}, true);
11397 * Gets the number of cached records.
11399 * <em>If using paging, this may not be the total size of the dataset. If the data object
11400 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11401 * the data set size</em>
11403 getCount : function(){
11404 return this.data.length || 0;
11408 * Gets the total number of records in the dataset as returned by the server.
11410 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11411 * the dataset size</em>
11413 getTotalCount : function(){
11414 return this.totalLength || 0;
11418 * Returns the sort state of the Store as an object with two properties:
11420 field {String} The name of the field by which the Records are sorted
11421 direction {String} The sort order, "ASC" or "DESC"
11424 getSortState : function(){
11425 return this.sortInfo;
11429 applySort : function(){
11430 if(this.sortInfo && !this.remoteSort){
11431 var s = this.sortInfo, f = s.field;
11432 var st = this.fields.get(f).sortType;
11433 var fn = function(r1, r2){
11434 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11435 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11437 this.data.sort(s.direction, fn);
11438 if(this.snapshot && this.snapshot != this.data){
11439 this.snapshot.sort(s.direction, fn);
11445 * Sets the default sort column and order to be used by the next load operation.
11446 * @param {String} fieldName The name of the field to sort by.
11447 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11449 setDefaultSort : function(field, dir){
11450 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11454 * Sort the Records.
11455 * If remote sorting is used, the sort is performed on the server, and the cache is
11456 * reloaded. If local sorting is used, the cache is sorted internally.
11457 * @param {String} fieldName The name of the field to sort by.
11458 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11460 sort : function(fieldName, dir){
11461 var f = this.fields.get(fieldName);
11463 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11465 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11466 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11471 this.sortToggle[f.name] = dir;
11472 this.sortInfo = {field: f.name, direction: dir};
11473 if(!this.remoteSort){
11475 this.fireEvent("datachanged", this);
11477 this.load(this.lastOptions);
11482 * Calls the specified function for each of the Records in the cache.
11483 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11484 * Returning <em>false</em> aborts and exits the iteration.
11485 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11487 each : function(fn, scope){
11488 this.data.each(fn, scope);
11492 * Gets all records modified since the last commit. Modified records are persisted across load operations
11493 * (e.g., during paging).
11494 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11496 getModifiedRecords : function(){
11497 return this.modified;
11501 createFilterFn : function(property, value, anyMatch){
11502 if(!value.exec){ // not a regex
11503 value = String(value);
11504 if(value.length == 0){
11507 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11509 return function(r){
11510 return value.test(r.data[property]);
11515 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11516 * @param {String} property A field on your records
11517 * @param {Number} start The record index to start at (defaults to 0)
11518 * @param {Number} end The last record index to include (defaults to length - 1)
11519 * @return {Number} The sum
11521 sum : function(property, start, end){
11522 var rs = this.data.items, v = 0;
11523 start = start || 0;
11524 end = (end || end === 0) ? end : rs.length-1;
11526 for(var i = start; i <= end; i++){
11527 v += (rs[i].data[property] || 0);
11533 * Filter the records by a specified property.
11534 * @param {String} field A field on your records
11535 * @param {String/RegExp} value Either a string that the field
11536 * should start with or a RegExp to test against the field
11537 * @param {Boolean} anyMatch True to match any part not just the beginning
11539 filter : function(property, value, anyMatch){
11540 var fn = this.createFilterFn(property, value, anyMatch);
11541 return fn ? this.filterBy(fn) : this.clearFilter();
11545 * Filter by a function. The specified function will be called with each
11546 * record in this data source. If the function returns true the record is included,
11547 * otherwise it is filtered.
11548 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11549 * @param {Object} scope (optional) The scope of the function (defaults to this)
11551 filterBy : function(fn, scope){
11552 this.snapshot = this.snapshot || this.data;
11553 this.data = this.queryBy(fn, scope||this);
11554 this.fireEvent("datachanged", this);
11558 * Query the records by a specified property.
11559 * @param {String} field A field on your records
11560 * @param {String/RegExp} value Either a string that the field
11561 * should start with or a RegExp to test against the field
11562 * @param {Boolean} anyMatch True to match any part not just the beginning
11563 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11565 query : function(property, value, anyMatch){
11566 var fn = this.createFilterFn(property, value, anyMatch);
11567 return fn ? this.queryBy(fn) : this.data.clone();
11571 * Query by a function. The specified function will be called with each
11572 * record in this data source. If the function returns true the record is included
11574 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11575 * @param {Object} scope (optional) The scope of the function (defaults to this)
11576 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11578 queryBy : function(fn, scope){
11579 var data = this.snapshot || this.data;
11580 return data.filterBy(fn, scope||this);
11584 * Collects unique values for a particular dataIndex from this store.
11585 * @param {String} dataIndex The property to collect
11586 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11587 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11588 * @return {Array} An array of the unique values
11590 collect : function(dataIndex, allowNull, bypassFilter){
11591 var d = (bypassFilter === true && this.snapshot) ?
11592 this.snapshot.items : this.data.items;
11593 var v, sv, r = [], l = {};
11594 for(var i = 0, len = d.length; i < len; i++){
11595 v = d[i].data[dataIndex];
11597 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11606 * Revert to a view of the Record cache with no filtering applied.
11607 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11609 clearFilter : function(suppressEvent){
11610 if(this.snapshot && this.snapshot != this.data){
11611 this.data = this.snapshot;
11612 delete this.snapshot;
11613 if(suppressEvent !== true){
11614 this.fireEvent("datachanged", this);
11620 afterEdit : function(record){
11621 if(this.modified.indexOf(record) == -1){
11622 this.modified.push(record);
11624 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11628 afterReject : function(record){
11629 this.modified.remove(record);
11630 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11634 afterCommit : function(record){
11635 this.modified.remove(record);
11636 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11640 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11641 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11643 commitChanges : function(){
11644 var m = this.modified.slice(0);
11645 this.modified = [];
11646 for(var i = 0, len = m.length; i < len; i++){
11652 * Cancel outstanding changes on all changed records.
11654 rejectChanges : function(){
11655 var m = this.modified.slice(0);
11656 this.modified = [];
11657 for(var i = 0, len = m.length; i < len; i++){
11662 onMetaChange : function(meta, rtype, o){
11663 this.recordType = rtype;
11664 this.fields = rtype.prototype.fields;
11665 delete this.snapshot;
11666 this.sortInfo = meta.sortInfo || this.sortInfo;
11667 this.modified = [];
11668 this.fireEvent('metachange', this, this.reader.meta);
11671 moveIndex : function(data, type)
11673 var index = this.indexOf(data);
11675 var newIndex = index + type;
11679 this.insert(newIndex, data);
11684 * Ext JS Library 1.1.1
11685 * Copyright(c) 2006-2007, Ext JS, LLC.
11687 * Originally Released Under LGPL - original licence link has changed is not relivant.
11690 * <script type="text/javascript">
11694 * @class Roo.data.SimpleStore
11695 * @extends Roo.data.Store
11696 * Small helper class to make creating Stores from Array data easier.
11697 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11698 * @cfg {Array} fields An array of field definition objects, or field name strings.
11699 * @cfg {Array} data The multi-dimensional array of data
11701 * @param {Object} config
11703 Roo.data.SimpleStore = function(config){
11704 Roo.data.SimpleStore.superclass.constructor.call(this, {
11706 reader: new Roo.data.ArrayReader({
11709 Roo.data.Record.create(config.fields)
11711 proxy : new Roo.data.MemoryProxy(config.data)
11715 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11717 * Ext JS Library 1.1.1
11718 * Copyright(c) 2006-2007, Ext JS, LLC.
11720 * Originally Released Under LGPL - original licence link has changed is not relivant.
11723 * <script type="text/javascript">
11728 * @extends Roo.data.Store
11729 * @class Roo.data.JsonStore
11730 * Small helper class to make creating Stores for JSON data easier. <br/>
11732 var store = new Roo.data.JsonStore({
11733 url: 'get-images.php',
11735 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11738 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11739 * JsonReader and HttpProxy (unless inline data is provided).</b>
11740 * @cfg {Array} fields An array of field definition objects, or field name strings.
11742 * @param {Object} config
11744 Roo.data.JsonStore = function(c){
11745 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11746 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11747 reader: new Roo.data.JsonReader(c, c.fields)
11750 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11752 * Ext JS Library 1.1.1
11753 * Copyright(c) 2006-2007, Ext JS, LLC.
11755 * Originally Released Under LGPL - original licence link has changed is not relivant.
11758 * <script type="text/javascript">
11762 Roo.data.Field = function(config){
11763 if(typeof config == "string"){
11764 config = {name: config};
11766 Roo.apply(this, config);
11769 this.type = "auto";
11772 var st = Roo.data.SortTypes;
11773 // named sortTypes are supported, here we look them up
11774 if(typeof this.sortType == "string"){
11775 this.sortType = st[this.sortType];
11778 // set default sortType for strings and dates
11779 if(!this.sortType){
11782 this.sortType = st.asUCString;
11785 this.sortType = st.asDate;
11788 this.sortType = st.none;
11793 var stripRe = /[\$,%]/g;
11795 // prebuilt conversion function for this field, instead of
11796 // switching every time we're reading a value
11798 var cv, dateFormat = this.dateFormat;
11803 cv = function(v){ return v; };
11806 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11810 return v !== undefined && v !== null && v !== '' ?
11811 parseInt(String(v).replace(stripRe, ""), 10) : '';
11816 return v !== undefined && v !== null && v !== '' ?
11817 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11822 cv = function(v){ return v === true || v === "true" || v == 1; };
11829 if(v instanceof Date){
11833 if(dateFormat == "timestamp"){
11834 return new Date(v*1000);
11836 return Date.parseDate(v, dateFormat);
11838 var parsed = Date.parse(v);
11839 return parsed ? new Date(parsed) : null;
11848 Roo.data.Field.prototype = {
11856 * Ext JS Library 1.1.1
11857 * Copyright(c) 2006-2007, Ext JS, LLC.
11859 * Originally Released Under LGPL - original licence link has changed is not relivant.
11862 * <script type="text/javascript">
11865 // Base class for reading structured data from a data source. This class is intended to be
11866 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11869 * @class Roo.data.DataReader
11870 * Base class for reading structured data from a data source. This class is intended to be
11871 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11874 Roo.data.DataReader = function(meta, recordType){
11878 this.recordType = recordType instanceof Array ?
11879 Roo.data.Record.create(recordType) : recordType;
11882 Roo.data.DataReader.prototype = {
11884 * Create an empty record
11885 * @param {Object} data (optional) - overlay some values
11886 * @return {Roo.data.Record} record created.
11888 newRow : function(d) {
11890 this.recordType.prototype.fields.each(function(c) {
11892 case 'int' : da[c.name] = 0; break;
11893 case 'date' : da[c.name] = new Date(); break;
11894 case 'float' : da[c.name] = 0.0; break;
11895 case 'boolean' : da[c.name] = false; break;
11896 default : da[c.name] = ""; break;
11900 return new this.recordType(Roo.apply(da, d));
11905 * Ext JS Library 1.1.1
11906 * Copyright(c) 2006-2007, Ext JS, LLC.
11908 * Originally Released Under LGPL - original licence link has changed is not relivant.
11911 * <script type="text/javascript">
11915 * @class Roo.data.DataProxy
11916 * @extends Roo.data.Observable
11917 * This class is an abstract base class for implementations which provide retrieval of
11918 * unformatted data objects.<br>
11920 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11921 * (of the appropriate type which knows how to parse the data object) to provide a block of
11922 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11924 * Custom implementations must implement the load method as described in
11925 * {@link Roo.data.HttpProxy#load}.
11927 Roo.data.DataProxy = function(){
11930 * @event beforeload
11931 * Fires before a network request is made to retrieve a data object.
11932 * @param {Object} This DataProxy object.
11933 * @param {Object} params The params parameter to the load function.
11938 * Fires before the load method's callback is called.
11939 * @param {Object} This DataProxy object.
11940 * @param {Object} o The data object.
11941 * @param {Object} arg The callback argument object passed to the load function.
11945 * @event loadexception
11946 * Fires if an Exception occurs during data retrieval.
11947 * @param {Object} This DataProxy object.
11948 * @param {Object} o The data object.
11949 * @param {Object} arg The callback argument object passed to the load function.
11950 * @param {Object} e The Exception.
11952 loadexception : true
11954 Roo.data.DataProxy.superclass.constructor.call(this);
11957 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11960 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11964 * Ext JS Library 1.1.1
11965 * Copyright(c) 2006-2007, Ext JS, LLC.
11967 * Originally Released Under LGPL - original licence link has changed is not relivant.
11970 * <script type="text/javascript">
11973 * @class Roo.data.MemoryProxy
11974 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11975 * to the Reader when its load method is called.
11977 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11979 Roo.data.MemoryProxy = function(data){
11983 Roo.data.MemoryProxy.superclass.constructor.call(this);
11987 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11990 * Load data from the requested source (in this case an in-memory
11991 * data object passed to the constructor), read the data object into
11992 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11993 * process that block using the passed callback.
11994 * @param {Object} params This parameter is not used by the MemoryProxy class.
11995 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11996 * object into a block of Roo.data.Records.
11997 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11998 * The function must be passed <ul>
11999 * <li>The Record block object</li>
12000 * <li>The "arg" argument from the load function</li>
12001 * <li>A boolean success indicator</li>
12003 * @param {Object} scope The scope in which to call the callback
12004 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12006 load : function(params, reader, callback, scope, arg){
12007 params = params || {};
12010 result = reader.readRecords(this.data);
12012 this.fireEvent("loadexception", this, arg, null, e);
12013 callback.call(scope, null, arg, false);
12016 callback.call(scope, result, arg, true);
12020 update : function(params, records){
12025 * Ext JS Library 1.1.1
12026 * Copyright(c) 2006-2007, Ext JS, LLC.
12028 * Originally Released Under LGPL - original licence link has changed is not relivant.
12031 * <script type="text/javascript">
12034 * @class Roo.data.HttpProxy
12035 * @extends Roo.data.DataProxy
12036 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12037 * configured to reference a certain URL.<br><br>
12039 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12040 * from which the running page was served.<br><br>
12042 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12044 * Be aware that to enable the browser to parse an XML document, the server must set
12045 * the Content-Type header in the HTTP response to "text/xml".
12047 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12048 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12049 * will be used to make the request.
12051 Roo.data.HttpProxy = function(conn){
12052 Roo.data.HttpProxy.superclass.constructor.call(this);
12053 // is conn a conn config or a real conn?
12055 this.useAjax = !conn || !conn.events;
12059 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12060 // thse are take from connection...
12063 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12066 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12067 * extra parameters to each request made by this object. (defaults to undefined)
12070 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12071 * to each request made by this object. (defaults to undefined)
12074 * @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)
12077 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12080 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12086 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12090 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12091 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12092 * a finer-grained basis than the DataProxy events.
12094 getConnection : function(){
12095 return this.useAjax ? Roo.Ajax : this.conn;
12099 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12100 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12101 * process that block using the passed callback.
12102 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12103 * for the request to the remote server.
12104 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12105 * object into a block of Roo.data.Records.
12106 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12107 * The function must be passed <ul>
12108 * <li>The Record block object</li>
12109 * <li>The "arg" argument from the load function</li>
12110 * <li>A boolean success indicator</li>
12112 * @param {Object} scope The scope in which to call the callback
12113 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12115 load : function(params, reader, callback, scope, arg){
12116 if(this.fireEvent("beforeload", this, params) !== false){
12118 params : params || {},
12120 callback : callback,
12125 callback : this.loadResponse,
12129 Roo.applyIf(o, this.conn);
12130 if(this.activeRequest){
12131 Roo.Ajax.abort(this.activeRequest);
12133 this.activeRequest = Roo.Ajax.request(o);
12135 this.conn.request(o);
12138 callback.call(scope||this, null, arg, false);
12143 loadResponse : function(o, success, response){
12144 delete this.activeRequest;
12146 this.fireEvent("loadexception", this, o, response);
12147 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12152 result = o.reader.read(response);
12154 this.fireEvent("loadexception", this, o, response, e);
12155 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12159 this.fireEvent("load", this, o, o.request.arg);
12160 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12164 update : function(dataSet){
12169 updateResponse : function(dataSet){
12174 * Ext JS Library 1.1.1
12175 * Copyright(c) 2006-2007, Ext JS, LLC.
12177 * Originally Released Under LGPL - original licence link has changed is not relivant.
12180 * <script type="text/javascript">
12184 * @class Roo.data.ScriptTagProxy
12185 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12186 * other than the originating domain of the running page.<br><br>
12188 * <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
12189 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12191 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12192 * source code that is used as the source inside a <script> tag.<br><br>
12194 * In order for the browser to process the returned data, the server must wrap the data object
12195 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12196 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12197 * depending on whether the callback name was passed:
12200 boolean scriptTag = false;
12201 String cb = request.getParameter("callback");
12204 response.setContentType("text/javascript");
12206 response.setContentType("application/x-json");
12208 Writer out = response.getWriter();
12210 out.write(cb + "(");
12212 out.print(dataBlock.toJsonString());
12219 * @param {Object} config A configuration object.
12221 Roo.data.ScriptTagProxy = function(config){
12222 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12223 Roo.apply(this, config);
12224 this.head = document.getElementsByTagName("head")[0];
12227 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12229 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12231 * @cfg {String} url The URL from which to request the data object.
12234 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12238 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12239 * the server the name of the callback function set up by the load call to process the returned data object.
12240 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12241 * javascript output which calls this named function passing the data object as its only parameter.
12243 callbackParam : "callback",
12245 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12246 * name to the request.
12251 * Load data from the configured URL, read the data object into
12252 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12253 * process that block using the passed callback.
12254 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12255 * for the request to the remote server.
12256 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12257 * object into a block of Roo.data.Records.
12258 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12259 * The function must be passed <ul>
12260 * <li>The Record block object</li>
12261 * <li>The "arg" argument from the load function</li>
12262 * <li>A boolean success indicator</li>
12264 * @param {Object} scope The scope in which to call the callback
12265 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12267 load : function(params, reader, callback, scope, arg){
12268 if(this.fireEvent("beforeload", this, params) !== false){
12270 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12272 var url = this.url;
12273 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12275 url += "&_dc=" + (new Date().getTime());
12277 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12280 cb : "stcCallback"+transId,
12281 scriptId : "stcScript"+transId,
12285 callback : callback,
12291 window[trans.cb] = function(o){
12292 conn.handleResponse(o, trans);
12295 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12297 if(this.autoAbort !== false){
12301 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12303 var script = document.createElement("script");
12304 script.setAttribute("src", url);
12305 script.setAttribute("type", "text/javascript");
12306 script.setAttribute("id", trans.scriptId);
12307 this.head.appendChild(script);
12309 this.trans = trans;
12311 callback.call(scope||this, null, arg, false);
12316 isLoading : function(){
12317 return this.trans ? true : false;
12321 * Abort the current server request.
12323 abort : function(){
12324 if(this.isLoading()){
12325 this.destroyTrans(this.trans);
12330 destroyTrans : function(trans, isLoaded){
12331 this.head.removeChild(document.getElementById(trans.scriptId));
12332 clearTimeout(trans.timeoutId);
12334 window[trans.cb] = undefined;
12336 delete window[trans.cb];
12339 // if hasn't been loaded, wait for load to remove it to prevent script error
12340 window[trans.cb] = function(){
12341 window[trans.cb] = undefined;
12343 delete window[trans.cb];
12350 handleResponse : function(o, trans){
12351 this.trans = false;
12352 this.destroyTrans(trans, true);
12355 result = trans.reader.readRecords(o);
12357 this.fireEvent("loadexception", this, o, trans.arg, e);
12358 trans.callback.call(trans.scope||window, null, trans.arg, false);
12361 this.fireEvent("load", this, o, trans.arg);
12362 trans.callback.call(trans.scope||window, result, trans.arg, true);
12366 handleFailure : function(trans){
12367 this.trans = false;
12368 this.destroyTrans(trans, false);
12369 this.fireEvent("loadexception", this, null, trans.arg);
12370 trans.callback.call(trans.scope||window, null, trans.arg, false);
12374 * Ext JS Library 1.1.1
12375 * Copyright(c) 2006-2007, Ext JS, LLC.
12377 * Originally Released Under LGPL - original licence link has changed is not relivant.
12380 * <script type="text/javascript">
12384 * @class Roo.data.JsonReader
12385 * @extends Roo.data.DataReader
12386 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12387 * based on mappings in a provided Roo.data.Record constructor.
12389 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12390 * in the reply previously.
12395 var RecordDef = Roo.data.Record.create([
12396 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12397 {name: 'occupation'} // This field will use "occupation" as the mapping.
12399 var myReader = new Roo.data.JsonReader({
12400 totalProperty: "results", // The property which contains the total dataset size (optional)
12401 root: "rows", // The property which contains an Array of row objects
12402 id: "id" // The property within each row object that provides an ID for the record (optional)
12406 * This would consume a JSON file like this:
12408 { 'results': 2, 'rows': [
12409 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12410 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12413 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12414 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12415 * paged from the remote server.
12416 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12417 * @cfg {String} root name of the property which contains the Array of row objects.
12418 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12419 * @cfg {Array} fields Array of field definition objects
12421 * Create a new JsonReader
12422 * @param {Object} meta Metadata configuration options
12423 * @param {Object} recordType Either an Array of field definition objects,
12424 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12426 Roo.data.JsonReader = function(meta, recordType){
12429 // set some defaults:
12430 Roo.applyIf(meta, {
12431 totalProperty: 'total',
12432 successProperty : 'success',
12437 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12439 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12442 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12443 * Used by Store query builder to append _requestMeta to params.
12446 metaFromRemote : false,
12448 * This method is only used by a DataProxy which has retrieved data from a remote server.
12449 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12450 * @return {Object} data A data block which is used by an Roo.data.Store object as
12451 * a cache of Roo.data.Records.
12453 read : function(response){
12454 var json = response.responseText;
12456 var o = /* eval:var:o */ eval("("+json+")");
12458 throw {message: "JsonReader.read: Json object not found"};
12464 this.metaFromRemote = true;
12465 this.meta = o.metaData;
12466 this.recordType = Roo.data.Record.create(o.metaData.fields);
12467 this.onMetaChange(this.meta, this.recordType, o);
12469 return this.readRecords(o);
12472 // private function a store will implement
12473 onMetaChange : function(meta, recordType, o){
12480 simpleAccess: function(obj, subsc) {
12487 getJsonAccessor: function(){
12489 return function(expr) {
12491 return(re.test(expr))
12492 ? new Function("obj", "return obj." + expr)
12497 return Roo.emptyFn;
12502 * Create a data block containing Roo.data.Records from an XML document.
12503 * @param {Object} o An object which contains an Array of row objects in the property specified
12504 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12505 * which contains the total size of the dataset.
12506 * @return {Object} data A data block which is used by an Roo.data.Store object as
12507 * a cache of Roo.data.Records.
12509 readRecords : function(o){
12511 * After any data loads, the raw JSON data is available for further custom processing.
12515 var s = this.meta, Record = this.recordType,
12516 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12518 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12520 if(s.totalProperty) {
12521 this.getTotal = this.getJsonAccessor(s.totalProperty);
12523 if(s.successProperty) {
12524 this.getSuccess = this.getJsonAccessor(s.successProperty);
12526 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12528 var g = this.getJsonAccessor(s.id);
12529 this.getId = function(rec) {
12531 return (r === undefined || r === "") ? null : r;
12534 this.getId = function(){return null;};
12537 for(var jj = 0; jj < fl; jj++){
12539 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12540 this.ef[jj] = this.getJsonAccessor(map);
12544 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12545 if(s.totalProperty){
12546 var vt = parseInt(this.getTotal(o), 10);
12551 if(s.successProperty){
12552 var vs = this.getSuccess(o);
12553 if(vs === false || vs === 'false'){
12558 for(var i = 0; i < c; i++){
12561 var id = this.getId(n);
12562 for(var j = 0; j < fl; j++){
12564 var v = this.ef[j](n);
12566 Roo.log('missing convert for ' + f.name);
12570 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12572 var record = new Record(values, id);
12574 records[i] = record;
12580 totalRecords : totalRecords
12585 * Ext JS Library 1.1.1
12586 * Copyright(c) 2006-2007, Ext JS, LLC.
12588 * Originally Released Under LGPL - original licence link has changed is not relivant.
12591 * <script type="text/javascript">
12595 * @class Roo.data.ArrayReader
12596 * @extends Roo.data.DataReader
12597 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12598 * Each element of that Array represents a row of data fields. The
12599 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12600 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12604 var RecordDef = Roo.data.Record.create([
12605 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12606 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12608 var myReader = new Roo.data.ArrayReader({
12609 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12613 * This would consume an Array like this:
12615 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12617 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12619 * Create a new JsonReader
12620 * @param {Object} meta Metadata configuration options.
12621 * @param {Object} recordType Either an Array of field definition objects
12622 * as specified to {@link Roo.data.Record#create},
12623 * or an {@link Roo.data.Record} object
12624 * created using {@link Roo.data.Record#create}.
12626 Roo.data.ArrayReader = function(meta, recordType){
12627 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12630 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12632 * Create a data block containing Roo.data.Records from an XML document.
12633 * @param {Object} o An Array of row objects which represents the dataset.
12634 * @return {Object} data A data block which is used by an Roo.data.Store object as
12635 * a cache of Roo.data.Records.
12637 readRecords : function(o){
12638 var sid = this.meta ? this.meta.id : null;
12639 var recordType = this.recordType, fields = recordType.prototype.fields;
12642 for(var i = 0; i < root.length; i++){
12645 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12646 for(var j = 0, jlen = fields.length; j < jlen; j++){
12647 var f = fields.items[j];
12648 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12649 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12651 values[f.name] = v;
12653 var record = new recordType(values, id);
12655 records[records.length] = record;
12659 totalRecords : records.length
12668 * @class Roo.bootstrap.ComboBox
12669 * @extends Roo.bootstrap.TriggerField
12670 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12671 * @cfg {Boolean} append (true|false) default false
12672 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12673 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12674 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12675 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12676 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12677 * @cfg {Boolean} animate default true
12678 * @cfg {Boolean} emptyResultText only for touch device
12679 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12680 * @cfg {String} emptyTitle default ''
12682 * Create a new ComboBox.
12683 * @param {Object} config Configuration options
12685 Roo.bootstrap.ComboBox = function(config){
12686 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12690 * Fires when the dropdown list is expanded
12691 * @param {Roo.bootstrap.ComboBox} combo This combo box
12696 * Fires when the dropdown list is collapsed
12697 * @param {Roo.bootstrap.ComboBox} combo This combo box
12701 * @event beforeselect
12702 * Fires before a list item is selected. Return false to cancel the selection.
12703 * @param {Roo.bootstrap.ComboBox} combo This combo box
12704 * @param {Roo.data.Record} record The data record returned from the underlying store
12705 * @param {Number} index The index of the selected item in the dropdown list
12707 'beforeselect' : true,
12710 * Fires when a list item is selected
12711 * @param {Roo.bootstrap.ComboBox} combo This combo box
12712 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12713 * @param {Number} index The index of the selected item in the dropdown list
12717 * @event beforequery
12718 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12719 * The event object passed has these properties:
12720 * @param {Roo.bootstrap.ComboBox} combo This combo box
12721 * @param {String} query The query
12722 * @param {Boolean} forceAll true to force "all" query
12723 * @param {Boolean} cancel true to cancel the query
12724 * @param {Object} e The query event object
12726 'beforequery': true,
12729 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12730 * @param {Roo.bootstrap.ComboBox} combo This combo box
12735 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12736 * @param {Roo.bootstrap.ComboBox} combo This combo box
12737 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12742 * Fires when the remove value from the combobox array
12743 * @param {Roo.bootstrap.ComboBox} combo This combo box
12747 * @event afterremove
12748 * Fires when the remove value from the combobox array
12749 * @param {Roo.bootstrap.ComboBox} combo This combo box
12751 'afterremove' : true,
12753 * @event specialfilter
12754 * Fires when specialfilter
12755 * @param {Roo.bootstrap.ComboBox} combo This combo box
12757 'specialfilter' : true,
12760 * Fires when tick the element
12761 * @param {Roo.bootstrap.ComboBox} combo This combo box
12765 * @event touchviewdisplay
12766 * Fires when touch view require special display (default is using displayField)
12767 * @param {Roo.bootstrap.ComboBox} combo This combo box
12768 * @param {Object} cfg set html .
12770 'touchviewdisplay' : true
12775 this.tickItems = [];
12777 this.selectedIndex = -1;
12778 if(this.mode == 'local'){
12779 if(config.queryDelay === undefined){
12780 this.queryDelay = 10;
12782 if(config.minChars === undefined){
12788 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12791 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12792 * rendering into an Roo.Editor, defaults to false)
12795 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12796 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12799 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12802 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12803 * the dropdown list (defaults to undefined, with no header element)
12807 * @cfg {String/Roo.Template} tpl The template to use to render the output
12811 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12813 listWidth: undefined,
12815 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12816 * mode = 'remote' or 'text' if mode = 'local')
12818 displayField: undefined,
12821 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12822 * mode = 'remote' or 'value' if mode = 'local').
12823 * Note: use of a valueField requires the user make a selection
12824 * in order for a value to be mapped.
12826 valueField: undefined,
12828 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12833 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12834 * field's data value (defaults to the underlying DOM element's name)
12836 hiddenName: undefined,
12838 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12842 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12844 selectedClass: 'active',
12847 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12851 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12852 * anchor positions (defaults to 'tl-bl')
12854 listAlign: 'tl-bl?',
12856 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12860 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12861 * query specified by the allQuery config option (defaults to 'query')
12863 triggerAction: 'query',
12865 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12866 * (defaults to 4, does not apply if editable = false)
12870 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12871 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12875 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12876 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12880 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12881 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12885 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12886 * when editable = true (defaults to false)
12888 selectOnFocus:false,
12890 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12892 queryParam: 'query',
12894 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12895 * when mode = 'remote' (defaults to 'Loading...')
12897 loadingText: 'Loading...',
12899 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12903 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12907 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12908 * traditional select (defaults to true)
12912 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12916 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12920 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12921 * listWidth has a higher value)
12925 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12926 * allow the user to set arbitrary text into the field (defaults to false)
12928 forceSelection:false,
12930 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12931 * if typeAhead = true (defaults to 250)
12933 typeAheadDelay : 250,
12935 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12936 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12938 valueNotFoundText : undefined,
12940 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12942 blockFocus : false,
12945 * @cfg {Boolean} disableClear Disable showing of clear button.
12947 disableClear : false,
12949 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12951 alwaysQuery : false,
12954 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12959 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12961 invalidClass : "has-warning",
12964 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12966 validClass : "has-success",
12969 * @cfg {Boolean} specialFilter (true|false) special filter default false
12971 specialFilter : false,
12974 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12976 mobileTouchView : true,
12979 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12981 useNativeIOS : false,
12983 ios_options : false,
12995 btnPosition : 'right',
12996 triggerList : true,
12997 showToggleBtn : true,
12999 emptyResultText: 'Empty',
13000 triggerText : 'Select',
13003 // element that contains real text value.. (when hidden is used..)
13005 getAutoCreate : function()
13010 * Render classic select for iso
13013 if(Roo.isIOS && this.useNativeIOS){
13014 cfg = this.getAutoCreateNativeIOS();
13022 if(Roo.isTouch && this.mobileTouchView){
13023 cfg = this.getAutoCreateTouchView();
13030 if(!this.tickable){
13031 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13036 * ComboBox with tickable selections
13039 var align = this.labelAlign || this.parentLabelAlign();
13042 cls : 'form-group roo-combobox-tickable' //input-group
13045 var btn_text_select = '';
13046 var btn_text_done = '';
13047 var btn_text_cancel = '';
13049 if (this.btn_text_show) {
13050 btn_text_select = 'Select';
13051 btn_text_done = 'Done';
13052 btn_text_cancel = 'Cancel';
13057 cls : 'tickable-buttons',
13062 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13063 //html : this.triggerText
13064 html: btn_text_select
13070 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13072 html: btn_text_done
13078 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13080 html: btn_text_cancel
13086 buttons.cn.unshift({
13088 cls: 'roo-select2-search-field-input'
13094 Roo.each(buttons.cn, function(c){
13096 c.cls += ' btn-' + _this.size;
13099 if (_this.disabled) {
13110 cls: 'form-hidden-field'
13114 cls: 'roo-select2-choices',
13118 cls: 'roo-select2-search-field',
13129 cls: 'roo-select2-container input-group roo-select2-container-multi',
13134 // cls: 'typeahead typeahead-long dropdown-menu',
13135 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13140 if(this.hasFeedback && !this.allowBlank){
13144 cls: 'glyphicon form-control-feedback'
13147 combobox.cn.push(feedback);
13151 if (align ==='left' && this.fieldLabel.length) {
13153 cfg.cls += ' roo-form-group-label-left';
13158 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13159 tooltip : 'This field is required'
13164 cls : 'control-label',
13165 html : this.fieldLabel
13177 var labelCfg = cfg.cn[1];
13178 var contentCfg = cfg.cn[2];
13181 if(this.indicatorpos == 'right'){
13187 cls : 'control-label',
13191 html : this.fieldLabel
13195 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13196 tooltip : 'This field is required'
13211 labelCfg = cfg.cn[0];
13212 contentCfg = cfg.cn[1];
13216 if(this.labelWidth > 12){
13217 labelCfg.style = "width: " + this.labelWidth + 'px';
13220 if(this.labelWidth < 13 && this.labelmd == 0){
13221 this.labelmd = this.labelWidth;
13224 if(this.labellg > 0){
13225 labelCfg.cls += ' col-lg-' + this.labellg;
13226 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13229 if(this.labelmd > 0){
13230 labelCfg.cls += ' col-md-' + this.labelmd;
13231 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13234 if(this.labelsm > 0){
13235 labelCfg.cls += ' col-sm-' + this.labelsm;
13236 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13239 if(this.labelxs > 0){
13240 labelCfg.cls += ' col-xs-' + this.labelxs;
13241 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13245 } else if ( this.fieldLabel.length) {
13246 // Roo.log(" label");
13250 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13251 tooltip : 'This field is required'
13255 //cls : 'input-group-addon',
13256 html : this.fieldLabel
13261 if(this.indicatorpos == 'right'){
13265 //cls : 'input-group-addon',
13266 html : this.fieldLabel
13270 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13271 tooltip : 'This field is required'
13280 // Roo.log(" no label && no align");
13287 ['xs','sm','md','lg'].map(function(size){
13288 if (settings[size]) {
13289 cfg.cls += ' col-' + size + '-' + settings[size];
13297 _initEventsCalled : false,
13300 initEvents: function()
13302 if (this._initEventsCalled) { // as we call render... prevent looping...
13305 this._initEventsCalled = true;
13308 throw "can not find store for combo";
13311 this.indicator = this.indicatorEl();
13313 this.store = Roo.factory(this.store, Roo.data);
13314 this.store.parent = this;
13316 // if we are building from html. then this element is so complex, that we can not really
13317 // use the rendered HTML.
13318 // so we have to trash and replace the previous code.
13319 if (Roo.XComponent.build_from_html) {
13320 // remove this element....
13321 var e = this.el.dom, k=0;
13322 while (e ) { e = e.previousSibling; ++k;}
13327 this.rendered = false;
13329 this.render(this.parent().getChildContainer(true), k);
13332 if(Roo.isIOS && this.useNativeIOS){
13333 this.initIOSView();
13341 if(Roo.isTouch && this.mobileTouchView){
13342 this.initTouchView();
13347 this.initTickableEvents();
13351 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13353 if(this.hiddenName){
13355 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13357 this.hiddenField.dom.value =
13358 this.hiddenValue !== undefined ? this.hiddenValue :
13359 this.value !== undefined ? this.value : '';
13361 // prevent input submission
13362 this.el.dom.removeAttribute('name');
13363 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13368 // this.el.dom.setAttribute('autocomplete', 'off');
13371 var cls = 'x-combo-list';
13373 //this.list = new Roo.Layer({
13374 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13380 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13381 _this.list.setWidth(lw);
13384 this.list.on('mouseover', this.onViewOver, this);
13385 this.list.on('mousemove', this.onViewMove, this);
13386 this.list.on('scroll', this.onViewScroll, this);
13389 this.list.swallowEvent('mousewheel');
13390 this.assetHeight = 0;
13393 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13394 this.assetHeight += this.header.getHeight();
13397 this.innerList = this.list.createChild({cls:cls+'-inner'});
13398 this.innerList.on('mouseover', this.onViewOver, this);
13399 this.innerList.on('mousemove', this.onViewMove, this);
13400 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13402 if(this.allowBlank && !this.pageSize && !this.disableClear){
13403 this.footer = this.list.createChild({cls:cls+'-ft'});
13404 this.pageTb = new Roo.Toolbar(this.footer);
13408 this.footer = this.list.createChild({cls:cls+'-ft'});
13409 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13410 {pageSize: this.pageSize});
13414 if (this.pageTb && this.allowBlank && !this.disableClear) {
13416 this.pageTb.add(new Roo.Toolbar.Fill(), {
13417 cls: 'x-btn-icon x-btn-clear',
13419 handler: function()
13422 _this.clearValue();
13423 _this.onSelect(false, -1);
13428 this.assetHeight += this.footer.getHeight();
13433 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13436 this.view = new Roo.View(this.list, this.tpl, {
13437 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13439 //this.view.wrapEl.setDisplayed(false);
13440 this.view.on('click', this.onViewClick, this);
13443 this.store.on('beforeload', this.onBeforeLoad, this);
13444 this.store.on('load', this.onLoad, this);
13445 this.store.on('loadexception', this.onLoadException, this);
13447 if(this.resizable){
13448 this.resizer = new Roo.Resizable(this.list, {
13449 pinned:true, handles:'se'
13451 this.resizer.on('resize', function(r, w, h){
13452 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13453 this.listWidth = w;
13454 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13455 this.restrictHeight();
13457 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13460 if(!this.editable){
13461 this.editable = true;
13462 this.setEditable(false);
13467 if (typeof(this.events.add.listeners) != 'undefined') {
13469 this.addicon = this.wrap.createChild(
13470 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13472 this.addicon.on('click', function(e) {
13473 this.fireEvent('add', this);
13476 if (typeof(this.events.edit.listeners) != 'undefined') {
13478 this.editicon = this.wrap.createChild(
13479 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13480 if (this.addicon) {
13481 this.editicon.setStyle('margin-left', '40px');
13483 this.editicon.on('click', function(e) {
13485 // we fire even if inothing is selected..
13486 this.fireEvent('edit', this, this.lastData );
13492 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13493 "up" : function(e){
13494 this.inKeyMode = true;
13498 "down" : function(e){
13499 if(!this.isExpanded()){
13500 this.onTriggerClick();
13502 this.inKeyMode = true;
13507 "enter" : function(e){
13508 // this.onViewClick();
13512 if(this.fireEvent("specialkey", this, e)){
13513 this.onViewClick(false);
13519 "esc" : function(e){
13523 "tab" : function(e){
13526 if(this.fireEvent("specialkey", this, e)){
13527 this.onViewClick(false);
13535 doRelay : function(foo, bar, hname){
13536 if(hname == 'down' || this.scope.isExpanded()){
13537 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13546 this.queryDelay = Math.max(this.queryDelay || 10,
13547 this.mode == 'local' ? 10 : 250);
13550 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13552 if(this.typeAhead){
13553 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13555 if(this.editable !== false){
13556 this.inputEl().on("keyup", this.onKeyUp, this);
13558 if(this.forceSelection){
13559 this.inputEl().on('blur', this.doForce, this);
13563 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13564 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13568 initTickableEvents: function()
13572 if(this.hiddenName){
13574 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13576 this.hiddenField.dom.value =
13577 this.hiddenValue !== undefined ? this.hiddenValue :
13578 this.value !== undefined ? this.value : '';
13580 // prevent input submission
13581 this.el.dom.removeAttribute('name');
13582 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13587 // this.list = this.el.select('ul.dropdown-menu',true).first();
13589 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13590 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13591 if(this.triggerList){
13592 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13595 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13596 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13598 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13599 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13601 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13602 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13604 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13605 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13606 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13609 this.cancelBtn.hide();
13614 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13615 _this.list.setWidth(lw);
13618 this.list.on('mouseover', this.onViewOver, this);
13619 this.list.on('mousemove', this.onViewMove, this);
13621 this.list.on('scroll', this.onViewScroll, this);
13624 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13625 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13628 this.view = new Roo.View(this.list, this.tpl, {
13633 selectedClass: this.selectedClass
13636 //this.view.wrapEl.setDisplayed(false);
13637 this.view.on('click', this.onViewClick, this);
13641 this.store.on('beforeload', this.onBeforeLoad, this);
13642 this.store.on('load', this.onLoad, this);
13643 this.store.on('loadexception', this.onLoadException, this);
13646 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13647 "up" : function(e){
13648 this.inKeyMode = true;
13652 "down" : function(e){
13653 this.inKeyMode = true;
13657 "enter" : function(e){
13658 if(this.fireEvent("specialkey", this, e)){
13659 this.onViewClick(false);
13665 "esc" : function(e){
13666 this.onTickableFooterButtonClick(e, false, false);
13669 "tab" : function(e){
13670 this.fireEvent("specialkey", this, e);
13672 this.onTickableFooterButtonClick(e, false, false);
13679 doRelay : function(e, fn, key){
13680 if(this.scope.isExpanded()){
13681 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13690 this.queryDelay = Math.max(this.queryDelay || 10,
13691 this.mode == 'local' ? 10 : 250);
13694 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13696 if(this.typeAhead){
13697 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13700 if(this.editable !== false){
13701 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13704 this.indicator = this.indicatorEl();
13706 if(this.indicator){
13707 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13708 this.indicator.hide();
13713 onDestroy : function(){
13715 this.view.setStore(null);
13716 this.view.el.removeAllListeners();
13717 this.view.el.remove();
13718 this.view.purgeListeners();
13721 this.list.dom.innerHTML = '';
13725 this.store.un('beforeload', this.onBeforeLoad, this);
13726 this.store.un('load', this.onLoad, this);
13727 this.store.un('loadexception', this.onLoadException, this);
13729 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13733 fireKey : function(e){
13734 if(e.isNavKeyPress() && !this.list.isVisible()){
13735 this.fireEvent("specialkey", this, e);
13740 onResize: function(w, h){
13741 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13743 // if(typeof w != 'number'){
13744 // // we do not handle it!?!?
13747 // var tw = this.trigger.getWidth();
13748 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13749 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13751 // this.inputEl().setWidth( this.adjustWidth('input', x));
13753 // //this.trigger.setStyle('left', x+'px');
13755 // if(this.list && this.listWidth === undefined){
13756 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13757 // this.list.setWidth(lw);
13758 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13766 * Allow or prevent the user from directly editing the field text. If false is passed,
13767 * the user will only be able to select from the items defined in the dropdown list. This method
13768 * is the runtime equivalent of setting the 'editable' config option at config time.
13769 * @param {Boolean} value True to allow the user to directly edit the field text
13771 setEditable : function(value){
13772 if(value == this.editable){
13775 this.editable = value;
13777 this.inputEl().dom.setAttribute('readOnly', true);
13778 this.inputEl().on('mousedown', this.onTriggerClick, this);
13779 this.inputEl().addClass('x-combo-noedit');
13781 this.inputEl().dom.setAttribute('readOnly', false);
13782 this.inputEl().un('mousedown', this.onTriggerClick, this);
13783 this.inputEl().removeClass('x-combo-noedit');
13789 onBeforeLoad : function(combo,opts){
13790 if(!this.hasFocus){
13794 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13796 this.restrictHeight();
13797 this.selectedIndex = -1;
13801 onLoad : function(){
13803 this.hasQuery = false;
13805 if(!this.hasFocus){
13809 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13810 this.loading.hide();
13813 if(this.store.getCount() > 0){
13816 this.restrictHeight();
13817 if(this.lastQuery == this.allQuery){
13818 if(this.editable && !this.tickable){
13819 this.inputEl().dom.select();
13823 !this.selectByValue(this.value, true) &&
13826 !this.store.lastOptions ||
13827 typeof(this.store.lastOptions.add) == 'undefined' ||
13828 this.store.lastOptions.add != true
13831 this.select(0, true);
13834 if(this.autoFocus){
13837 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13838 this.taTask.delay(this.typeAheadDelay);
13842 this.onEmptyResults();
13848 onLoadException : function()
13850 this.hasQuery = false;
13852 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13853 this.loading.hide();
13856 if(this.tickable && this.editable){
13861 // only causes errors at present
13862 //Roo.log(this.store.reader.jsonData);
13863 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13865 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13871 onTypeAhead : function(){
13872 if(this.store.getCount() > 0){
13873 var r = this.store.getAt(0);
13874 var newValue = r.data[this.displayField];
13875 var len = newValue.length;
13876 var selStart = this.getRawValue().length;
13878 if(selStart != len){
13879 this.setRawValue(newValue);
13880 this.selectText(selStart, newValue.length);
13886 onSelect : function(record, index){
13888 if(this.fireEvent('beforeselect', this, record, index) !== false){
13890 this.setFromData(index > -1 ? record.data : false);
13893 this.fireEvent('select', this, record, index);
13898 * Returns the currently selected field value or empty string if no value is set.
13899 * @return {String} value The selected value
13901 getValue : function()
13903 if(Roo.isIOS && this.useNativeIOS){
13904 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13908 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13911 if(this.valueField){
13912 return typeof this.value != 'undefined' ? this.value : '';
13914 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13918 getRawValue : function()
13920 if(Roo.isIOS && this.useNativeIOS){
13921 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13924 var v = this.inputEl().getValue();
13930 * Clears any text/value currently set in the field
13932 clearValue : function(){
13934 if(this.hiddenField){
13935 this.hiddenField.dom.value = '';
13938 this.setRawValue('');
13939 this.lastSelectionText = '';
13940 this.lastData = false;
13942 var close = this.closeTriggerEl();
13953 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13954 * will be displayed in the field. If the value does not match the data value of an existing item,
13955 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13956 * Otherwise the field will be blank (although the value will still be set).
13957 * @param {String} value The value to match
13959 setValue : function(v)
13961 if(Roo.isIOS && this.useNativeIOS){
13962 this.setIOSValue(v);
13972 if(this.valueField){
13973 var r = this.findRecord(this.valueField, v);
13975 text = r.data[this.displayField];
13976 }else if(this.valueNotFoundText !== undefined){
13977 text = this.valueNotFoundText;
13980 this.lastSelectionText = text;
13981 if(this.hiddenField){
13982 this.hiddenField.dom.value = v;
13984 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13987 var close = this.closeTriggerEl();
13990 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13996 * @property {Object} the last set data for the element
14001 * Sets the value of the field based on a object which is related to the record format for the store.
14002 * @param {Object} value the value to set as. or false on reset?
14004 setFromData : function(o){
14011 var dv = ''; // display value
14012 var vv = ''; // value value..
14014 if (this.displayField) {
14015 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14017 // this is an error condition!!!
14018 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14021 if(this.valueField){
14022 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14025 var close = this.closeTriggerEl();
14028 if(dv.length || vv * 1 > 0){
14030 this.blockFocus=true;
14036 if(this.hiddenField){
14037 this.hiddenField.dom.value = vv;
14039 this.lastSelectionText = dv;
14040 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14044 // no hidden field.. - we store the value in 'value', but still display
14045 // display field!!!!
14046 this.lastSelectionText = dv;
14047 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14054 reset : function(){
14055 // overridden so that last data is reset..
14062 this.setValue(this.originalValue);
14063 //this.clearInvalid();
14064 this.lastData = false;
14066 this.view.clearSelections();
14072 findRecord : function(prop, value){
14074 if(this.store.getCount() > 0){
14075 this.store.each(function(r){
14076 if(r.data[prop] == value){
14086 getName: function()
14088 // returns hidden if it's set..
14089 if (!this.rendered) {return ''};
14090 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14094 onViewMove : function(e, t){
14095 this.inKeyMode = false;
14099 onViewOver : function(e, t){
14100 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14103 var item = this.view.findItemFromChild(t);
14106 var index = this.view.indexOf(item);
14107 this.select(index, false);
14112 onViewClick : function(view, doFocus, el, e)
14114 var index = this.view.getSelectedIndexes()[0];
14116 var r = this.store.getAt(index);
14120 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14127 Roo.each(this.tickItems, function(v,k){
14129 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14131 _this.tickItems.splice(k, 1);
14133 if(typeof(e) == 'undefined' && view == false){
14134 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14146 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14147 this.tickItems.push(r.data);
14150 if(typeof(e) == 'undefined' && view == false){
14151 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14158 this.onSelect(r, index);
14160 if(doFocus !== false && !this.blockFocus){
14161 this.inputEl().focus();
14166 restrictHeight : function(){
14167 //this.innerList.dom.style.height = '';
14168 //var inner = this.innerList.dom;
14169 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14170 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14171 //this.list.beginUpdate();
14172 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14173 this.list.alignTo(this.inputEl(), this.listAlign);
14174 this.list.alignTo(this.inputEl(), this.listAlign);
14175 //this.list.endUpdate();
14179 onEmptyResults : function(){
14181 if(this.tickable && this.editable){
14182 this.hasFocus = false;
14183 this.restrictHeight();
14191 * Returns true if the dropdown list is expanded, else false.
14193 isExpanded : function(){
14194 return this.list.isVisible();
14198 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14199 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14200 * @param {String} value The data value of the item to select
14201 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14202 * selected item if it is not currently in view (defaults to true)
14203 * @return {Boolean} True if the value matched an item in the list, else false
14205 selectByValue : function(v, scrollIntoView){
14206 if(v !== undefined && v !== null){
14207 var r = this.findRecord(this.valueField || this.displayField, v);
14209 this.select(this.store.indexOf(r), scrollIntoView);
14217 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14218 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14219 * @param {Number} index The zero-based index of the list item to select
14220 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14221 * selected item if it is not currently in view (defaults to true)
14223 select : function(index, scrollIntoView){
14224 this.selectedIndex = index;
14225 this.view.select(index);
14226 if(scrollIntoView !== false){
14227 var el = this.view.getNode(index);
14229 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14232 this.list.scrollChildIntoView(el, false);
14238 selectNext : function(){
14239 var ct = this.store.getCount();
14241 if(this.selectedIndex == -1){
14243 }else if(this.selectedIndex < ct-1){
14244 this.select(this.selectedIndex+1);
14250 selectPrev : function(){
14251 var ct = this.store.getCount();
14253 if(this.selectedIndex == -1){
14255 }else if(this.selectedIndex != 0){
14256 this.select(this.selectedIndex-1);
14262 onKeyUp : function(e){
14263 if(this.editable !== false && !e.isSpecialKey()){
14264 this.lastKey = e.getKey();
14265 this.dqTask.delay(this.queryDelay);
14270 validateBlur : function(){
14271 return !this.list || !this.list.isVisible();
14275 initQuery : function(){
14277 var v = this.getRawValue();
14279 if(this.tickable && this.editable){
14280 v = this.tickableInputEl().getValue();
14287 doForce : function(){
14288 if(this.inputEl().dom.value.length > 0){
14289 this.inputEl().dom.value =
14290 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14296 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14297 * query allowing the query action to be canceled if needed.
14298 * @param {String} query The SQL query to execute
14299 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14300 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14301 * saved in the current store (defaults to false)
14303 doQuery : function(q, forceAll){
14305 if(q === undefined || q === null){
14310 forceAll: forceAll,
14314 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14319 forceAll = qe.forceAll;
14320 if(forceAll === true || (q.length >= this.minChars)){
14322 this.hasQuery = true;
14324 if(this.lastQuery != q || this.alwaysQuery){
14325 this.lastQuery = q;
14326 if(this.mode == 'local'){
14327 this.selectedIndex = -1;
14329 this.store.clearFilter();
14332 if(this.specialFilter){
14333 this.fireEvent('specialfilter', this);
14338 this.store.filter(this.displayField, q);
14341 this.store.fireEvent("datachanged", this.store);
14348 this.store.baseParams[this.queryParam] = q;
14350 var options = {params : this.getParams(q)};
14353 options.add = true;
14354 options.params.start = this.page * this.pageSize;
14357 this.store.load(options);
14360 * this code will make the page width larger, at the beginning, the list not align correctly,
14361 * we should expand the list on onLoad
14362 * so command out it
14367 this.selectedIndex = -1;
14372 this.loadNext = false;
14376 getParams : function(q){
14378 //p[this.queryParam] = q;
14382 p.limit = this.pageSize;
14388 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14390 collapse : function(){
14391 if(!this.isExpanded()){
14397 this.hasFocus = false;
14401 this.cancelBtn.hide();
14402 this.trigger.show();
14405 this.tickableInputEl().dom.value = '';
14406 this.tickableInputEl().blur();
14411 Roo.get(document).un('mousedown', this.collapseIf, this);
14412 Roo.get(document).un('mousewheel', this.collapseIf, this);
14413 if (!this.editable) {
14414 Roo.get(document).un('keydown', this.listKeyPress, this);
14416 this.fireEvent('collapse', this);
14422 collapseIf : function(e){
14423 var in_combo = e.within(this.el);
14424 var in_list = e.within(this.list);
14425 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14427 if (in_combo || in_list || is_list) {
14428 //e.stopPropagation();
14433 this.onTickableFooterButtonClick(e, false, false);
14441 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14443 expand : function(){
14445 if(this.isExpanded() || !this.hasFocus){
14449 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14450 this.list.setWidth(lw);
14456 this.restrictHeight();
14460 this.tickItems = Roo.apply([], this.item);
14463 this.cancelBtn.show();
14464 this.trigger.hide();
14467 this.tickableInputEl().focus();
14472 Roo.get(document).on('mousedown', this.collapseIf, this);
14473 Roo.get(document).on('mousewheel', this.collapseIf, this);
14474 if (!this.editable) {
14475 Roo.get(document).on('keydown', this.listKeyPress, this);
14478 this.fireEvent('expand', this);
14482 // Implements the default empty TriggerField.onTriggerClick function
14483 onTriggerClick : function(e)
14485 Roo.log('trigger click');
14487 if(this.disabled || !this.triggerList){
14492 this.loadNext = false;
14494 if(this.isExpanded()){
14496 if (!this.blockFocus) {
14497 this.inputEl().focus();
14501 this.hasFocus = true;
14502 if(this.triggerAction == 'all') {
14503 this.doQuery(this.allQuery, true);
14505 this.doQuery(this.getRawValue());
14507 if (!this.blockFocus) {
14508 this.inputEl().focus();
14513 onTickableTriggerClick : function(e)
14520 this.loadNext = false;
14521 this.hasFocus = true;
14523 if(this.triggerAction == 'all') {
14524 this.doQuery(this.allQuery, true);
14526 this.doQuery(this.getRawValue());
14530 onSearchFieldClick : function(e)
14532 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14533 this.onTickableFooterButtonClick(e, false, false);
14537 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14542 this.loadNext = false;
14543 this.hasFocus = true;
14545 if(this.triggerAction == 'all') {
14546 this.doQuery(this.allQuery, true);
14548 this.doQuery(this.getRawValue());
14552 listKeyPress : function(e)
14554 //Roo.log('listkeypress');
14555 // scroll to first matching element based on key pres..
14556 if (e.isSpecialKey()) {
14559 var k = String.fromCharCode(e.getKey()).toUpperCase();
14562 var csel = this.view.getSelectedNodes();
14563 var cselitem = false;
14565 var ix = this.view.indexOf(csel[0]);
14566 cselitem = this.store.getAt(ix);
14567 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14573 this.store.each(function(v) {
14575 // start at existing selection.
14576 if (cselitem.id == v.id) {
14582 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14583 match = this.store.indexOf(v);
14589 if (match === false) {
14590 return true; // no more action?
14593 this.view.select(match);
14594 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14595 sn.scrollIntoView(sn.dom.parentNode, false);
14598 onViewScroll : function(e, t){
14600 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){
14604 this.hasQuery = true;
14606 this.loading = this.list.select('.loading', true).first();
14608 if(this.loading === null){
14609 this.list.createChild({
14611 cls: 'loading roo-select2-more-results roo-select2-active',
14612 html: 'Loading more results...'
14615 this.loading = this.list.select('.loading', true).first();
14617 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14619 this.loading.hide();
14622 this.loading.show();
14627 this.loadNext = true;
14629 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14634 addItem : function(o)
14636 var dv = ''; // display value
14638 if (this.displayField) {
14639 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14641 // this is an error condition!!!
14642 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14649 var choice = this.choices.createChild({
14651 cls: 'roo-select2-search-choice',
14660 cls: 'roo-select2-search-choice-close fa fa-times',
14665 }, this.searchField);
14667 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14669 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14677 this.inputEl().dom.value = '';
14682 onRemoveItem : function(e, _self, o)
14684 e.preventDefault();
14686 this.lastItem = Roo.apply([], this.item);
14688 var index = this.item.indexOf(o.data) * 1;
14691 Roo.log('not this item?!');
14695 this.item.splice(index, 1);
14700 this.fireEvent('remove', this, e);
14706 syncValue : function()
14708 if(!this.item.length){
14715 Roo.each(this.item, function(i){
14716 if(_this.valueField){
14717 value.push(i[_this.valueField]);
14724 this.value = value.join(',');
14726 if(this.hiddenField){
14727 this.hiddenField.dom.value = this.value;
14730 this.store.fireEvent("datachanged", this.store);
14735 clearItem : function()
14737 if(!this.multiple){
14743 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14751 if(this.tickable && !Roo.isTouch){
14752 this.view.refresh();
14756 inputEl: function ()
14758 if(Roo.isIOS && this.useNativeIOS){
14759 return this.el.select('select.roo-ios-select', true).first();
14762 if(Roo.isTouch && this.mobileTouchView){
14763 return this.el.select('input.form-control',true).first();
14767 return this.searchField;
14770 return this.el.select('input.form-control',true).first();
14773 onTickableFooterButtonClick : function(e, btn, el)
14775 e.preventDefault();
14777 this.lastItem = Roo.apply([], this.item);
14779 if(btn && btn.name == 'cancel'){
14780 this.tickItems = Roo.apply([], this.item);
14789 Roo.each(this.tickItems, function(o){
14797 validate : function()
14799 if(this.getVisibilityEl().hasClass('hidden')){
14803 var v = this.getRawValue();
14806 v = this.getValue();
14809 if(this.disabled || this.allowBlank || v.length){
14814 this.markInvalid();
14818 tickableInputEl : function()
14820 if(!this.tickable || !this.editable){
14821 return this.inputEl();
14824 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14828 getAutoCreateTouchView : function()
14833 cls: 'form-group' //input-group
14839 type : this.inputType,
14840 cls : 'form-control x-combo-noedit',
14841 autocomplete: 'new-password',
14842 placeholder : this.placeholder || '',
14847 input.name = this.name;
14851 input.cls += ' input-' + this.size;
14854 if (this.disabled) {
14855 input.disabled = true;
14866 inputblock.cls += ' input-group';
14868 inputblock.cn.unshift({
14870 cls : 'input-group-addon',
14875 if(this.removable && !this.multiple){
14876 inputblock.cls += ' roo-removable';
14878 inputblock.cn.push({
14881 cls : 'roo-combo-removable-btn close'
14885 if(this.hasFeedback && !this.allowBlank){
14887 inputblock.cls += ' has-feedback';
14889 inputblock.cn.push({
14891 cls: 'glyphicon form-control-feedback'
14898 inputblock.cls += (this.before) ? '' : ' input-group';
14900 inputblock.cn.push({
14902 cls : 'input-group-addon',
14913 cls: 'form-hidden-field'
14927 cls: 'form-hidden-field'
14931 cls: 'roo-select2-choices',
14935 cls: 'roo-select2-search-field',
14948 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14954 if(!this.multiple && this.showToggleBtn){
14961 if (this.caret != false) {
14964 cls: 'fa fa-' + this.caret
14971 cls : 'input-group-addon btn dropdown-toggle',
14976 cls: 'combobox-clear',
14990 combobox.cls += ' roo-select2-container-multi';
14993 var align = this.labelAlign || this.parentLabelAlign();
14995 if (align ==='left' && this.fieldLabel.length) {
15000 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15001 tooltip : 'This field is required'
15005 cls : 'control-label',
15006 html : this.fieldLabel
15017 var labelCfg = cfg.cn[1];
15018 var contentCfg = cfg.cn[2];
15021 if(this.indicatorpos == 'right'){
15026 cls : 'control-label',
15030 html : this.fieldLabel
15034 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15035 tooltip : 'This field is required'
15048 labelCfg = cfg.cn[0];
15049 contentCfg = cfg.cn[1];
15054 if(this.labelWidth > 12){
15055 labelCfg.style = "width: " + this.labelWidth + 'px';
15058 if(this.labelWidth < 13 && this.labelmd == 0){
15059 this.labelmd = this.labelWidth;
15062 if(this.labellg > 0){
15063 labelCfg.cls += ' col-lg-' + this.labellg;
15064 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15067 if(this.labelmd > 0){
15068 labelCfg.cls += ' col-md-' + this.labelmd;
15069 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15072 if(this.labelsm > 0){
15073 labelCfg.cls += ' col-sm-' + this.labelsm;
15074 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15077 if(this.labelxs > 0){
15078 labelCfg.cls += ' col-xs-' + this.labelxs;
15079 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15083 } else if ( this.fieldLabel.length) {
15087 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15088 tooltip : 'This field is required'
15092 cls : 'control-label',
15093 html : this.fieldLabel
15104 if(this.indicatorpos == 'right'){
15108 cls : 'control-label',
15109 html : this.fieldLabel,
15113 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15114 tooltip : 'This field is required'
15131 var settings = this;
15133 ['xs','sm','md','lg'].map(function(size){
15134 if (settings[size]) {
15135 cfg.cls += ' col-' + size + '-' + settings[size];
15142 initTouchView : function()
15144 this.renderTouchView();
15146 this.touchViewEl.on('scroll', function(){
15147 this.el.dom.scrollTop = 0;
15150 this.originalValue = this.getValue();
15152 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15154 this.inputEl().on("click", this.showTouchView, this);
15155 if (this.triggerEl) {
15156 this.triggerEl.on("click", this.showTouchView, this);
15160 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15161 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15163 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15165 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15166 this.store.on('load', this.onTouchViewLoad, this);
15167 this.store.on('loadexception', this.onTouchViewLoadException, this);
15169 if(this.hiddenName){
15171 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15173 this.hiddenField.dom.value =
15174 this.hiddenValue !== undefined ? this.hiddenValue :
15175 this.value !== undefined ? this.value : '';
15177 this.el.dom.removeAttribute('name');
15178 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15182 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15183 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15186 if(this.removable && !this.multiple){
15187 var close = this.closeTriggerEl();
15189 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15190 close.on('click', this.removeBtnClick, this, close);
15194 * fix the bug in Safari iOS8
15196 this.inputEl().on("focus", function(e){
15197 document.activeElement.blur();
15205 renderTouchView : function()
15207 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15208 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15210 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15211 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15213 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15214 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15215 this.touchViewBodyEl.setStyle('overflow', 'auto');
15217 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15218 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15220 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15221 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15225 showTouchView : function()
15231 this.touchViewHeaderEl.hide();
15233 if(this.modalTitle.length){
15234 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15235 this.touchViewHeaderEl.show();
15238 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15239 this.touchViewEl.show();
15241 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15243 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15244 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15246 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15248 if(this.modalTitle.length){
15249 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15252 this.touchViewBodyEl.setHeight(bodyHeight);
15256 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15258 this.touchViewEl.addClass('in');
15261 this.doTouchViewQuery();
15265 hideTouchView : function()
15267 this.touchViewEl.removeClass('in');
15271 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15273 this.touchViewEl.setStyle('display', 'none');
15278 setTouchViewValue : function()
15285 Roo.each(this.tickItems, function(o){
15290 this.hideTouchView();
15293 doTouchViewQuery : function()
15302 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15306 if(!this.alwaysQuery || this.mode == 'local'){
15307 this.onTouchViewLoad();
15314 onTouchViewBeforeLoad : function(combo,opts)
15320 onTouchViewLoad : function()
15322 if(this.store.getCount() < 1){
15323 this.onTouchViewEmptyResults();
15327 this.clearTouchView();
15329 var rawValue = this.getRawValue();
15331 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15333 this.tickItems = [];
15335 this.store.data.each(function(d, rowIndex){
15336 var row = this.touchViewListGroup.createChild(template);
15338 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15339 row.addClass(d.data.cls);
15342 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15345 html : d.data[this.displayField]
15348 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15349 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15352 row.removeClass('selected');
15353 if(!this.multiple && this.valueField &&
15354 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15357 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15358 row.addClass('selected');
15361 if(this.multiple && this.valueField &&
15362 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15366 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15367 this.tickItems.push(d.data);
15370 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15374 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15376 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15378 if(this.modalTitle.length){
15379 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15382 var listHeight = this.touchViewListGroup.getHeight();
15386 if(firstChecked && listHeight > bodyHeight){
15387 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15392 onTouchViewLoadException : function()
15394 this.hideTouchView();
15397 onTouchViewEmptyResults : function()
15399 this.clearTouchView();
15401 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15403 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15407 clearTouchView : function()
15409 this.touchViewListGroup.dom.innerHTML = '';
15412 onTouchViewClick : function(e, el, o)
15414 e.preventDefault();
15417 var rowIndex = o.rowIndex;
15419 var r = this.store.getAt(rowIndex);
15421 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15423 if(!this.multiple){
15424 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15425 c.dom.removeAttribute('checked');
15428 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15430 this.setFromData(r.data);
15432 var close = this.closeTriggerEl();
15438 this.hideTouchView();
15440 this.fireEvent('select', this, r, rowIndex);
15445 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15446 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15447 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15451 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15452 this.addItem(r.data);
15453 this.tickItems.push(r.data);
15457 getAutoCreateNativeIOS : function()
15460 cls: 'form-group' //input-group,
15465 cls : 'roo-ios-select'
15469 combobox.name = this.name;
15472 if (this.disabled) {
15473 combobox.disabled = true;
15476 var settings = this;
15478 ['xs','sm','md','lg'].map(function(size){
15479 if (settings[size]) {
15480 cfg.cls += ' col-' + size + '-' + settings[size];
15490 initIOSView : function()
15492 this.store.on('load', this.onIOSViewLoad, this);
15497 onIOSViewLoad : function()
15499 if(this.store.getCount() < 1){
15503 this.clearIOSView();
15505 if(this.allowBlank) {
15507 var default_text = '-- SELECT --';
15509 if(this.placeholder.length){
15510 default_text = this.placeholder;
15513 if(this.emptyTitle.length){
15514 default_text += ' - ' + this.emptyTitle + ' -';
15517 var opt = this.inputEl().createChild({
15520 html : default_text
15524 o[this.valueField] = 0;
15525 o[this.displayField] = default_text;
15527 this.ios_options.push({
15534 this.store.data.each(function(d, rowIndex){
15538 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15539 html = d.data[this.displayField];
15544 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15545 value = d.data[this.valueField];
15554 if(this.value == d.data[this.valueField]){
15555 option['selected'] = true;
15558 var opt = this.inputEl().createChild(option);
15560 this.ios_options.push({
15567 this.inputEl().on('change', function(){
15568 this.fireEvent('select', this);
15573 clearIOSView: function()
15575 this.inputEl().dom.innerHTML = '';
15577 this.ios_options = [];
15580 setIOSValue: function(v)
15584 if(!this.ios_options){
15588 Roo.each(this.ios_options, function(opts){
15590 opts.el.dom.removeAttribute('selected');
15592 if(opts.data[this.valueField] != v){
15596 opts.el.dom.setAttribute('selected', true);
15602 * @cfg {Boolean} grow
15606 * @cfg {Number} growMin
15610 * @cfg {Number} growMax
15619 Roo.apply(Roo.bootstrap.ComboBox, {
15623 cls: 'modal-header',
15645 cls: 'list-group-item',
15649 cls: 'roo-combobox-list-group-item-value'
15653 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15667 listItemCheckbox : {
15669 cls: 'list-group-item',
15673 cls: 'roo-combobox-list-group-item-value'
15677 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15693 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15698 cls: 'modal-footer',
15706 cls: 'col-xs-6 text-left',
15709 cls: 'btn btn-danger roo-touch-view-cancel',
15715 cls: 'col-xs-6 text-right',
15718 cls: 'btn btn-success roo-touch-view-ok',
15729 Roo.apply(Roo.bootstrap.ComboBox, {
15731 touchViewTemplate : {
15733 cls: 'modal fade roo-combobox-touch-view',
15737 cls: 'modal-dialog',
15738 style : 'position:fixed', // we have to fix position....
15742 cls: 'modal-content',
15744 Roo.bootstrap.ComboBox.header,
15745 Roo.bootstrap.ComboBox.body,
15746 Roo.bootstrap.ComboBox.footer
15755 * Ext JS Library 1.1.1
15756 * Copyright(c) 2006-2007, Ext JS, LLC.
15758 * Originally Released Under LGPL - original licence link has changed is not relivant.
15761 * <script type="text/javascript">
15766 * @extends Roo.util.Observable
15767 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15768 * This class also supports single and multi selection modes. <br>
15769 * Create a data model bound view:
15771 var store = new Roo.data.Store(...);
15773 var view = new Roo.View({
15775 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15777 singleSelect: true,
15778 selectedClass: "ydataview-selected",
15782 // listen for node click?
15783 view.on("click", function(vw, index, node, e){
15784 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15788 dataModel.load("foobar.xml");
15790 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15792 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15793 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15795 * Note: old style constructor is still suported (container, template, config)
15798 * Create a new View
15799 * @param {Object} config The config object
15802 Roo.View = function(config, depreciated_tpl, depreciated_config){
15804 this.parent = false;
15806 if (typeof(depreciated_tpl) == 'undefined') {
15807 // new way.. - universal constructor.
15808 Roo.apply(this, config);
15809 this.el = Roo.get(this.el);
15812 this.el = Roo.get(config);
15813 this.tpl = depreciated_tpl;
15814 Roo.apply(this, depreciated_config);
15816 this.wrapEl = this.el.wrap().wrap();
15817 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15820 if(typeof(this.tpl) == "string"){
15821 this.tpl = new Roo.Template(this.tpl);
15823 // support xtype ctors..
15824 this.tpl = new Roo.factory(this.tpl, Roo);
15828 this.tpl.compile();
15833 * @event beforeclick
15834 * Fires before a click is processed. Returns false to cancel the default action.
15835 * @param {Roo.View} this
15836 * @param {Number} index The index of the target node
15837 * @param {HTMLElement} node The target node
15838 * @param {Roo.EventObject} e The raw event object
15840 "beforeclick" : true,
15843 * Fires when a template node is clicked.
15844 * @param {Roo.View} this
15845 * @param {Number} index The index of the target node
15846 * @param {HTMLElement} node The target node
15847 * @param {Roo.EventObject} e The raw event object
15852 * Fires when a template node is double clicked.
15853 * @param {Roo.View} this
15854 * @param {Number} index The index of the target node
15855 * @param {HTMLElement} node The target node
15856 * @param {Roo.EventObject} e The raw event object
15860 * @event contextmenu
15861 * Fires when a template node is right clicked.
15862 * @param {Roo.View} this
15863 * @param {Number} index The index of the target node
15864 * @param {HTMLElement} node The target node
15865 * @param {Roo.EventObject} e The raw event object
15867 "contextmenu" : true,
15869 * @event selectionchange
15870 * Fires when the selected nodes change.
15871 * @param {Roo.View} this
15872 * @param {Array} selections Array of the selected nodes
15874 "selectionchange" : true,
15877 * @event beforeselect
15878 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15879 * @param {Roo.View} this
15880 * @param {HTMLElement} node The node to be selected
15881 * @param {Array} selections Array of currently selected nodes
15883 "beforeselect" : true,
15885 * @event preparedata
15886 * Fires on every row to render, to allow you to change the data.
15887 * @param {Roo.View} this
15888 * @param {Object} data to be rendered (change this)
15890 "preparedata" : true
15898 "click": this.onClick,
15899 "dblclick": this.onDblClick,
15900 "contextmenu": this.onContextMenu,
15904 this.selections = [];
15906 this.cmp = new Roo.CompositeElementLite([]);
15908 this.store = Roo.factory(this.store, Roo.data);
15909 this.setStore(this.store, true);
15912 if ( this.footer && this.footer.xtype) {
15914 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15916 this.footer.dataSource = this.store;
15917 this.footer.container = fctr;
15918 this.footer = Roo.factory(this.footer, Roo);
15919 fctr.insertFirst(this.el);
15921 // this is a bit insane - as the paging toolbar seems to detach the el..
15922 // dom.parentNode.parentNode.parentNode
15923 // they get detached?
15927 Roo.View.superclass.constructor.call(this);
15932 Roo.extend(Roo.View, Roo.util.Observable, {
15935 * @cfg {Roo.data.Store} store Data store to load data from.
15940 * @cfg {String|Roo.Element} el The container element.
15945 * @cfg {String|Roo.Template} tpl The template used by this View
15949 * @cfg {String} dataName the named area of the template to use as the data area
15950 * Works with domtemplates roo-name="name"
15954 * @cfg {String} selectedClass The css class to add to selected nodes
15956 selectedClass : "x-view-selected",
15958 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15963 * @cfg {String} text to display on mask (default Loading)
15967 * @cfg {Boolean} multiSelect Allow multiple selection
15969 multiSelect : false,
15971 * @cfg {Boolean} singleSelect Allow single selection
15973 singleSelect: false,
15976 * @cfg {Boolean} toggleSelect - selecting
15978 toggleSelect : false,
15981 * @cfg {Boolean} tickable - selecting
15986 * Returns the element this view is bound to.
15987 * @return {Roo.Element}
15989 getEl : function(){
15990 return this.wrapEl;
15996 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15998 refresh : function(){
15999 //Roo.log('refresh');
16002 // if we are using something like 'domtemplate', then
16003 // the what gets used is:
16004 // t.applySubtemplate(NAME, data, wrapping data..)
16005 // the outer template then get' applied with
16006 // the store 'extra data'
16007 // and the body get's added to the
16008 // roo-name="data" node?
16009 // <span class='roo-tpl-{name}'></span> ?????
16013 this.clearSelections();
16014 this.el.update("");
16016 var records = this.store.getRange();
16017 if(records.length < 1) {
16019 // is this valid?? = should it render a template??
16021 this.el.update(this.emptyText);
16025 if (this.dataName) {
16026 this.el.update(t.apply(this.store.meta)); //????
16027 el = this.el.child('.roo-tpl-' + this.dataName);
16030 for(var i = 0, len = records.length; i < len; i++){
16031 var data = this.prepareData(records[i].data, i, records[i]);
16032 this.fireEvent("preparedata", this, data, i, records[i]);
16034 var d = Roo.apply({}, data);
16037 Roo.apply(d, {'roo-id' : Roo.id()});
16041 Roo.each(this.parent.item, function(item){
16042 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16045 Roo.apply(d, {'roo-data-checked' : 'checked'});
16049 html[html.length] = Roo.util.Format.trim(
16051 t.applySubtemplate(this.dataName, d, this.store.meta) :
16058 el.update(html.join(""));
16059 this.nodes = el.dom.childNodes;
16060 this.updateIndexes(0);
16065 * Function to override to reformat the data that is sent to
16066 * the template for each node.
16067 * DEPRICATED - use the preparedata event handler.
16068 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16069 * a JSON object for an UpdateManager bound view).
16071 prepareData : function(data, index, record)
16073 this.fireEvent("preparedata", this, data, index, record);
16077 onUpdate : function(ds, record){
16078 // Roo.log('on update');
16079 this.clearSelections();
16080 var index = this.store.indexOf(record);
16081 var n = this.nodes[index];
16082 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16083 n.parentNode.removeChild(n);
16084 this.updateIndexes(index, index);
16090 onAdd : function(ds, records, index)
16092 //Roo.log(['on Add', ds, records, index] );
16093 this.clearSelections();
16094 if(this.nodes.length == 0){
16098 var n = this.nodes[index];
16099 for(var i = 0, len = records.length; i < len; i++){
16100 var d = this.prepareData(records[i].data, i, records[i]);
16102 this.tpl.insertBefore(n, d);
16105 this.tpl.append(this.el, d);
16108 this.updateIndexes(index);
16111 onRemove : function(ds, record, index){
16112 // Roo.log('onRemove');
16113 this.clearSelections();
16114 var el = this.dataName ?
16115 this.el.child('.roo-tpl-' + this.dataName) :
16118 el.dom.removeChild(this.nodes[index]);
16119 this.updateIndexes(index);
16123 * Refresh an individual node.
16124 * @param {Number} index
16126 refreshNode : function(index){
16127 this.onUpdate(this.store, this.store.getAt(index));
16130 updateIndexes : function(startIndex, endIndex){
16131 var ns = this.nodes;
16132 startIndex = startIndex || 0;
16133 endIndex = endIndex || ns.length - 1;
16134 for(var i = startIndex; i <= endIndex; i++){
16135 ns[i].nodeIndex = i;
16140 * Changes the data store this view uses and refresh the view.
16141 * @param {Store} store
16143 setStore : function(store, initial){
16144 if(!initial && this.store){
16145 this.store.un("datachanged", this.refresh);
16146 this.store.un("add", this.onAdd);
16147 this.store.un("remove", this.onRemove);
16148 this.store.un("update", this.onUpdate);
16149 this.store.un("clear", this.refresh);
16150 this.store.un("beforeload", this.onBeforeLoad);
16151 this.store.un("load", this.onLoad);
16152 this.store.un("loadexception", this.onLoad);
16156 store.on("datachanged", this.refresh, this);
16157 store.on("add", this.onAdd, this);
16158 store.on("remove", this.onRemove, this);
16159 store.on("update", this.onUpdate, this);
16160 store.on("clear", this.refresh, this);
16161 store.on("beforeload", this.onBeforeLoad, this);
16162 store.on("load", this.onLoad, this);
16163 store.on("loadexception", this.onLoad, this);
16171 * onbeforeLoad - masks the loading area.
16174 onBeforeLoad : function(store,opts)
16176 //Roo.log('onBeforeLoad');
16178 this.el.update("");
16180 this.el.mask(this.mask ? this.mask : "Loading" );
16182 onLoad : function ()
16189 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16190 * @param {HTMLElement} node
16191 * @return {HTMLElement} The template node
16193 findItemFromChild : function(node){
16194 var el = this.dataName ?
16195 this.el.child('.roo-tpl-' + this.dataName,true) :
16198 if(!node || node.parentNode == el){
16201 var p = node.parentNode;
16202 while(p && p != el){
16203 if(p.parentNode == el){
16212 onClick : function(e){
16213 var item = this.findItemFromChild(e.getTarget());
16215 var index = this.indexOf(item);
16216 if(this.onItemClick(item, index, e) !== false){
16217 this.fireEvent("click", this, index, item, e);
16220 this.clearSelections();
16225 onContextMenu : function(e){
16226 var item = this.findItemFromChild(e.getTarget());
16228 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16233 onDblClick : function(e){
16234 var item = this.findItemFromChild(e.getTarget());
16236 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16240 onItemClick : function(item, index, e)
16242 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16245 if (this.toggleSelect) {
16246 var m = this.isSelected(item) ? 'unselect' : 'select';
16249 _t[m](item, true, false);
16252 if(this.multiSelect || this.singleSelect){
16253 if(this.multiSelect && e.shiftKey && this.lastSelection){
16254 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16256 this.select(item, this.multiSelect && e.ctrlKey);
16257 this.lastSelection = item;
16260 if(!this.tickable){
16261 e.preventDefault();
16269 * Get the number of selected nodes.
16272 getSelectionCount : function(){
16273 return this.selections.length;
16277 * Get the currently selected nodes.
16278 * @return {Array} An array of HTMLElements
16280 getSelectedNodes : function(){
16281 return this.selections;
16285 * Get the indexes of the selected nodes.
16288 getSelectedIndexes : function(){
16289 var indexes = [], s = this.selections;
16290 for(var i = 0, len = s.length; i < len; i++){
16291 indexes.push(s[i].nodeIndex);
16297 * Clear all selections
16298 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16300 clearSelections : function(suppressEvent){
16301 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16302 this.cmp.elements = this.selections;
16303 this.cmp.removeClass(this.selectedClass);
16304 this.selections = [];
16305 if(!suppressEvent){
16306 this.fireEvent("selectionchange", this, this.selections);
16312 * Returns true if the passed node is selected
16313 * @param {HTMLElement/Number} node The node or node index
16314 * @return {Boolean}
16316 isSelected : function(node){
16317 var s = this.selections;
16321 node = this.getNode(node);
16322 return s.indexOf(node) !== -1;
16327 * @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
16328 * @param {Boolean} keepExisting (optional) true to keep existing selections
16329 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16331 select : function(nodeInfo, keepExisting, suppressEvent){
16332 if(nodeInfo instanceof Array){
16334 this.clearSelections(true);
16336 for(var i = 0, len = nodeInfo.length; i < len; i++){
16337 this.select(nodeInfo[i], true, true);
16341 var node = this.getNode(nodeInfo);
16342 if(!node || this.isSelected(node)){
16343 return; // already selected.
16346 this.clearSelections(true);
16349 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16350 Roo.fly(node).addClass(this.selectedClass);
16351 this.selections.push(node);
16352 if(!suppressEvent){
16353 this.fireEvent("selectionchange", this, this.selections);
16361 * @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
16362 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16363 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16365 unselect : function(nodeInfo, keepExisting, suppressEvent)
16367 if(nodeInfo instanceof Array){
16368 Roo.each(this.selections, function(s) {
16369 this.unselect(s, nodeInfo);
16373 var node = this.getNode(nodeInfo);
16374 if(!node || !this.isSelected(node)){
16375 //Roo.log("not selected");
16376 return; // not selected.
16380 Roo.each(this.selections, function(s) {
16382 Roo.fly(node).removeClass(this.selectedClass);
16389 this.selections= ns;
16390 this.fireEvent("selectionchange", this, this.selections);
16394 * Gets a template node.
16395 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16396 * @return {HTMLElement} The node or null if it wasn't found
16398 getNode : function(nodeInfo){
16399 if(typeof nodeInfo == "string"){
16400 return document.getElementById(nodeInfo);
16401 }else if(typeof nodeInfo == "number"){
16402 return this.nodes[nodeInfo];
16408 * Gets a range template nodes.
16409 * @param {Number} startIndex
16410 * @param {Number} endIndex
16411 * @return {Array} An array of nodes
16413 getNodes : function(start, end){
16414 var ns = this.nodes;
16415 start = start || 0;
16416 end = typeof end == "undefined" ? ns.length - 1 : end;
16419 for(var i = start; i <= end; i++){
16423 for(var i = start; i >= end; i--){
16431 * Finds the index of the passed node
16432 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16433 * @return {Number} The index of the node or -1
16435 indexOf : function(node){
16436 node = this.getNode(node);
16437 if(typeof node.nodeIndex == "number"){
16438 return node.nodeIndex;
16440 var ns = this.nodes;
16441 for(var i = 0, len = ns.length; i < len; i++){
16452 * based on jquery fullcalendar
16456 Roo.bootstrap = Roo.bootstrap || {};
16458 * @class Roo.bootstrap.Calendar
16459 * @extends Roo.bootstrap.Component
16460 * Bootstrap Calendar class
16461 * @cfg {Boolean} loadMask (true|false) default false
16462 * @cfg {Object} header generate the user specific header of the calendar, default false
16465 * Create a new Container
16466 * @param {Object} config The config object
16471 Roo.bootstrap.Calendar = function(config){
16472 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16476 * Fires when a date is selected
16477 * @param {DatePicker} this
16478 * @param {Date} date The selected date
16482 * @event monthchange
16483 * Fires when the displayed month changes
16484 * @param {DatePicker} this
16485 * @param {Date} date The selected month
16487 'monthchange': true,
16489 * @event evententer
16490 * Fires when mouse over an event
16491 * @param {Calendar} this
16492 * @param {event} Event
16494 'evententer': true,
16496 * @event eventleave
16497 * Fires when the mouse leaves an
16498 * @param {Calendar} this
16501 'eventleave': true,
16503 * @event eventclick
16504 * Fires when the mouse click an
16505 * @param {Calendar} this
16514 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16517 * @cfg {Number} startDay
16518 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16526 getAutoCreate : function(){
16529 var fc_button = function(name, corner, style, content ) {
16530 return Roo.apply({},{
16532 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16534 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16537 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16548 style : 'width:100%',
16555 cls : 'fc-header-left',
16557 fc_button('prev', 'left', 'arrow', '‹' ),
16558 fc_button('next', 'right', 'arrow', '›' ),
16559 { tag: 'span', cls: 'fc-header-space' },
16560 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16568 cls : 'fc-header-center',
16572 cls: 'fc-header-title',
16575 html : 'month / year'
16583 cls : 'fc-header-right',
16585 /* fc_button('month', 'left', '', 'month' ),
16586 fc_button('week', '', '', 'week' ),
16587 fc_button('day', 'right', '', 'day' )
16599 header = this.header;
16602 var cal_heads = function() {
16604 // fixme - handle this.
16606 for (var i =0; i < Date.dayNames.length; i++) {
16607 var d = Date.dayNames[i];
16610 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16611 html : d.substring(0,3)
16615 ret[0].cls += ' fc-first';
16616 ret[6].cls += ' fc-last';
16619 var cal_cell = function(n) {
16622 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16627 cls: 'fc-day-number',
16631 cls: 'fc-day-content',
16635 style: 'position: relative;' // height: 17px;
16647 var cal_rows = function() {
16650 for (var r = 0; r < 6; r++) {
16657 for (var i =0; i < Date.dayNames.length; i++) {
16658 var d = Date.dayNames[i];
16659 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16662 row.cn[0].cls+=' fc-first';
16663 row.cn[0].cn[0].style = 'min-height:90px';
16664 row.cn[6].cls+=' fc-last';
16668 ret[0].cls += ' fc-first';
16669 ret[4].cls += ' fc-prev-last';
16670 ret[5].cls += ' fc-last';
16677 cls: 'fc-border-separate',
16678 style : 'width:100%',
16686 cls : 'fc-first fc-last',
16704 cls : 'fc-content',
16705 style : "position: relative;",
16708 cls : 'fc-view fc-view-month fc-grid',
16709 style : 'position: relative',
16710 unselectable : 'on',
16713 cls : 'fc-event-container',
16714 style : 'position:absolute;z-index:8;top:0;left:0;'
16732 initEvents : function()
16735 throw "can not find store for calendar";
16741 style: "text-align:center",
16745 style: "background-color:white;width:50%;margin:250 auto",
16749 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16760 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16762 var size = this.el.select('.fc-content', true).first().getSize();
16763 this.maskEl.setSize(size.width, size.height);
16764 this.maskEl.enableDisplayMode("block");
16765 if(!this.loadMask){
16766 this.maskEl.hide();
16769 this.store = Roo.factory(this.store, Roo.data);
16770 this.store.on('load', this.onLoad, this);
16771 this.store.on('beforeload', this.onBeforeLoad, this);
16775 this.cells = this.el.select('.fc-day',true);
16776 //Roo.log(this.cells);
16777 this.textNodes = this.el.query('.fc-day-number');
16778 this.cells.addClassOnOver('fc-state-hover');
16780 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16781 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16782 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16783 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16785 this.on('monthchange', this.onMonthChange, this);
16787 this.update(new Date().clearTime());
16790 resize : function() {
16791 var sz = this.el.getSize();
16793 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16794 this.el.select('.fc-day-content div',true).setHeight(34);
16799 showPrevMonth : function(e){
16800 this.update(this.activeDate.add("mo", -1));
16802 showToday : function(e){
16803 this.update(new Date().clearTime());
16806 showNextMonth : function(e){
16807 this.update(this.activeDate.add("mo", 1));
16811 showPrevYear : function(){
16812 this.update(this.activeDate.add("y", -1));
16816 showNextYear : function(){
16817 this.update(this.activeDate.add("y", 1));
16822 update : function(date)
16824 var vd = this.activeDate;
16825 this.activeDate = date;
16826 // if(vd && this.el){
16827 // var t = date.getTime();
16828 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16829 // Roo.log('using add remove');
16831 // this.fireEvent('monthchange', this, date);
16833 // this.cells.removeClass("fc-state-highlight");
16834 // this.cells.each(function(c){
16835 // if(c.dateValue == t){
16836 // c.addClass("fc-state-highlight");
16837 // setTimeout(function(){
16838 // try{c.dom.firstChild.focus();}catch(e){}
16848 var days = date.getDaysInMonth();
16850 var firstOfMonth = date.getFirstDateOfMonth();
16851 var startingPos = firstOfMonth.getDay()-this.startDay;
16853 if(startingPos < this.startDay){
16857 var pm = date.add(Date.MONTH, -1);
16858 var prevStart = pm.getDaysInMonth()-startingPos;
16860 this.cells = this.el.select('.fc-day',true);
16861 this.textNodes = this.el.query('.fc-day-number');
16862 this.cells.addClassOnOver('fc-state-hover');
16864 var cells = this.cells.elements;
16865 var textEls = this.textNodes;
16867 Roo.each(cells, function(cell){
16868 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16871 days += startingPos;
16873 // convert everything to numbers so it's fast
16874 var day = 86400000;
16875 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16878 //Roo.log(prevStart);
16880 var today = new Date().clearTime().getTime();
16881 var sel = date.clearTime().getTime();
16882 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16883 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16884 var ddMatch = this.disabledDatesRE;
16885 var ddText = this.disabledDatesText;
16886 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16887 var ddaysText = this.disabledDaysText;
16888 var format = this.format;
16890 var setCellClass = function(cal, cell){
16894 //Roo.log('set Cell Class');
16896 var t = d.getTime();
16900 cell.dateValue = t;
16902 cell.className += " fc-today";
16903 cell.className += " fc-state-highlight";
16904 cell.title = cal.todayText;
16907 // disable highlight in other month..
16908 //cell.className += " fc-state-highlight";
16913 cell.className = " fc-state-disabled";
16914 cell.title = cal.minText;
16918 cell.className = " fc-state-disabled";
16919 cell.title = cal.maxText;
16923 if(ddays.indexOf(d.getDay()) != -1){
16924 cell.title = ddaysText;
16925 cell.className = " fc-state-disabled";
16928 if(ddMatch && format){
16929 var fvalue = d.dateFormat(format);
16930 if(ddMatch.test(fvalue)){
16931 cell.title = ddText.replace("%0", fvalue);
16932 cell.className = " fc-state-disabled";
16936 if (!cell.initialClassName) {
16937 cell.initialClassName = cell.dom.className;
16940 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16945 for(; i < startingPos; i++) {
16946 textEls[i].innerHTML = (++prevStart);
16947 d.setDate(d.getDate()+1);
16949 cells[i].className = "fc-past fc-other-month";
16950 setCellClass(this, cells[i]);
16955 for(; i < days; i++){
16956 intDay = i - startingPos + 1;
16957 textEls[i].innerHTML = (intDay);
16958 d.setDate(d.getDate()+1);
16960 cells[i].className = ''; // "x-date-active";
16961 setCellClass(this, cells[i]);
16965 for(; i < 42; i++) {
16966 textEls[i].innerHTML = (++extraDays);
16967 d.setDate(d.getDate()+1);
16969 cells[i].className = "fc-future fc-other-month";
16970 setCellClass(this, cells[i]);
16973 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16975 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16977 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16978 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16980 if(totalRows != 6){
16981 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16982 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16985 this.fireEvent('monthchange', this, date);
16989 if(!this.internalRender){
16990 var main = this.el.dom.firstChild;
16991 var w = main.offsetWidth;
16992 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16993 Roo.fly(main).setWidth(w);
16994 this.internalRender = true;
16995 // opera does not respect the auto grow header center column
16996 // then, after it gets a width opera refuses to recalculate
16997 // without a second pass
16998 if(Roo.isOpera && !this.secondPass){
16999 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17000 this.secondPass = true;
17001 this.update.defer(10, this, [date]);
17008 findCell : function(dt) {
17009 dt = dt.clearTime().getTime();
17011 this.cells.each(function(c){
17012 //Roo.log("check " +c.dateValue + '?=' + dt);
17013 if(c.dateValue == dt){
17023 findCells : function(ev) {
17024 var s = ev.start.clone().clearTime().getTime();
17026 var e= ev.end.clone().clearTime().getTime();
17029 this.cells.each(function(c){
17030 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17032 if(c.dateValue > e){
17035 if(c.dateValue < s){
17044 // findBestRow: function(cells)
17048 // for (var i =0 ; i < cells.length;i++) {
17049 // ret = Math.max(cells[i].rows || 0,ret);
17056 addItem : function(ev)
17058 // look for vertical location slot in
17059 var cells = this.findCells(ev);
17061 // ev.row = this.findBestRow(cells);
17063 // work out the location.
17067 for(var i =0; i < cells.length; i++) {
17069 cells[i].row = cells[0].row;
17072 cells[i].row = cells[i].row + 1;
17082 if (crow.start.getY() == cells[i].getY()) {
17084 crow.end = cells[i];
17101 cells[0].events.push(ev);
17103 this.calevents.push(ev);
17106 clearEvents: function() {
17108 if(!this.calevents){
17112 Roo.each(this.cells.elements, function(c){
17118 Roo.each(this.calevents, function(e) {
17119 Roo.each(e.els, function(el) {
17120 el.un('mouseenter' ,this.onEventEnter, this);
17121 el.un('mouseleave' ,this.onEventLeave, this);
17126 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17132 renderEvents: function()
17136 this.cells.each(function(c) {
17145 if(c.row != c.events.length){
17146 r = 4 - (4 - (c.row - c.events.length));
17149 c.events = ev.slice(0, r);
17150 c.more = ev.slice(r);
17152 if(c.more.length && c.more.length == 1){
17153 c.events.push(c.more.pop());
17156 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17160 this.cells.each(function(c) {
17162 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17165 for (var e = 0; e < c.events.length; e++){
17166 var ev = c.events[e];
17167 var rows = ev.rows;
17169 for(var i = 0; i < rows.length; i++) {
17171 // how many rows should it span..
17174 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17175 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17177 unselectable : "on",
17180 cls: 'fc-event-inner',
17184 // cls: 'fc-event-time',
17185 // html : cells.length > 1 ? '' : ev.time
17189 cls: 'fc-event-title',
17190 html : String.format('{0}', ev.title)
17197 cls: 'ui-resizable-handle ui-resizable-e',
17198 html : '  '
17205 cfg.cls += ' fc-event-start';
17207 if ((i+1) == rows.length) {
17208 cfg.cls += ' fc-event-end';
17211 var ctr = _this.el.select('.fc-event-container',true).first();
17212 var cg = ctr.createChild(cfg);
17214 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17215 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17217 var r = (c.more.length) ? 1 : 0;
17218 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17219 cg.setWidth(ebox.right - sbox.x -2);
17221 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17222 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17223 cg.on('click', _this.onEventClick, _this, ev);
17234 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17235 style : 'position: absolute',
17236 unselectable : "on",
17239 cls: 'fc-event-inner',
17243 cls: 'fc-event-title',
17251 cls: 'ui-resizable-handle ui-resizable-e',
17252 html : '  '
17258 var ctr = _this.el.select('.fc-event-container',true).first();
17259 var cg = ctr.createChild(cfg);
17261 var sbox = c.select('.fc-day-content',true).first().getBox();
17262 var ebox = c.select('.fc-day-content',true).first().getBox();
17264 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17265 cg.setWidth(ebox.right - sbox.x -2);
17267 cg.on('click', _this.onMoreEventClick, _this, c.more);
17277 onEventEnter: function (e, el,event,d) {
17278 this.fireEvent('evententer', this, el, event);
17281 onEventLeave: function (e, el,event,d) {
17282 this.fireEvent('eventleave', this, el, event);
17285 onEventClick: function (e, el,event,d) {
17286 this.fireEvent('eventclick', this, el, event);
17289 onMonthChange: function () {
17293 onMoreEventClick: function(e, el, more)
17297 this.calpopover.placement = 'right';
17298 this.calpopover.setTitle('More');
17300 this.calpopover.setContent('');
17302 var ctr = this.calpopover.el.select('.popover-content', true).first();
17304 Roo.each(more, function(m){
17306 cls : 'fc-event-hori fc-event-draggable',
17309 var cg = ctr.createChild(cfg);
17311 cg.on('click', _this.onEventClick, _this, m);
17314 this.calpopover.show(el);
17319 onLoad: function ()
17321 this.calevents = [];
17324 if(this.store.getCount() > 0){
17325 this.store.data.each(function(d){
17328 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17329 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17330 time : d.data.start_time,
17331 title : d.data.title,
17332 description : d.data.description,
17333 venue : d.data.venue
17338 this.renderEvents();
17340 if(this.calevents.length && this.loadMask){
17341 this.maskEl.hide();
17345 onBeforeLoad: function()
17347 this.clearEvents();
17349 this.maskEl.show();
17363 * @class Roo.bootstrap.Popover
17364 * @extends Roo.bootstrap.Component
17365 * Bootstrap Popover class
17366 * @cfg {String} html contents of the popover (or false to use children..)
17367 * @cfg {String} title of popover (or false to hide)
17368 * @cfg {String} placement how it is placed
17369 * @cfg {String} trigger click || hover (or false to trigger manually)
17370 * @cfg {String} over what (parent or false to trigger manually.)
17371 * @cfg {Number} delay - delay before showing
17374 * Create a new Popover
17375 * @param {Object} config The config object
17378 Roo.bootstrap.Popover = function(config){
17379 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17385 * After the popover show
17387 * @param {Roo.bootstrap.Popover} this
17392 * After the popover hide
17394 * @param {Roo.bootstrap.Popover} this
17400 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17402 title: 'Fill in a title',
17405 placement : 'right',
17406 trigger : 'hover', // hover
17412 can_build_overlaid : false,
17414 getChildContainer : function()
17416 return this.el.select('.popover-content',true).first();
17419 getAutoCreate : function(){
17422 cls : 'popover roo-dynamic',
17423 style: 'display:block',
17429 cls : 'popover-inner',
17433 cls: 'popover-title',
17437 cls : 'popover-content',
17448 setTitle: function(str)
17451 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17453 setContent: function(str)
17456 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17458 // as it get's added to the bottom of the page.
17459 onRender : function(ct, position)
17461 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17463 var cfg = Roo.apply({}, this.getAutoCreate());
17467 cfg.cls += ' ' + this.cls;
17470 cfg.style = this.style;
17472 //Roo.log("adding to ");
17473 this.el = Roo.get(document.body).createChild(cfg, position);
17474 // Roo.log(this.el);
17479 initEvents : function()
17481 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17482 this.el.enableDisplayMode('block');
17484 if (this.over === false) {
17487 if (this.triggers === false) {
17490 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17491 var triggers = this.trigger ? this.trigger.split(' ') : [];
17492 Roo.each(triggers, function(trigger) {
17494 if (trigger == 'click') {
17495 on_el.on('click', this.toggle, this);
17496 } else if (trigger != 'manual') {
17497 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17498 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17500 on_el.on(eventIn ,this.enter, this);
17501 on_el.on(eventOut, this.leave, this);
17512 toggle : function () {
17513 this.hoverState == 'in' ? this.leave() : this.enter();
17516 enter : function () {
17518 clearTimeout(this.timeout);
17520 this.hoverState = 'in';
17522 if (!this.delay || !this.delay.show) {
17527 this.timeout = setTimeout(function () {
17528 if (_t.hoverState == 'in') {
17531 }, this.delay.show)
17534 leave : function() {
17535 clearTimeout(this.timeout);
17537 this.hoverState = 'out';
17539 if (!this.delay || !this.delay.hide) {
17544 this.timeout = setTimeout(function () {
17545 if (_t.hoverState == 'out') {
17548 }, this.delay.hide)
17551 show : function (on_el)
17554 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17558 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17559 if (this.html !== false) {
17560 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17562 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17563 if (!this.title.length) {
17564 this.el.select('.popover-title',true).hide();
17567 var placement = typeof this.placement == 'function' ?
17568 this.placement.call(this, this.el, on_el) :
17571 var autoToken = /\s?auto?\s?/i;
17572 var autoPlace = autoToken.test(placement);
17574 placement = placement.replace(autoToken, '') || 'top';
17578 //this.el.setXY([0,0]);
17580 this.el.dom.style.display='block';
17581 this.el.addClass(placement);
17583 //this.el.appendTo(on_el);
17585 var p = this.getPosition();
17586 var box = this.el.getBox();
17591 var align = Roo.bootstrap.Popover.alignment[placement];
17594 this.el.alignTo(on_el, align[0],align[1]);
17595 //var arrow = this.el.select('.arrow',true).first();
17596 //arrow.set(align[2],
17598 this.el.addClass('in');
17601 if (this.el.hasClass('fade')) {
17605 this.hoverState = 'in';
17607 this.fireEvent('show', this);
17612 this.el.setXY([0,0]);
17613 this.el.removeClass('in');
17615 this.hoverState = null;
17617 this.fireEvent('hide', this);
17622 Roo.bootstrap.Popover.alignment = {
17623 'left' : ['r-l', [-10,0], 'right'],
17624 'right' : ['l-r', [10,0], 'left'],
17625 'bottom' : ['t-b', [0,10], 'top'],
17626 'top' : [ 'b-t', [0,-10], 'bottom']
17637 * @class Roo.bootstrap.Progress
17638 * @extends Roo.bootstrap.Component
17639 * Bootstrap Progress class
17640 * @cfg {Boolean} striped striped of the progress bar
17641 * @cfg {Boolean} active animated of the progress bar
17645 * Create a new Progress
17646 * @param {Object} config The config object
17649 Roo.bootstrap.Progress = function(config){
17650 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17653 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17658 getAutoCreate : function(){
17666 cfg.cls += ' progress-striped';
17670 cfg.cls += ' active';
17689 * @class Roo.bootstrap.ProgressBar
17690 * @extends Roo.bootstrap.Component
17691 * Bootstrap ProgressBar class
17692 * @cfg {Number} aria_valuenow aria-value now
17693 * @cfg {Number} aria_valuemin aria-value min
17694 * @cfg {Number} aria_valuemax aria-value max
17695 * @cfg {String} label label for the progress bar
17696 * @cfg {String} panel (success | info | warning | danger )
17697 * @cfg {String} role role of the progress bar
17698 * @cfg {String} sr_only text
17702 * Create a new ProgressBar
17703 * @param {Object} config The config object
17706 Roo.bootstrap.ProgressBar = function(config){
17707 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17710 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17714 aria_valuemax : 100,
17720 getAutoCreate : function()
17725 cls: 'progress-bar',
17726 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17738 cfg.role = this.role;
17741 if(this.aria_valuenow){
17742 cfg['aria-valuenow'] = this.aria_valuenow;
17745 if(this.aria_valuemin){
17746 cfg['aria-valuemin'] = this.aria_valuemin;
17749 if(this.aria_valuemax){
17750 cfg['aria-valuemax'] = this.aria_valuemax;
17753 if(this.label && !this.sr_only){
17754 cfg.html = this.label;
17758 cfg.cls += ' progress-bar-' + this.panel;
17764 update : function(aria_valuenow)
17766 this.aria_valuenow = aria_valuenow;
17768 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17783 * @class Roo.bootstrap.TabGroup
17784 * @extends Roo.bootstrap.Column
17785 * Bootstrap Column class
17786 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17787 * @cfg {Boolean} carousel true to make the group behave like a carousel
17788 * @cfg {Boolean} bullets show bullets for the panels
17789 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17790 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17791 * @cfg {Boolean} showarrow (true|false) show arrow default true
17794 * Create a new TabGroup
17795 * @param {Object} config The config object
17798 Roo.bootstrap.TabGroup = function(config){
17799 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17801 this.navId = Roo.id();
17804 Roo.bootstrap.TabGroup.register(this);
17808 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17811 transition : false,
17816 slideOnTouch : false,
17819 getAutoCreate : function()
17821 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17823 cfg.cls += ' tab-content';
17825 if (this.carousel) {
17826 cfg.cls += ' carousel slide';
17829 cls : 'carousel-inner',
17833 if(this.bullets && !Roo.isTouch){
17836 cls : 'carousel-bullets',
17840 if(this.bullets_cls){
17841 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17848 cfg.cn[0].cn.push(bullets);
17851 if(this.showarrow){
17852 cfg.cn[0].cn.push({
17854 class : 'carousel-arrow',
17858 class : 'carousel-prev',
17862 class : 'fa fa-chevron-left'
17868 class : 'carousel-next',
17872 class : 'fa fa-chevron-right'
17885 initEvents: function()
17887 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17888 // this.el.on("touchstart", this.onTouchStart, this);
17891 if(this.autoslide){
17894 this.slideFn = window.setInterval(function() {
17895 _this.showPanelNext();
17899 if(this.showarrow){
17900 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17901 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17907 // onTouchStart : function(e, el, o)
17909 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17913 // this.showPanelNext();
17917 getChildContainer : function()
17919 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17923 * register a Navigation item
17924 * @param {Roo.bootstrap.NavItem} the navitem to add
17926 register : function(item)
17928 this.tabs.push( item);
17929 item.navId = this.navId; // not really needed..
17934 getActivePanel : function()
17937 Roo.each(this.tabs, function(t) {
17947 getPanelByName : function(n)
17950 Roo.each(this.tabs, function(t) {
17951 if (t.tabId == n) {
17959 indexOfPanel : function(p)
17962 Roo.each(this.tabs, function(t,i) {
17963 if (t.tabId == p.tabId) {
17972 * show a specific panel
17973 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17974 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17976 showPanel : function (pan)
17978 if(this.transition || typeof(pan) == 'undefined'){
17979 Roo.log("waiting for the transitionend");
17983 if (typeof(pan) == 'number') {
17984 pan = this.tabs[pan];
17987 if (typeof(pan) == 'string') {
17988 pan = this.getPanelByName(pan);
17991 var cur = this.getActivePanel();
17994 Roo.log('pan or acitve pan is undefined');
17998 if (pan.tabId == this.getActivePanel().tabId) {
18002 if (false === cur.fireEvent('beforedeactivate')) {
18006 if(this.bullets > 0 && !Roo.isTouch){
18007 this.setActiveBullet(this.indexOfPanel(pan));
18010 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18012 this.transition = true;
18013 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18014 var lr = dir == 'next' ? 'left' : 'right';
18015 pan.el.addClass(dir); // or prev
18016 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18017 cur.el.addClass(lr); // or right
18018 pan.el.addClass(lr);
18021 cur.el.on('transitionend', function() {
18022 Roo.log("trans end?");
18024 pan.el.removeClass([lr,dir]);
18025 pan.setActive(true);
18027 cur.el.removeClass([lr]);
18028 cur.setActive(false);
18030 _this.transition = false;
18032 }, this, { single: true } );
18037 cur.setActive(false);
18038 pan.setActive(true);
18043 showPanelNext : function()
18045 var i = this.indexOfPanel(this.getActivePanel());
18047 if (i >= this.tabs.length - 1 && !this.autoslide) {
18051 if (i >= this.tabs.length - 1 && this.autoslide) {
18055 this.showPanel(this.tabs[i+1]);
18058 showPanelPrev : function()
18060 var i = this.indexOfPanel(this.getActivePanel());
18062 if (i < 1 && !this.autoslide) {
18066 if (i < 1 && this.autoslide) {
18067 i = this.tabs.length;
18070 this.showPanel(this.tabs[i-1]);
18074 addBullet: function()
18076 if(!this.bullets || Roo.isTouch){
18079 var ctr = this.el.select('.carousel-bullets',true).first();
18080 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18081 var bullet = ctr.createChild({
18082 cls : 'bullet bullet-' + i
18083 },ctr.dom.lastChild);
18088 bullet.on('click', (function(e, el, o, ii, t){
18090 e.preventDefault();
18092 this.showPanel(ii);
18094 if(this.autoslide && this.slideFn){
18095 clearInterval(this.slideFn);
18096 this.slideFn = window.setInterval(function() {
18097 _this.showPanelNext();
18101 }).createDelegate(this, [i, bullet], true));
18106 setActiveBullet : function(i)
18112 Roo.each(this.el.select('.bullet', true).elements, function(el){
18113 el.removeClass('selected');
18116 var bullet = this.el.select('.bullet-' + i, true).first();
18122 bullet.addClass('selected');
18133 Roo.apply(Roo.bootstrap.TabGroup, {
18137 * register a Navigation Group
18138 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18140 register : function(navgrp)
18142 this.groups[navgrp.navId] = navgrp;
18146 * fetch a Navigation Group based on the navigation ID
18147 * if one does not exist , it will get created.
18148 * @param {string} the navgroup to add
18149 * @returns {Roo.bootstrap.NavGroup} the navgroup
18151 get: function(navId) {
18152 if (typeof(this.groups[navId]) == 'undefined') {
18153 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18155 return this.groups[navId] ;
18170 * @class Roo.bootstrap.TabPanel
18171 * @extends Roo.bootstrap.Component
18172 * Bootstrap TabPanel class
18173 * @cfg {Boolean} active panel active
18174 * @cfg {String} html panel content
18175 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18176 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18177 * @cfg {String} href click to link..
18181 * Create a new TabPanel
18182 * @param {Object} config The config object
18185 Roo.bootstrap.TabPanel = function(config){
18186 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18190 * Fires when the active status changes
18191 * @param {Roo.bootstrap.TabPanel} this
18192 * @param {Boolean} state the new state
18197 * @event beforedeactivate
18198 * Fires before a tab is de-activated - can be used to do validation on a form.
18199 * @param {Roo.bootstrap.TabPanel} this
18200 * @return {Boolean} false if there is an error
18203 'beforedeactivate': true
18206 this.tabId = this.tabId || Roo.id();
18210 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18218 getAutoCreate : function(){
18221 // item is needed for carousel - not sure if it has any effect otherwise
18222 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18223 html: this.html || ''
18227 cfg.cls += ' active';
18231 cfg.tabId = this.tabId;
18238 initEvents: function()
18240 var p = this.parent();
18242 this.navId = this.navId || p.navId;
18244 if (typeof(this.navId) != 'undefined') {
18245 // not really needed.. but just in case.. parent should be a NavGroup.
18246 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18250 var i = tg.tabs.length - 1;
18252 if(this.active && tg.bullets > 0 && i < tg.bullets){
18253 tg.setActiveBullet(i);
18257 this.el.on('click', this.onClick, this);
18260 this.el.on("touchstart", this.onTouchStart, this);
18261 this.el.on("touchmove", this.onTouchMove, this);
18262 this.el.on("touchend", this.onTouchEnd, this);
18267 onRender : function(ct, position)
18269 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18272 setActive : function(state)
18274 Roo.log("panel - set active " + this.tabId + "=" + state);
18276 this.active = state;
18278 this.el.removeClass('active');
18280 } else if (!this.el.hasClass('active')) {
18281 this.el.addClass('active');
18284 this.fireEvent('changed', this, state);
18287 onClick : function(e)
18289 e.preventDefault();
18291 if(!this.href.length){
18295 window.location.href = this.href;
18304 onTouchStart : function(e)
18306 this.swiping = false;
18308 this.startX = e.browserEvent.touches[0].clientX;
18309 this.startY = e.browserEvent.touches[0].clientY;
18312 onTouchMove : function(e)
18314 this.swiping = true;
18316 this.endX = e.browserEvent.touches[0].clientX;
18317 this.endY = e.browserEvent.touches[0].clientY;
18320 onTouchEnd : function(e)
18327 var tabGroup = this.parent();
18329 if(this.endX > this.startX){ // swiping right
18330 tabGroup.showPanelPrev();
18334 if(this.startX > this.endX){ // swiping left
18335 tabGroup.showPanelNext();
18354 * @class Roo.bootstrap.DateField
18355 * @extends Roo.bootstrap.Input
18356 * Bootstrap DateField class
18357 * @cfg {Number} weekStart default 0
18358 * @cfg {String} viewMode default empty, (months|years)
18359 * @cfg {String} minViewMode default empty, (months|years)
18360 * @cfg {Number} startDate default -Infinity
18361 * @cfg {Number} endDate default Infinity
18362 * @cfg {Boolean} todayHighlight default false
18363 * @cfg {Boolean} todayBtn default false
18364 * @cfg {Boolean} calendarWeeks default false
18365 * @cfg {Object} daysOfWeekDisabled default empty
18366 * @cfg {Boolean} singleMode default false (true | false)
18368 * @cfg {Boolean} keyboardNavigation default true
18369 * @cfg {String} language default en
18372 * Create a new DateField
18373 * @param {Object} config The config object
18376 Roo.bootstrap.DateField = function(config){
18377 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18381 * Fires when this field show.
18382 * @param {Roo.bootstrap.DateField} this
18383 * @param {Mixed} date The date value
18388 * Fires when this field hide.
18389 * @param {Roo.bootstrap.DateField} this
18390 * @param {Mixed} date The date value
18395 * Fires when select a date.
18396 * @param {Roo.bootstrap.DateField} this
18397 * @param {Mixed} date The date value
18401 * @event beforeselect
18402 * Fires when before select a date.
18403 * @param {Roo.bootstrap.DateField} this
18404 * @param {Mixed} date The date value
18406 beforeselect : true
18410 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18413 * @cfg {String} format
18414 * The default date format string which can be overriden for localization support. The format must be
18415 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18419 * @cfg {String} altFormats
18420 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18421 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18423 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18431 todayHighlight : false,
18437 keyboardNavigation: true,
18439 calendarWeeks: false,
18441 startDate: -Infinity,
18445 daysOfWeekDisabled: [],
18449 singleMode : false,
18451 UTCDate: function()
18453 return new Date(Date.UTC.apply(Date, arguments));
18456 UTCToday: function()
18458 var today = new Date();
18459 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18462 getDate: function() {
18463 var d = this.getUTCDate();
18464 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18467 getUTCDate: function() {
18471 setDate: function(d) {
18472 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18475 setUTCDate: function(d) {
18477 this.setValue(this.formatDate(this.date));
18480 onRender: function(ct, position)
18483 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18485 this.language = this.language || 'en';
18486 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18487 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18489 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18490 this.format = this.format || 'm/d/y';
18491 this.isInline = false;
18492 this.isInput = true;
18493 this.component = this.el.select('.add-on', true).first() || false;
18494 this.component = (this.component && this.component.length === 0) ? false : this.component;
18495 this.hasInput = this.component && this.inputEl().length;
18497 if (typeof(this.minViewMode === 'string')) {
18498 switch (this.minViewMode) {
18500 this.minViewMode = 1;
18503 this.minViewMode = 2;
18506 this.minViewMode = 0;
18511 if (typeof(this.viewMode === 'string')) {
18512 switch (this.viewMode) {
18525 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18527 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18529 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18531 this.picker().on('mousedown', this.onMousedown, this);
18532 this.picker().on('click', this.onClick, this);
18534 this.picker().addClass('datepicker-dropdown');
18536 this.startViewMode = this.viewMode;
18538 if(this.singleMode){
18539 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18540 v.setVisibilityMode(Roo.Element.DISPLAY);
18544 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18545 v.setStyle('width', '189px');
18549 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18550 if(!this.calendarWeeks){
18555 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18556 v.attr('colspan', function(i, val){
18557 return parseInt(val) + 1;
18562 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18564 this.setStartDate(this.startDate);
18565 this.setEndDate(this.endDate);
18567 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18574 if(this.isInline) {
18579 picker : function()
18581 return this.pickerEl;
18582 // return this.el.select('.datepicker', true).first();
18585 fillDow: function()
18587 var dowCnt = this.weekStart;
18596 if(this.calendarWeeks){
18604 while (dowCnt < this.weekStart + 7) {
18608 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18612 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18615 fillMonths: function()
18618 var months = this.picker().select('>.datepicker-months td', true).first();
18620 months.dom.innerHTML = '';
18626 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18629 months.createChild(month);
18636 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;
18638 if (this.date < this.startDate) {
18639 this.viewDate = new Date(this.startDate);
18640 } else if (this.date > this.endDate) {
18641 this.viewDate = new Date(this.endDate);
18643 this.viewDate = new Date(this.date);
18651 var d = new Date(this.viewDate),
18652 year = d.getUTCFullYear(),
18653 month = d.getUTCMonth(),
18654 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18655 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18656 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18657 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18658 currentDate = this.date && this.date.valueOf(),
18659 today = this.UTCToday();
18661 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18663 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18665 // this.picker.select('>tfoot th.today').
18666 // .text(dates[this.language].today)
18667 // .toggle(this.todayBtn !== false);
18669 this.updateNavArrows();
18672 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18674 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18676 prevMonth.setUTCDate(day);
18678 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18680 var nextMonth = new Date(prevMonth);
18682 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18684 nextMonth = nextMonth.valueOf();
18686 var fillMonths = false;
18688 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18690 while(prevMonth.valueOf() <= nextMonth) {
18693 if (prevMonth.getUTCDay() === this.weekStart) {
18695 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18703 if(this.calendarWeeks){
18704 // ISO 8601: First week contains first thursday.
18705 // ISO also states week starts on Monday, but we can be more abstract here.
18707 // Start of current week: based on weekstart/current date
18708 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18709 // Thursday of this week
18710 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18711 // First Thursday of year, year from thursday
18712 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18713 // Calendar week: ms between thursdays, div ms per day, div 7 days
18714 calWeek = (th - yth) / 864e5 / 7 + 1;
18716 fillMonths.cn.push({
18724 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18726 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18729 if (this.todayHighlight &&
18730 prevMonth.getUTCFullYear() == today.getFullYear() &&
18731 prevMonth.getUTCMonth() == today.getMonth() &&
18732 prevMonth.getUTCDate() == today.getDate()) {
18733 clsName += ' today';
18736 if (currentDate && prevMonth.valueOf() === currentDate) {
18737 clsName += ' active';
18740 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18741 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18742 clsName += ' disabled';
18745 fillMonths.cn.push({
18747 cls: 'day ' + clsName,
18748 html: prevMonth.getDate()
18751 prevMonth.setDate(prevMonth.getDate()+1);
18754 var currentYear = this.date && this.date.getUTCFullYear();
18755 var currentMonth = this.date && this.date.getUTCMonth();
18757 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18759 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18760 v.removeClass('active');
18762 if(currentYear === year && k === currentMonth){
18763 v.addClass('active');
18766 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18767 v.addClass('disabled');
18773 year = parseInt(year/10, 10) * 10;
18775 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18777 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18780 for (var i = -1; i < 11; i++) {
18781 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18783 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18791 showMode: function(dir)
18794 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18797 Roo.each(this.picker().select('>div',true).elements, function(v){
18798 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18801 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18806 if(this.isInline) {
18810 this.picker().removeClass(['bottom', 'top']);
18812 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18814 * place to the top of element!
18818 this.picker().addClass('top');
18819 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18824 this.picker().addClass('bottom');
18826 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18829 parseDate : function(value)
18831 if(!value || value instanceof Date){
18834 var v = Date.parseDate(value, this.format);
18835 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18836 v = Date.parseDate(value, 'Y-m-d');
18838 if(!v && this.altFormats){
18839 if(!this.altFormatsArray){
18840 this.altFormatsArray = this.altFormats.split("|");
18842 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18843 v = Date.parseDate(value, this.altFormatsArray[i]);
18849 formatDate : function(date, fmt)
18851 return (!date || !(date instanceof Date)) ?
18852 date : date.dateFormat(fmt || this.format);
18855 onFocus : function()
18857 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18861 onBlur : function()
18863 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18865 var d = this.inputEl().getValue();
18872 showPopup : function()
18874 this.picker().show();
18878 this.fireEvent('showpopup', this, this.date);
18881 hidePopup : function()
18883 if(this.isInline) {
18886 this.picker().hide();
18887 this.viewMode = this.startViewMode;
18890 this.fireEvent('hidepopup', this, this.date);
18894 onMousedown: function(e)
18896 e.stopPropagation();
18897 e.preventDefault();
18902 Roo.bootstrap.DateField.superclass.keyup.call(this);
18906 setValue: function(v)
18908 if(this.fireEvent('beforeselect', this, v) !== false){
18909 var d = new Date(this.parseDate(v) ).clearTime();
18911 if(isNaN(d.getTime())){
18912 this.date = this.viewDate = '';
18913 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18917 v = this.formatDate(d);
18919 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18921 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18925 this.fireEvent('select', this, this.date);
18929 getValue: function()
18931 return this.formatDate(this.date);
18934 fireKey: function(e)
18936 if (!this.picker().isVisible()){
18937 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18943 var dateChanged = false,
18945 newDate, newViewDate;
18950 e.preventDefault();
18954 if (!this.keyboardNavigation) {
18957 dir = e.keyCode == 37 ? -1 : 1;
18960 newDate = this.moveYear(this.date, dir);
18961 newViewDate = this.moveYear(this.viewDate, dir);
18962 } else if (e.shiftKey){
18963 newDate = this.moveMonth(this.date, dir);
18964 newViewDate = this.moveMonth(this.viewDate, dir);
18966 newDate = new Date(this.date);
18967 newDate.setUTCDate(this.date.getUTCDate() + dir);
18968 newViewDate = new Date(this.viewDate);
18969 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18971 if (this.dateWithinRange(newDate)){
18972 this.date = newDate;
18973 this.viewDate = newViewDate;
18974 this.setValue(this.formatDate(this.date));
18976 e.preventDefault();
18977 dateChanged = true;
18982 if (!this.keyboardNavigation) {
18985 dir = e.keyCode == 38 ? -1 : 1;
18987 newDate = this.moveYear(this.date, dir);
18988 newViewDate = this.moveYear(this.viewDate, dir);
18989 } else if (e.shiftKey){
18990 newDate = this.moveMonth(this.date, dir);
18991 newViewDate = this.moveMonth(this.viewDate, dir);
18993 newDate = new Date(this.date);
18994 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18995 newViewDate = new Date(this.viewDate);
18996 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18998 if (this.dateWithinRange(newDate)){
18999 this.date = newDate;
19000 this.viewDate = newViewDate;
19001 this.setValue(this.formatDate(this.date));
19003 e.preventDefault();
19004 dateChanged = true;
19008 this.setValue(this.formatDate(this.date));
19010 e.preventDefault();
19013 this.setValue(this.formatDate(this.date));
19027 onClick: function(e)
19029 e.stopPropagation();
19030 e.preventDefault();
19032 var target = e.getTarget();
19034 if(target.nodeName.toLowerCase() === 'i'){
19035 target = Roo.get(target).dom.parentNode;
19038 var nodeName = target.nodeName;
19039 var className = target.className;
19040 var html = target.innerHTML;
19041 //Roo.log(nodeName);
19043 switch(nodeName.toLowerCase()) {
19045 switch(className) {
19051 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19052 switch(this.viewMode){
19054 this.viewDate = this.moveMonth(this.viewDate, dir);
19058 this.viewDate = this.moveYear(this.viewDate, dir);
19064 var date = new Date();
19065 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19067 this.setValue(this.formatDate(this.date));
19074 if (className.indexOf('disabled') < 0) {
19075 this.viewDate.setUTCDate(1);
19076 if (className.indexOf('month') > -1) {
19077 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19079 var year = parseInt(html, 10) || 0;
19080 this.viewDate.setUTCFullYear(year);
19084 if(this.singleMode){
19085 this.setValue(this.formatDate(this.viewDate));
19096 //Roo.log(className);
19097 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19098 var day = parseInt(html, 10) || 1;
19099 var year = this.viewDate.getUTCFullYear(),
19100 month = this.viewDate.getUTCMonth();
19102 if (className.indexOf('old') > -1) {
19109 } else if (className.indexOf('new') > -1) {
19117 //Roo.log([year,month,day]);
19118 this.date = this.UTCDate(year, month, day,0,0,0,0);
19119 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19121 //Roo.log(this.formatDate(this.date));
19122 this.setValue(this.formatDate(this.date));
19129 setStartDate: function(startDate)
19131 this.startDate = startDate || -Infinity;
19132 if (this.startDate !== -Infinity) {
19133 this.startDate = this.parseDate(this.startDate);
19136 this.updateNavArrows();
19139 setEndDate: function(endDate)
19141 this.endDate = endDate || Infinity;
19142 if (this.endDate !== Infinity) {
19143 this.endDate = this.parseDate(this.endDate);
19146 this.updateNavArrows();
19149 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19151 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19152 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19153 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19155 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19156 return parseInt(d, 10);
19159 this.updateNavArrows();
19162 updateNavArrows: function()
19164 if(this.singleMode){
19168 var d = new Date(this.viewDate),
19169 year = d.getUTCFullYear(),
19170 month = d.getUTCMonth();
19172 Roo.each(this.picker().select('.prev', true).elements, function(v){
19174 switch (this.viewMode) {
19177 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19183 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19190 Roo.each(this.picker().select('.next', true).elements, function(v){
19192 switch (this.viewMode) {
19195 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19201 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19209 moveMonth: function(date, dir)
19214 var new_date = new Date(date.valueOf()),
19215 day = new_date.getUTCDate(),
19216 month = new_date.getUTCMonth(),
19217 mag = Math.abs(dir),
19219 dir = dir > 0 ? 1 : -1;
19222 // If going back one month, make sure month is not current month
19223 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19225 return new_date.getUTCMonth() == month;
19227 // If going forward one month, make sure month is as expected
19228 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19230 return new_date.getUTCMonth() != new_month;
19232 new_month = month + dir;
19233 new_date.setUTCMonth(new_month);
19234 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19235 if (new_month < 0 || new_month > 11) {
19236 new_month = (new_month + 12) % 12;
19239 // For magnitudes >1, move one month at a time...
19240 for (var i=0; i<mag; i++) {
19241 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19242 new_date = this.moveMonth(new_date, dir);
19244 // ...then reset the day, keeping it in the new month
19245 new_month = new_date.getUTCMonth();
19246 new_date.setUTCDate(day);
19248 return new_month != new_date.getUTCMonth();
19251 // Common date-resetting loop -- if date is beyond end of month, make it
19254 new_date.setUTCDate(--day);
19255 new_date.setUTCMonth(new_month);
19260 moveYear: function(date, dir)
19262 return this.moveMonth(date, dir*12);
19265 dateWithinRange: function(date)
19267 return date >= this.startDate && date <= this.endDate;
19273 this.picker().remove();
19276 validateValue : function(value)
19278 if(this.getVisibilityEl().hasClass('hidden')){
19282 if(value.length < 1) {
19283 if(this.allowBlank){
19289 if(value.length < this.minLength){
19292 if(value.length > this.maxLength){
19296 var vt = Roo.form.VTypes;
19297 if(!vt[this.vtype](value, this)){
19301 if(typeof this.validator == "function"){
19302 var msg = this.validator(value);
19308 if(this.regex && !this.regex.test(value)){
19312 if(typeof(this.parseDate(value)) == 'undefined'){
19316 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19320 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19330 this.date = this.viewDate = '';
19332 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19337 Roo.apply(Roo.bootstrap.DateField, {
19348 html: '<i class="fa fa-arrow-left"/>'
19358 html: '<i class="fa fa-arrow-right"/>'
19400 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19401 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19402 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19403 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19404 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19417 navFnc: 'FullYear',
19422 navFnc: 'FullYear',
19427 Roo.apply(Roo.bootstrap.DateField, {
19431 cls: 'datepicker dropdown-menu roo-dynamic',
19435 cls: 'datepicker-days',
19439 cls: 'table-condensed',
19441 Roo.bootstrap.DateField.head,
19445 Roo.bootstrap.DateField.footer
19452 cls: 'datepicker-months',
19456 cls: 'table-condensed',
19458 Roo.bootstrap.DateField.head,
19459 Roo.bootstrap.DateField.content,
19460 Roo.bootstrap.DateField.footer
19467 cls: 'datepicker-years',
19471 cls: 'table-condensed',
19473 Roo.bootstrap.DateField.head,
19474 Roo.bootstrap.DateField.content,
19475 Roo.bootstrap.DateField.footer
19494 * @class Roo.bootstrap.TimeField
19495 * @extends Roo.bootstrap.Input
19496 * Bootstrap DateField class
19500 * Create a new TimeField
19501 * @param {Object} config The config object
19504 Roo.bootstrap.TimeField = function(config){
19505 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19509 * Fires when this field show.
19510 * @param {Roo.bootstrap.DateField} thisthis
19511 * @param {Mixed} date The date value
19516 * Fires when this field hide.
19517 * @param {Roo.bootstrap.DateField} this
19518 * @param {Mixed} date The date value
19523 * Fires when select a date.
19524 * @param {Roo.bootstrap.DateField} this
19525 * @param {Mixed} date The date value
19531 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19534 * @cfg {String} format
19535 * The default time format string which can be overriden for localization support. The format must be
19536 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19540 onRender: function(ct, position)
19543 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19545 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19547 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19549 this.pop = this.picker().select('>.datepicker-time',true).first();
19550 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19552 this.picker().on('mousedown', this.onMousedown, this);
19553 this.picker().on('click', this.onClick, this);
19555 this.picker().addClass('datepicker-dropdown');
19560 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19561 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19562 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19563 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19564 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19565 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19569 fireKey: function(e){
19570 if (!this.picker().isVisible()){
19571 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19577 e.preventDefault();
19585 this.onTogglePeriod();
19588 this.onIncrementMinutes();
19591 this.onDecrementMinutes();
19600 onClick: function(e) {
19601 e.stopPropagation();
19602 e.preventDefault();
19605 picker : function()
19607 return this.el.select('.datepicker', true).first();
19610 fillTime: function()
19612 var time = this.pop.select('tbody', true).first();
19614 time.dom.innerHTML = '';
19629 cls: 'hours-up glyphicon glyphicon-chevron-up'
19649 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19670 cls: 'timepicker-hour',
19685 cls: 'timepicker-minute',
19700 cls: 'btn btn-primary period',
19722 cls: 'hours-down glyphicon glyphicon-chevron-down'
19742 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19760 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19767 var hours = this.time.getHours();
19768 var minutes = this.time.getMinutes();
19781 hours = hours - 12;
19785 hours = '0' + hours;
19789 minutes = '0' + minutes;
19792 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19793 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19794 this.pop.select('button', true).first().dom.innerHTML = period;
19800 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19802 var cls = ['bottom'];
19804 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19811 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19816 this.picker().addClass(cls.join('-'));
19820 Roo.each(cls, function(c){
19822 _this.picker().setTop(_this.inputEl().getHeight());
19826 _this.picker().setTop(0 - _this.picker().getHeight());
19831 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19835 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19842 onFocus : function()
19844 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19848 onBlur : function()
19850 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19856 this.picker().show();
19861 this.fireEvent('show', this, this.date);
19866 this.picker().hide();
19869 this.fireEvent('hide', this, this.date);
19872 setTime : function()
19875 this.setValue(this.time.format(this.format));
19877 this.fireEvent('select', this, this.date);
19882 onMousedown: function(e){
19883 e.stopPropagation();
19884 e.preventDefault();
19887 onIncrementHours: function()
19889 Roo.log('onIncrementHours');
19890 this.time = this.time.add(Date.HOUR, 1);
19895 onDecrementHours: function()
19897 Roo.log('onDecrementHours');
19898 this.time = this.time.add(Date.HOUR, -1);
19902 onIncrementMinutes: function()
19904 Roo.log('onIncrementMinutes');
19905 this.time = this.time.add(Date.MINUTE, 1);
19909 onDecrementMinutes: function()
19911 Roo.log('onDecrementMinutes');
19912 this.time = this.time.add(Date.MINUTE, -1);
19916 onTogglePeriod: function()
19918 Roo.log('onTogglePeriod');
19919 this.time = this.time.add(Date.HOUR, 12);
19926 Roo.apply(Roo.bootstrap.TimeField, {
19956 cls: 'btn btn-info ok',
19968 Roo.apply(Roo.bootstrap.TimeField, {
19972 cls: 'datepicker dropdown-menu',
19976 cls: 'datepicker-time',
19980 cls: 'table-condensed',
19982 Roo.bootstrap.TimeField.content,
19983 Roo.bootstrap.TimeField.footer
20002 * @class Roo.bootstrap.MonthField
20003 * @extends Roo.bootstrap.Input
20004 * Bootstrap MonthField class
20006 * @cfg {String} language default en
20009 * Create a new MonthField
20010 * @param {Object} config The config object
20013 Roo.bootstrap.MonthField = function(config){
20014 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20019 * Fires when this field show.
20020 * @param {Roo.bootstrap.MonthField} this
20021 * @param {Mixed} date The date value
20026 * Fires when this field hide.
20027 * @param {Roo.bootstrap.MonthField} this
20028 * @param {Mixed} date The date value
20033 * Fires when select a date.
20034 * @param {Roo.bootstrap.MonthField} this
20035 * @param {String} oldvalue The old value
20036 * @param {String} newvalue The new value
20042 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20044 onRender: function(ct, position)
20047 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20049 this.language = this.language || 'en';
20050 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20051 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20053 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20054 this.isInline = false;
20055 this.isInput = true;
20056 this.component = this.el.select('.add-on', true).first() || false;
20057 this.component = (this.component && this.component.length === 0) ? false : this.component;
20058 this.hasInput = this.component && this.inputEL().length;
20060 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20062 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20064 this.picker().on('mousedown', this.onMousedown, this);
20065 this.picker().on('click', this.onClick, this);
20067 this.picker().addClass('datepicker-dropdown');
20069 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20070 v.setStyle('width', '189px');
20077 if(this.isInline) {
20083 setValue: function(v, suppressEvent)
20085 var o = this.getValue();
20087 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20091 if(suppressEvent !== true){
20092 this.fireEvent('select', this, o, v);
20097 getValue: function()
20102 onClick: function(e)
20104 e.stopPropagation();
20105 e.preventDefault();
20107 var target = e.getTarget();
20109 if(target.nodeName.toLowerCase() === 'i'){
20110 target = Roo.get(target).dom.parentNode;
20113 var nodeName = target.nodeName;
20114 var className = target.className;
20115 var html = target.innerHTML;
20117 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20121 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20123 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20129 picker : function()
20131 return this.pickerEl;
20134 fillMonths: function()
20137 var months = this.picker().select('>.datepicker-months td', true).first();
20139 months.dom.innerHTML = '';
20145 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20148 months.createChild(month);
20157 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20158 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20161 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20162 e.removeClass('active');
20164 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20165 e.addClass('active');
20172 if(this.isInline) {
20176 this.picker().removeClass(['bottom', 'top']);
20178 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20180 * place to the top of element!
20184 this.picker().addClass('top');
20185 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20190 this.picker().addClass('bottom');
20192 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20195 onFocus : function()
20197 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20201 onBlur : function()
20203 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20205 var d = this.inputEl().getValue();
20214 this.picker().show();
20215 this.picker().select('>.datepicker-months', true).first().show();
20219 this.fireEvent('show', this, this.date);
20224 if(this.isInline) {
20227 this.picker().hide();
20228 this.fireEvent('hide', this, this.date);
20232 onMousedown: function(e)
20234 e.stopPropagation();
20235 e.preventDefault();
20240 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20244 fireKey: function(e)
20246 if (!this.picker().isVisible()){
20247 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20258 e.preventDefault();
20262 dir = e.keyCode == 37 ? -1 : 1;
20264 this.vIndex = this.vIndex + dir;
20266 if(this.vIndex < 0){
20270 if(this.vIndex > 11){
20274 if(isNaN(this.vIndex)){
20278 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20284 dir = e.keyCode == 38 ? -1 : 1;
20286 this.vIndex = this.vIndex + dir * 4;
20288 if(this.vIndex < 0){
20292 if(this.vIndex > 11){
20296 if(isNaN(this.vIndex)){
20300 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20305 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20306 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20310 e.preventDefault();
20313 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20314 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20330 this.picker().remove();
20335 Roo.apply(Roo.bootstrap.MonthField, {
20354 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20355 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20360 Roo.apply(Roo.bootstrap.MonthField, {
20364 cls: 'datepicker dropdown-menu roo-dynamic',
20368 cls: 'datepicker-months',
20372 cls: 'table-condensed',
20374 Roo.bootstrap.DateField.content
20394 * @class Roo.bootstrap.CheckBox
20395 * @extends Roo.bootstrap.Input
20396 * Bootstrap CheckBox class
20398 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20399 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20400 * @cfg {String} boxLabel The text that appears beside the checkbox
20401 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20402 * @cfg {Boolean} checked initnal the element
20403 * @cfg {Boolean} inline inline the element (default false)
20404 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20405 * @cfg {String} tooltip label tooltip
20408 * Create a new CheckBox
20409 * @param {Object} config The config object
20412 Roo.bootstrap.CheckBox = function(config){
20413 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20418 * Fires when the element is checked or unchecked.
20419 * @param {Roo.bootstrap.CheckBox} this This input
20420 * @param {Boolean} checked The new checked value
20425 * Fires when the element is click.
20426 * @param {Roo.bootstrap.CheckBox} this This input
20433 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20435 inputType: 'checkbox',
20444 getAutoCreate : function()
20446 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20452 cfg.cls = 'form-group ' + this.inputType; //input-group
20455 cfg.cls += ' ' + this.inputType + '-inline';
20461 type : this.inputType,
20462 value : this.inputValue,
20463 cls : 'roo-' + this.inputType, //'form-box',
20464 placeholder : this.placeholder || ''
20468 if(this.inputType != 'radio'){
20472 cls : 'roo-hidden-value',
20473 value : this.checked ? this.inputValue : this.valueOff
20478 if (this.weight) { // Validity check?
20479 cfg.cls += " " + this.inputType + "-" + this.weight;
20482 if (this.disabled) {
20483 input.disabled=true;
20487 input.checked = this.checked;
20492 input.name = this.name;
20494 if(this.inputType != 'radio'){
20495 hidden.name = this.name;
20496 input.name = '_hidden_' + this.name;
20501 input.cls += ' input-' + this.size;
20506 ['xs','sm','md','lg'].map(function(size){
20507 if (settings[size]) {
20508 cfg.cls += ' col-' + size + '-' + settings[size];
20512 var inputblock = input;
20514 if (this.before || this.after) {
20517 cls : 'input-group',
20522 inputblock.cn.push({
20524 cls : 'input-group-addon',
20529 inputblock.cn.push(input);
20531 if(this.inputType != 'radio'){
20532 inputblock.cn.push(hidden);
20536 inputblock.cn.push({
20538 cls : 'input-group-addon',
20545 if (align ==='left' && this.fieldLabel.length) {
20546 // Roo.log("left and has label");
20551 cls : 'control-label',
20552 html : this.fieldLabel
20562 if(this.labelWidth > 12){
20563 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20566 if(this.labelWidth < 13 && this.labelmd == 0){
20567 this.labelmd = this.labelWidth;
20570 if(this.labellg > 0){
20571 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20572 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20575 if(this.labelmd > 0){
20576 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20577 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20580 if(this.labelsm > 0){
20581 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20582 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20585 if(this.labelxs > 0){
20586 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20587 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20590 } else if ( this.fieldLabel.length) {
20591 // Roo.log(" label");
20595 tag: this.boxLabel ? 'span' : 'label',
20597 cls: 'control-label box-input-label',
20598 //cls : 'input-group-addon',
20599 html : this.fieldLabel
20608 // Roo.log(" no label && no align");
20609 cfg.cn = [ inputblock ] ;
20615 var boxLabelCfg = {
20617 //'for': id, // box label is handled by onclick - so no for...
20619 html: this.boxLabel
20623 boxLabelCfg.tooltip = this.tooltip;
20626 cfg.cn.push(boxLabelCfg);
20629 if(this.inputType != 'radio'){
20630 cfg.cn.push(hidden);
20638 * return the real input element.
20640 inputEl: function ()
20642 return this.el.select('input.roo-' + this.inputType,true).first();
20644 hiddenEl: function ()
20646 return this.el.select('input.roo-hidden-value',true).first();
20649 labelEl: function()
20651 return this.el.select('label.control-label',true).first();
20653 /* depricated... */
20657 return this.labelEl();
20660 boxLabelEl: function()
20662 return this.el.select('label.box-label',true).first();
20665 initEvents : function()
20667 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20669 this.inputEl().on('click', this.onClick, this);
20671 if (this.boxLabel) {
20672 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20675 this.startValue = this.getValue();
20678 Roo.bootstrap.CheckBox.register(this);
20682 onClick : function(e)
20684 if(this.fireEvent('click', this, e) !== false){
20685 this.setChecked(!this.checked);
20690 setChecked : function(state,suppressEvent)
20692 this.startValue = this.getValue();
20694 if(this.inputType == 'radio'){
20696 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20697 e.dom.checked = false;
20700 this.inputEl().dom.checked = true;
20702 this.inputEl().dom.value = this.inputValue;
20704 if(suppressEvent !== true){
20705 this.fireEvent('check', this, true);
20713 this.checked = state;
20715 this.inputEl().dom.checked = state;
20718 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20720 if(suppressEvent !== true){
20721 this.fireEvent('check', this, state);
20727 getValue : function()
20729 if(this.inputType == 'radio'){
20730 return this.getGroupValue();
20733 return this.hiddenEl().dom.value;
20737 getGroupValue : function()
20739 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20743 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20746 setValue : function(v,suppressEvent)
20748 if(this.inputType == 'radio'){
20749 this.setGroupValue(v, suppressEvent);
20753 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20758 setGroupValue : function(v, suppressEvent)
20760 this.startValue = this.getValue();
20762 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20763 e.dom.checked = false;
20765 if(e.dom.value == v){
20766 e.dom.checked = true;
20770 if(suppressEvent !== true){
20771 this.fireEvent('check', this, true);
20779 validate : function()
20781 if(this.getVisibilityEl().hasClass('hidden')){
20787 (this.inputType == 'radio' && this.validateRadio()) ||
20788 (this.inputType == 'checkbox' && this.validateCheckbox())
20794 this.markInvalid();
20798 validateRadio : function()
20800 if(this.getVisibilityEl().hasClass('hidden')){
20804 if(this.allowBlank){
20810 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20811 if(!e.dom.checked){
20823 validateCheckbox : function()
20826 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20827 //return (this.getValue() == this.inputValue) ? true : false;
20830 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20838 for(var i in group){
20839 if(group[i].el.isVisible(true)){
20847 for(var i in group){
20852 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20859 * Mark this field as valid
20861 markValid : function()
20865 this.fireEvent('valid', this);
20867 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20870 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20877 if(this.inputType == 'radio'){
20878 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20879 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20880 e.findParent('.form-group', false, true).addClass(_this.validClass);
20887 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20888 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20892 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20898 for(var i in group){
20899 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20900 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20905 * Mark this field as invalid
20906 * @param {String} msg The validation message
20908 markInvalid : function(msg)
20910 if(this.allowBlank){
20916 this.fireEvent('invalid', this, msg);
20918 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20921 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20925 label.markInvalid();
20928 if(this.inputType == 'radio'){
20929 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20930 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20931 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20938 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20939 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20943 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20949 for(var i in group){
20950 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20951 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20956 clearInvalid : function()
20958 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20960 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20962 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20964 if (label && label.iconEl) {
20965 label.iconEl.removeClass(label.validClass);
20966 label.iconEl.removeClass(label.invalidClass);
20970 disable : function()
20972 if(this.inputType != 'radio'){
20973 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20980 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20981 _this.getActionEl().addClass(this.disabledClass);
20982 e.dom.disabled = true;
20986 this.disabled = true;
20987 this.fireEvent("disable", this);
20991 enable : function()
20993 if(this.inputType != 'radio'){
20994 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21001 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21002 _this.getActionEl().removeClass(this.disabledClass);
21003 e.dom.disabled = false;
21007 this.disabled = false;
21008 this.fireEvent("enable", this);
21012 setBoxLabel : function(v)
21017 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21023 Roo.apply(Roo.bootstrap.CheckBox, {
21028 * register a CheckBox Group
21029 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21031 register : function(checkbox)
21033 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21034 this.groups[checkbox.groupId] = {};
21037 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21041 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21045 * fetch a CheckBox Group based on the group ID
21046 * @param {string} the group ID
21047 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21049 get: function(groupId) {
21050 if (typeof(this.groups[groupId]) == 'undefined') {
21054 return this.groups[groupId] ;
21067 * @class Roo.bootstrap.Radio
21068 * @extends Roo.bootstrap.Component
21069 * Bootstrap Radio class
21070 * @cfg {String} boxLabel - the label associated
21071 * @cfg {String} value - the value of radio
21074 * Create a new Radio
21075 * @param {Object} config The config object
21077 Roo.bootstrap.Radio = function(config){
21078 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21082 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21088 getAutoCreate : function()
21092 cls : 'form-group radio',
21097 html : this.boxLabel
21105 initEvents : function()
21107 this.parent().register(this);
21109 this.el.on('click', this.onClick, this);
21113 onClick : function(e)
21115 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21116 this.setChecked(true);
21120 setChecked : function(state, suppressEvent)
21122 this.parent().setValue(this.value, suppressEvent);
21126 setBoxLabel : function(v)
21131 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21146 * @class Roo.bootstrap.SecurePass
21147 * @extends Roo.bootstrap.Input
21148 * Bootstrap SecurePass class
21152 * Create a new SecurePass
21153 * @param {Object} config The config object
21156 Roo.bootstrap.SecurePass = function (config) {
21157 // these go here, so the translation tool can replace them..
21159 PwdEmpty: "Please type a password, and then retype it to confirm.",
21160 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21161 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21162 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21163 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21164 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21165 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21166 TooWeak: "Your password is Too Weak."
21168 this.meterLabel = "Password strength:";
21169 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21170 this.meterClass = [
21171 "roo-password-meter-tooweak",
21172 "roo-password-meter-weak",
21173 "roo-password-meter-medium",
21174 "roo-password-meter-strong",
21175 "roo-password-meter-grey"
21180 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21183 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21185 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21187 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21188 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21189 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21190 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21191 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21192 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21193 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21203 * @cfg {String/Object} Label for the strength meter (defaults to
21204 * 'Password strength:')
21209 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21210 * ['Weak', 'Medium', 'Strong'])
21213 pwdStrengths: false,
21226 initEvents: function ()
21228 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21230 if (this.el.is('input[type=password]') && Roo.isSafari) {
21231 this.el.on('keydown', this.SafariOnKeyDown, this);
21234 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21237 onRender: function (ct, position)
21239 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21240 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21241 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21243 this.trigger.createChild({
21248 cls: 'roo-password-meter-grey col-xs-12',
21251 //width: this.meterWidth + 'px'
21255 cls: 'roo-password-meter-text'
21261 if (this.hideTrigger) {
21262 this.trigger.setDisplayed(false);
21264 this.setSize(this.width || '', this.height || '');
21267 onDestroy: function ()
21269 if (this.trigger) {
21270 this.trigger.removeAllListeners();
21271 this.trigger.remove();
21274 this.wrap.remove();
21276 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21279 checkStrength: function ()
21281 var pwd = this.inputEl().getValue();
21282 if (pwd == this._lastPwd) {
21287 if (this.ClientSideStrongPassword(pwd)) {
21289 } else if (this.ClientSideMediumPassword(pwd)) {
21291 } else if (this.ClientSideWeakPassword(pwd)) {
21297 Roo.log('strength1: ' + strength);
21299 //var pm = this.trigger.child('div/div/div').dom;
21300 var pm = this.trigger.child('div/div');
21301 pm.removeClass(this.meterClass);
21302 pm.addClass(this.meterClass[strength]);
21305 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21307 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21309 this._lastPwd = pwd;
21313 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21315 this._lastPwd = '';
21317 var pm = this.trigger.child('div/div');
21318 pm.removeClass(this.meterClass);
21319 pm.addClass('roo-password-meter-grey');
21322 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21325 this.inputEl().dom.type='password';
21328 validateValue: function (value)
21331 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21334 if (value.length == 0) {
21335 if (this.allowBlank) {
21336 this.clearInvalid();
21340 this.markInvalid(this.errors.PwdEmpty);
21341 this.errorMsg = this.errors.PwdEmpty;
21349 if ('[\x21-\x7e]*'.match(value)) {
21350 this.markInvalid(this.errors.PwdBadChar);
21351 this.errorMsg = this.errors.PwdBadChar;
21354 if (value.length < 6) {
21355 this.markInvalid(this.errors.PwdShort);
21356 this.errorMsg = this.errors.PwdShort;
21359 if (value.length > 16) {
21360 this.markInvalid(this.errors.PwdLong);
21361 this.errorMsg = this.errors.PwdLong;
21365 if (this.ClientSideStrongPassword(value)) {
21367 } else if (this.ClientSideMediumPassword(value)) {
21369 } else if (this.ClientSideWeakPassword(value)) {
21376 if (strength < 2) {
21377 //this.markInvalid(this.errors.TooWeak);
21378 this.errorMsg = this.errors.TooWeak;
21383 console.log('strength2: ' + strength);
21385 //var pm = this.trigger.child('div/div/div').dom;
21387 var pm = this.trigger.child('div/div');
21388 pm.removeClass(this.meterClass);
21389 pm.addClass(this.meterClass[strength]);
21391 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21393 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21395 this.errorMsg = '';
21399 CharacterSetChecks: function (type)
21402 this.fResult = false;
21405 isctype: function (character, type)
21408 case this.kCapitalLetter:
21409 if (character >= 'A' && character <= 'Z') {
21414 case this.kSmallLetter:
21415 if (character >= 'a' && character <= 'z') {
21421 if (character >= '0' && character <= '9') {
21426 case this.kPunctuation:
21427 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21438 IsLongEnough: function (pwd, size)
21440 return !(pwd == null || isNaN(size) || pwd.length < size);
21443 SpansEnoughCharacterSets: function (word, nb)
21445 if (!this.IsLongEnough(word, nb))
21450 var characterSetChecks = new Array(
21451 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21452 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21455 for (var index = 0; index < word.length; ++index) {
21456 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21457 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21458 characterSetChecks[nCharSet].fResult = true;
21465 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21466 if (characterSetChecks[nCharSet].fResult) {
21471 if (nCharSets < nb) {
21477 ClientSideStrongPassword: function (pwd)
21479 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21482 ClientSideMediumPassword: function (pwd)
21484 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21487 ClientSideWeakPassword: function (pwd)
21489 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21492 })//<script type="text/javascript">
21495 * Based Ext JS Library 1.1.1
21496 * Copyright(c) 2006-2007, Ext JS, LLC.
21502 * @class Roo.HtmlEditorCore
21503 * @extends Roo.Component
21504 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21506 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21509 Roo.HtmlEditorCore = function(config){
21512 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21517 * @event initialize
21518 * Fires when the editor is fully initialized (including the iframe)
21519 * @param {Roo.HtmlEditorCore} this
21524 * Fires when the editor is first receives the focus. Any insertion must wait
21525 * until after this event.
21526 * @param {Roo.HtmlEditorCore} this
21530 * @event beforesync
21531 * Fires before the textarea is updated with content from the editor iframe. Return false
21532 * to cancel the sync.
21533 * @param {Roo.HtmlEditorCore} this
21534 * @param {String} html
21538 * @event beforepush
21539 * Fires before the iframe editor is updated with content from the textarea. Return false
21540 * to cancel the push.
21541 * @param {Roo.HtmlEditorCore} this
21542 * @param {String} html
21547 * Fires when the textarea is updated with content from the editor iframe.
21548 * @param {Roo.HtmlEditorCore} this
21549 * @param {String} html
21554 * Fires when the iframe editor is updated with content from the textarea.
21555 * @param {Roo.HtmlEditorCore} this
21556 * @param {String} html
21561 * @event editorevent
21562 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21563 * @param {Roo.HtmlEditorCore} this
21569 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21571 // defaults : white / black...
21572 this.applyBlacklists();
21579 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21583 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21589 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21594 * @cfg {Number} height (in pixels)
21598 * @cfg {Number} width (in pixels)
21603 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21606 stylesheets: false,
21611 // private properties
21612 validationEvent : false,
21614 initialized : false,
21616 sourceEditMode : false,
21617 onFocus : Roo.emptyFn,
21619 hideMode:'offsets',
21623 // blacklist + whitelisted elements..
21630 * Protected method that will not generally be called directly. It
21631 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21632 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21634 getDocMarkup : function(){
21638 // inherit styels from page...??
21639 if (this.stylesheets === false) {
21641 Roo.get(document.head).select('style').each(function(node) {
21642 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21645 Roo.get(document.head).select('link').each(function(node) {
21646 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21649 } else if (!this.stylesheets.length) {
21651 st = '<style type="text/css">' +
21652 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21655 st = '<style type="text/css">' +
21660 st += '<style type="text/css">' +
21661 'IMG { cursor: pointer } ' +
21664 var cls = 'roo-htmleditor-body';
21666 if(this.bodyCls.length){
21667 cls += ' ' + this.bodyCls;
21670 return '<html><head>' + st +
21671 //<style type="text/css">' +
21672 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21674 ' </head><body class="' + cls + '"></body></html>';
21678 onRender : function(ct, position)
21681 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21682 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21685 this.el.dom.style.border = '0 none';
21686 this.el.dom.setAttribute('tabIndex', -1);
21687 this.el.addClass('x-hidden hide');
21691 if(Roo.isIE){ // fix IE 1px bogus margin
21692 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21696 this.frameId = Roo.id();
21700 var iframe = this.owner.wrap.createChild({
21702 cls: 'form-control', // bootstrap..
21704 name: this.frameId,
21705 frameBorder : 'no',
21706 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21711 this.iframe = iframe.dom;
21713 this.assignDocWin();
21715 this.doc.designMode = 'on';
21718 this.doc.write(this.getDocMarkup());
21722 var task = { // must defer to wait for browser to be ready
21724 //console.log("run task?" + this.doc.readyState);
21725 this.assignDocWin();
21726 if(this.doc.body || this.doc.readyState == 'complete'){
21728 this.doc.designMode="on";
21732 Roo.TaskMgr.stop(task);
21733 this.initEditor.defer(10, this);
21740 Roo.TaskMgr.start(task);
21745 onResize : function(w, h)
21747 Roo.log('resize: ' +w + ',' + h );
21748 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21752 if(typeof w == 'number'){
21754 this.iframe.style.width = w + 'px';
21756 if(typeof h == 'number'){
21758 this.iframe.style.height = h + 'px';
21760 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21767 * Toggles the editor between standard and source edit mode.
21768 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21770 toggleSourceEdit : function(sourceEditMode){
21772 this.sourceEditMode = sourceEditMode === true;
21774 if(this.sourceEditMode){
21776 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21779 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21780 //this.iframe.className = '';
21783 //this.setSize(this.owner.wrap.getSize());
21784 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21791 * Protected method that will not generally be called directly. If you need/want
21792 * custom HTML cleanup, this is the method you should override.
21793 * @param {String} html The HTML to be cleaned
21794 * return {String} The cleaned HTML
21796 cleanHtml : function(html){
21797 html = String(html);
21798 if(html.length > 5){
21799 if(Roo.isSafari){ // strip safari nonsense
21800 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21803 if(html == ' '){
21810 * HTML Editor -> Textarea
21811 * Protected method that will not generally be called directly. Syncs the contents
21812 * of the editor iframe with the textarea.
21814 syncValue : function(){
21815 if(this.initialized){
21816 var bd = (this.doc.body || this.doc.documentElement);
21817 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21818 var html = bd.innerHTML;
21820 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21821 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21823 html = '<div style="'+m[0]+'">' + html + '</div>';
21826 html = this.cleanHtml(html);
21827 // fix up the special chars.. normaly like back quotes in word...
21828 // however we do not want to do this with chinese..
21829 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21830 var cc = b.charCodeAt();
21832 (cc >= 0x4E00 && cc < 0xA000 ) ||
21833 (cc >= 0x3400 && cc < 0x4E00 ) ||
21834 (cc >= 0xf900 && cc < 0xfb00 )
21840 if(this.owner.fireEvent('beforesync', this, html) !== false){
21841 this.el.dom.value = html;
21842 this.owner.fireEvent('sync', this, html);
21848 * Protected method that will not generally be called directly. Pushes the value of the textarea
21849 * into the iframe editor.
21851 pushValue : function(){
21852 if(this.initialized){
21853 var v = this.el.dom.value.trim();
21855 // if(v.length < 1){
21859 if(this.owner.fireEvent('beforepush', this, v) !== false){
21860 var d = (this.doc.body || this.doc.documentElement);
21862 this.cleanUpPaste();
21863 this.el.dom.value = d.innerHTML;
21864 this.owner.fireEvent('push', this, v);
21870 deferFocus : function(){
21871 this.focus.defer(10, this);
21875 focus : function(){
21876 if(this.win && !this.sourceEditMode){
21883 assignDocWin: function()
21885 var iframe = this.iframe;
21888 this.doc = iframe.contentWindow.document;
21889 this.win = iframe.contentWindow;
21891 // if (!Roo.get(this.frameId)) {
21894 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21895 // this.win = Roo.get(this.frameId).dom.contentWindow;
21897 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21901 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21902 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21907 initEditor : function(){
21908 //console.log("INIT EDITOR");
21909 this.assignDocWin();
21913 this.doc.designMode="on";
21915 this.doc.write(this.getDocMarkup());
21918 var dbody = (this.doc.body || this.doc.documentElement);
21919 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21920 // this copies styles from the containing element into thsi one..
21921 // not sure why we need all of this..
21922 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21924 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21925 //ss['background-attachment'] = 'fixed'; // w3c
21926 dbody.bgProperties = 'fixed'; // ie
21927 //Roo.DomHelper.applyStyles(dbody, ss);
21928 Roo.EventManager.on(this.doc, {
21929 //'mousedown': this.onEditorEvent,
21930 'mouseup': this.onEditorEvent,
21931 'dblclick': this.onEditorEvent,
21932 'click': this.onEditorEvent,
21933 'keyup': this.onEditorEvent,
21938 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21940 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21941 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21943 this.initialized = true;
21945 this.owner.fireEvent('initialize', this);
21950 onDestroy : function(){
21956 //for (var i =0; i < this.toolbars.length;i++) {
21957 // // fixme - ask toolbars for heights?
21958 // this.toolbars[i].onDestroy();
21961 //this.wrap.dom.innerHTML = '';
21962 //this.wrap.remove();
21967 onFirstFocus : function(){
21969 this.assignDocWin();
21972 this.activated = true;
21975 if(Roo.isGecko){ // prevent silly gecko errors
21977 var s = this.win.getSelection();
21978 if(!s.focusNode || s.focusNode.nodeType != 3){
21979 var r = s.getRangeAt(0);
21980 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21985 this.execCmd('useCSS', true);
21986 this.execCmd('styleWithCSS', false);
21989 this.owner.fireEvent('activate', this);
21993 adjustFont: function(btn){
21994 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21995 //if(Roo.isSafari){ // safari
21998 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21999 if(Roo.isSafari){ // safari
22000 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22001 v = (v < 10) ? 10 : v;
22002 v = (v > 48) ? 48 : v;
22003 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22008 v = Math.max(1, v+adjust);
22010 this.execCmd('FontSize', v );
22013 onEditorEvent : function(e)
22015 this.owner.fireEvent('editorevent', this, e);
22016 // this.updateToolbar();
22017 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22020 insertTag : function(tg)
22022 // could be a bit smarter... -> wrap the current selected tRoo..
22023 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22025 range = this.createRange(this.getSelection());
22026 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22027 wrappingNode.appendChild(range.extractContents());
22028 range.insertNode(wrappingNode);
22035 this.execCmd("formatblock", tg);
22039 insertText : function(txt)
22043 var range = this.createRange();
22044 range.deleteContents();
22045 //alert(Sender.getAttribute('label'));
22047 range.insertNode(this.doc.createTextNode(txt));
22053 * Executes a Midas editor command on the editor document and performs necessary focus and
22054 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22055 * @param {String} cmd The Midas command
22056 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22058 relayCmd : function(cmd, value){
22060 this.execCmd(cmd, value);
22061 this.owner.fireEvent('editorevent', this);
22062 //this.updateToolbar();
22063 this.owner.deferFocus();
22067 * Executes a Midas editor command directly on the editor document.
22068 * For visual commands, you should use {@link #relayCmd} instead.
22069 * <b>This should only be called after the editor is initialized.</b>
22070 * @param {String} cmd The Midas command
22071 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22073 execCmd : function(cmd, value){
22074 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22081 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22083 * @param {String} text | dom node..
22085 insertAtCursor : function(text)
22088 if(!this.activated){
22094 var r = this.doc.selection.createRange();
22105 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22109 // from jquery ui (MIT licenced)
22111 var win = this.win;
22113 if (win.getSelection && win.getSelection().getRangeAt) {
22114 range = win.getSelection().getRangeAt(0);
22115 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22116 range.insertNode(node);
22117 } else if (win.document.selection && win.document.selection.createRange) {
22118 // no firefox support
22119 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22120 win.document.selection.createRange().pasteHTML(txt);
22122 // no firefox support
22123 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22124 this.execCmd('InsertHTML', txt);
22133 mozKeyPress : function(e){
22135 var c = e.getCharCode(), cmd;
22138 c = String.fromCharCode(c).toLowerCase();
22152 this.cleanUpPaste.defer(100, this);
22160 e.preventDefault();
22168 fixKeys : function(){ // load time branching for fastest keydown performance
22170 return function(e){
22171 var k = e.getKey(), r;
22174 r = this.doc.selection.createRange();
22177 r.pasteHTML('    ');
22184 r = this.doc.selection.createRange();
22186 var target = r.parentElement();
22187 if(!target || target.tagName.toLowerCase() != 'li'){
22189 r.pasteHTML('<br />');
22195 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22196 this.cleanUpPaste.defer(100, this);
22202 }else if(Roo.isOpera){
22203 return function(e){
22204 var k = e.getKey();
22208 this.execCmd('InsertHTML','    ');
22211 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22212 this.cleanUpPaste.defer(100, this);
22217 }else if(Roo.isSafari){
22218 return function(e){
22219 var k = e.getKey();
22223 this.execCmd('InsertText','\t');
22227 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22228 this.cleanUpPaste.defer(100, this);
22236 getAllAncestors: function()
22238 var p = this.getSelectedNode();
22241 a.push(p); // push blank onto stack..
22242 p = this.getParentElement();
22246 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22250 a.push(this.doc.body);
22254 lastSelNode : false,
22257 getSelection : function()
22259 this.assignDocWin();
22260 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22263 getSelectedNode: function()
22265 // this may only work on Gecko!!!
22267 // should we cache this!!!!
22272 var range = this.createRange(this.getSelection()).cloneRange();
22275 var parent = range.parentElement();
22277 var testRange = range.duplicate();
22278 testRange.moveToElementText(parent);
22279 if (testRange.inRange(range)) {
22282 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22285 parent = parent.parentElement;
22290 // is ancestor a text element.
22291 var ac = range.commonAncestorContainer;
22292 if (ac.nodeType == 3) {
22293 ac = ac.parentNode;
22296 var ar = ac.childNodes;
22299 var other_nodes = [];
22300 var has_other_nodes = false;
22301 for (var i=0;i<ar.length;i++) {
22302 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22305 // fullly contained node.
22307 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22312 // probably selected..
22313 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22314 other_nodes.push(ar[i]);
22318 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22323 has_other_nodes = true;
22325 if (!nodes.length && other_nodes.length) {
22326 nodes= other_nodes;
22328 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22334 createRange: function(sel)
22336 // this has strange effects when using with
22337 // top toolbar - not sure if it's a great idea.
22338 //this.editor.contentWindow.focus();
22339 if (typeof sel != "undefined") {
22341 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22343 return this.doc.createRange();
22346 return this.doc.createRange();
22349 getParentElement: function()
22352 this.assignDocWin();
22353 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22355 var range = this.createRange(sel);
22358 var p = range.commonAncestorContainer;
22359 while (p.nodeType == 3) { // text node
22370 * Range intersection.. the hard stuff...
22374 * [ -- selected range --- ]
22378 * if end is before start or hits it. fail.
22379 * if start is after end or hits it fail.
22381 * if either hits (but other is outside. - then it's not
22387 // @see http://www.thismuchiknow.co.uk/?p=64.
22388 rangeIntersectsNode : function(range, node)
22390 var nodeRange = node.ownerDocument.createRange();
22392 nodeRange.selectNode(node);
22394 nodeRange.selectNodeContents(node);
22397 var rangeStartRange = range.cloneRange();
22398 rangeStartRange.collapse(true);
22400 var rangeEndRange = range.cloneRange();
22401 rangeEndRange.collapse(false);
22403 var nodeStartRange = nodeRange.cloneRange();
22404 nodeStartRange.collapse(true);
22406 var nodeEndRange = nodeRange.cloneRange();
22407 nodeEndRange.collapse(false);
22409 return rangeStartRange.compareBoundaryPoints(
22410 Range.START_TO_START, nodeEndRange) == -1 &&
22411 rangeEndRange.compareBoundaryPoints(
22412 Range.START_TO_START, nodeStartRange) == 1;
22416 rangeCompareNode : function(range, node)
22418 var nodeRange = node.ownerDocument.createRange();
22420 nodeRange.selectNode(node);
22422 nodeRange.selectNodeContents(node);
22426 range.collapse(true);
22428 nodeRange.collapse(true);
22430 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22431 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22433 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22435 var nodeIsBefore = ss == 1;
22436 var nodeIsAfter = ee == -1;
22438 if (nodeIsBefore && nodeIsAfter) {
22441 if (!nodeIsBefore && nodeIsAfter) {
22442 return 1; //right trailed.
22445 if (nodeIsBefore && !nodeIsAfter) {
22446 return 2; // left trailed.
22452 // private? - in a new class?
22453 cleanUpPaste : function()
22455 // cleans up the whole document..
22456 Roo.log('cleanuppaste');
22458 this.cleanUpChildren(this.doc.body);
22459 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22460 if (clean != this.doc.body.innerHTML) {
22461 this.doc.body.innerHTML = clean;
22466 cleanWordChars : function(input) {// change the chars to hex code
22467 var he = Roo.HtmlEditorCore;
22469 var output = input;
22470 Roo.each(he.swapCodes, function(sw) {
22471 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22473 output = output.replace(swapper, sw[1]);
22480 cleanUpChildren : function (n)
22482 if (!n.childNodes.length) {
22485 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22486 this.cleanUpChild(n.childNodes[i]);
22493 cleanUpChild : function (node)
22496 //console.log(node);
22497 if (node.nodeName == "#text") {
22498 // clean up silly Windows -- stuff?
22501 if (node.nodeName == "#comment") {
22502 node.parentNode.removeChild(node);
22503 // clean up silly Windows -- stuff?
22506 var lcname = node.tagName.toLowerCase();
22507 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22508 // whitelist of tags..
22510 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22512 node.parentNode.removeChild(node);
22517 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22519 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22520 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22522 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22523 // remove_keep_children = true;
22526 if (remove_keep_children) {
22527 this.cleanUpChildren(node);
22528 // inserts everything just before this node...
22529 while (node.childNodes.length) {
22530 var cn = node.childNodes[0];
22531 node.removeChild(cn);
22532 node.parentNode.insertBefore(cn, node);
22534 node.parentNode.removeChild(node);
22538 if (!node.attributes || !node.attributes.length) {
22539 this.cleanUpChildren(node);
22543 function cleanAttr(n,v)
22546 if (v.match(/^\./) || v.match(/^\//)) {
22549 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22552 if (v.match(/^#/)) {
22555 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22556 node.removeAttribute(n);
22560 var cwhite = this.cwhite;
22561 var cblack = this.cblack;
22563 function cleanStyle(n,v)
22565 if (v.match(/expression/)) { //XSS?? should we even bother..
22566 node.removeAttribute(n);
22570 var parts = v.split(/;/);
22573 Roo.each(parts, function(p) {
22574 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22578 var l = p.split(':').shift().replace(/\s+/g,'');
22579 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22581 if ( cwhite.length && cblack.indexOf(l) > -1) {
22582 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22583 //node.removeAttribute(n);
22587 // only allow 'c whitelisted system attributes'
22588 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22589 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22590 //node.removeAttribute(n);
22600 if (clean.length) {
22601 node.setAttribute(n, clean.join(';'));
22603 node.removeAttribute(n);
22609 for (var i = node.attributes.length-1; i > -1 ; i--) {
22610 var a = node.attributes[i];
22613 if (a.name.toLowerCase().substr(0,2)=='on') {
22614 node.removeAttribute(a.name);
22617 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22618 node.removeAttribute(a.name);
22621 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22622 cleanAttr(a.name,a.value); // fixme..
22625 if (a.name == 'style') {
22626 cleanStyle(a.name,a.value);
22629 /// clean up MS crap..
22630 // tecnically this should be a list of valid class'es..
22633 if (a.name == 'class') {
22634 if (a.value.match(/^Mso/)) {
22635 node.className = '';
22638 if (a.value.match(/^body$/)) {
22639 node.className = '';
22650 this.cleanUpChildren(node);
22656 * Clean up MS wordisms...
22658 cleanWord : function(node)
22663 this.cleanWord(this.doc.body);
22666 if (node.nodeName == "#text") {
22667 // clean up silly Windows -- stuff?
22670 if (node.nodeName == "#comment") {
22671 node.parentNode.removeChild(node);
22672 // clean up silly Windows -- stuff?
22676 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22677 node.parentNode.removeChild(node);
22681 // remove - but keep children..
22682 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22683 while (node.childNodes.length) {
22684 var cn = node.childNodes[0];
22685 node.removeChild(cn);
22686 node.parentNode.insertBefore(cn, node);
22688 node.parentNode.removeChild(node);
22689 this.iterateChildren(node, this.cleanWord);
22693 if (node.className.length) {
22695 var cn = node.className.split(/\W+/);
22697 Roo.each(cn, function(cls) {
22698 if (cls.match(/Mso[a-zA-Z]+/)) {
22703 node.className = cna.length ? cna.join(' ') : '';
22705 node.removeAttribute("class");
22709 if (node.hasAttribute("lang")) {
22710 node.removeAttribute("lang");
22713 if (node.hasAttribute("style")) {
22715 var styles = node.getAttribute("style").split(";");
22717 Roo.each(styles, function(s) {
22718 if (!s.match(/:/)) {
22721 var kv = s.split(":");
22722 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22725 // what ever is left... we allow.
22728 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22729 if (!nstyle.length) {
22730 node.removeAttribute('style');
22733 this.iterateChildren(node, this.cleanWord);
22739 * iterateChildren of a Node, calling fn each time, using this as the scole..
22740 * @param {DomNode} node node to iterate children of.
22741 * @param {Function} fn method of this class to call on each item.
22743 iterateChildren : function(node, fn)
22745 if (!node.childNodes.length) {
22748 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22749 fn.call(this, node.childNodes[i])
22755 * cleanTableWidths.
22757 * Quite often pasting from word etc.. results in tables with column and widths.
22758 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22761 cleanTableWidths : function(node)
22766 this.cleanTableWidths(this.doc.body);
22771 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22774 Roo.log(node.tagName);
22775 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22776 this.iterateChildren(node, this.cleanTableWidths);
22779 if (node.hasAttribute('width')) {
22780 node.removeAttribute('width');
22784 if (node.hasAttribute("style")) {
22787 var styles = node.getAttribute("style").split(";");
22789 Roo.each(styles, function(s) {
22790 if (!s.match(/:/)) {
22793 var kv = s.split(":");
22794 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22797 // what ever is left... we allow.
22800 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22801 if (!nstyle.length) {
22802 node.removeAttribute('style');
22806 this.iterateChildren(node, this.cleanTableWidths);
22814 domToHTML : function(currentElement, depth, nopadtext) {
22816 depth = depth || 0;
22817 nopadtext = nopadtext || false;
22819 if (!currentElement) {
22820 return this.domToHTML(this.doc.body);
22823 //Roo.log(currentElement);
22825 var allText = false;
22826 var nodeName = currentElement.nodeName;
22827 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22829 if (nodeName == '#text') {
22831 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22836 if (nodeName != 'BODY') {
22839 // Prints the node tagName, such as <A>, <IMG>, etc
22842 for(i = 0; i < currentElement.attributes.length;i++) {
22844 var aname = currentElement.attributes.item(i).name;
22845 if (!currentElement.attributes.item(i).value.length) {
22848 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22851 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22860 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22863 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22868 // Traverse the tree
22870 var currentElementChild = currentElement.childNodes.item(i);
22871 var allText = true;
22872 var innerHTML = '';
22874 while (currentElementChild) {
22875 // Formatting code (indent the tree so it looks nice on the screen)
22876 var nopad = nopadtext;
22877 if (lastnode == 'SPAN') {
22881 if (currentElementChild.nodeName == '#text') {
22882 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22883 toadd = nopadtext ? toadd : toadd.trim();
22884 if (!nopad && toadd.length > 80) {
22885 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22887 innerHTML += toadd;
22890 currentElementChild = currentElement.childNodes.item(i);
22896 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22898 // Recursively traverse the tree structure of the child node
22899 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22900 lastnode = currentElementChild.nodeName;
22902 currentElementChild=currentElement.childNodes.item(i);
22908 // The remaining code is mostly for formatting the tree
22909 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22914 ret+= "</"+tagName+">";
22920 applyBlacklists : function()
22922 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22923 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22927 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22928 if (b.indexOf(tag) > -1) {
22931 this.white.push(tag);
22935 Roo.each(w, function(tag) {
22936 if (b.indexOf(tag) > -1) {
22939 if (this.white.indexOf(tag) > -1) {
22942 this.white.push(tag);
22947 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22948 if (w.indexOf(tag) > -1) {
22951 this.black.push(tag);
22955 Roo.each(b, function(tag) {
22956 if (w.indexOf(tag) > -1) {
22959 if (this.black.indexOf(tag) > -1) {
22962 this.black.push(tag);
22967 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22968 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22972 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22973 if (b.indexOf(tag) > -1) {
22976 this.cwhite.push(tag);
22980 Roo.each(w, function(tag) {
22981 if (b.indexOf(tag) > -1) {
22984 if (this.cwhite.indexOf(tag) > -1) {
22987 this.cwhite.push(tag);
22992 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22993 if (w.indexOf(tag) > -1) {
22996 this.cblack.push(tag);
23000 Roo.each(b, function(tag) {
23001 if (w.indexOf(tag) > -1) {
23004 if (this.cblack.indexOf(tag) > -1) {
23007 this.cblack.push(tag);
23012 setStylesheets : function(stylesheets)
23014 if(typeof(stylesheets) == 'string'){
23015 Roo.get(this.iframe.contentDocument.head).createChild({
23017 rel : 'stylesheet',
23026 Roo.each(stylesheets, function(s) {
23031 Roo.get(_this.iframe.contentDocument.head).createChild({
23033 rel : 'stylesheet',
23042 removeStylesheets : function()
23046 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23051 setStyle : function(style)
23053 Roo.get(this.iframe.contentDocument.head).createChild({
23062 // hide stuff that is not compatible
23076 * @event specialkey
23080 * @cfg {String} fieldClass @hide
23083 * @cfg {String} focusClass @hide
23086 * @cfg {String} autoCreate @hide
23089 * @cfg {String} inputType @hide
23092 * @cfg {String} invalidClass @hide
23095 * @cfg {String} invalidText @hide
23098 * @cfg {String} msgFx @hide
23101 * @cfg {String} validateOnBlur @hide
23105 Roo.HtmlEditorCore.white = [
23106 'area', 'br', 'img', 'input', 'hr', 'wbr',
23108 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23109 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23110 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23111 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23112 'table', 'ul', 'xmp',
23114 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23117 'dir', 'menu', 'ol', 'ul', 'dl',
23123 Roo.HtmlEditorCore.black = [
23124 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23126 'base', 'basefont', 'bgsound', 'blink', 'body',
23127 'frame', 'frameset', 'head', 'html', 'ilayer',
23128 'iframe', 'layer', 'link', 'meta', 'object',
23129 'script', 'style' ,'title', 'xml' // clean later..
23131 Roo.HtmlEditorCore.clean = [
23132 'script', 'style', 'title', 'xml'
23134 Roo.HtmlEditorCore.remove = [
23139 Roo.HtmlEditorCore.ablack = [
23143 Roo.HtmlEditorCore.aclean = [
23144 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23148 Roo.HtmlEditorCore.pwhite= [
23149 'http', 'https', 'mailto'
23152 // white listed style attributes.
23153 Roo.HtmlEditorCore.cwhite= [
23154 // 'text-align', /// default is to allow most things..
23160 // black listed style attributes.
23161 Roo.HtmlEditorCore.cblack= [
23162 // 'font-size' -- this can be set by the project
23166 Roo.HtmlEditorCore.swapCodes =[
23185 * @class Roo.bootstrap.HtmlEditor
23186 * @extends Roo.bootstrap.TextArea
23187 * Bootstrap HtmlEditor class
23190 * Create a new HtmlEditor
23191 * @param {Object} config The config object
23194 Roo.bootstrap.HtmlEditor = function(config){
23195 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23196 if (!this.toolbars) {
23197 this.toolbars = [];
23200 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23203 * @event initialize
23204 * Fires when the editor is fully initialized (including the iframe)
23205 * @param {HtmlEditor} this
23210 * Fires when the editor is first receives the focus. Any insertion must wait
23211 * until after this event.
23212 * @param {HtmlEditor} this
23216 * @event beforesync
23217 * Fires before the textarea is updated with content from the editor iframe. Return false
23218 * to cancel the sync.
23219 * @param {HtmlEditor} this
23220 * @param {String} html
23224 * @event beforepush
23225 * Fires before the iframe editor is updated with content from the textarea. Return false
23226 * to cancel the push.
23227 * @param {HtmlEditor} this
23228 * @param {String} html
23233 * Fires when the textarea is updated with content from the editor iframe.
23234 * @param {HtmlEditor} this
23235 * @param {String} html
23240 * Fires when the iframe editor is updated with content from the textarea.
23241 * @param {HtmlEditor} this
23242 * @param {String} html
23246 * @event editmodechange
23247 * Fires when the editor switches edit modes
23248 * @param {HtmlEditor} this
23249 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23251 editmodechange: true,
23253 * @event editorevent
23254 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23255 * @param {HtmlEditor} this
23259 * @event firstfocus
23260 * Fires when on first focus - needed by toolbars..
23261 * @param {HtmlEditor} this
23266 * Auto save the htmlEditor value as a file into Events
23267 * @param {HtmlEditor} this
23271 * @event savedpreview
23272 * preview the saved version of htmlEditor
23273 * @param {HtmlEditor} this
23280 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23284 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23289 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23294 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23299 * @cfg {Number} height (in pixels)
23303 * @cfg {Number} width (in pixels)
23308 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23311 stylesheets: false,
23316 // private properties
23317 validationEvent : false,
23319 initialized : false,
23322 onFocus : Roo.emptyFn,
23324 hideMode:'offsets',
23326 tbContainer : false,
23330 toolbarContainer :function() {
23331 return this.wrap.select('.x-html-editor-tb',true).first();
23335 * Protected method that will not generally be called directly. It
23336 * is called when the editor creates its toolbar. Override this method if you need to
23337 * add custom toolbar buttons.
23338 * @param {HtmlEditor} editor
23340 createToolbar : function(){
23341 Roo.log('renewing');
23342 Roo.log("create toolbars");
23344 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23345 this.toolbars[0].render(this.toolbarContainer());
23349 // if (!editor.toolbars || !editor.toolbars.length) {
23350 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23353 // for (var i =0 ; i < editor.toolbars.length;i++) {
23354 // editor.toolbars[i] = Roo.factory(
23355 // typeof(editor.toolbars[i]) == 'string' ?
23356 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23357 // Roo.bootstrap.HtmlEditor);
23358 // editor.toolbars[i].init(editor);
23364 onRender : function(ct, position)
23366 // Roo.log("Call onRender: " + this.xtype);
23368 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23370 this.wrap = this.inputEl().wrap({
23371 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23374 this.editorcore.onRender(ct, position);
23376 if (this.resizable) {
23377 this.resizeEl = new Roo.Resizable(this.wrap, {
23381 minHeight : this.height,
23382 height: this.height,
23383 handles : this.resizable,
23386 resize : function(r, w, h) {
23387 _t.onResize(w,h); // -something
23393 this.createToolbar(this);
23396 if(!this.width && this.resizable){
23397 this.setSize(this.wrap.getSize());
23399 if (this.resizeEl) {
23400 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23401 // should trigger onReize..
23407 onResize : function(w, h)
23409 Roo.log('resize: ' +w + ',' + h );
23410 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23414 if(this.inputEl() ){
23415 if(typeof w == 'number'){
23416 var aw = w - this.wrap.getFrameWidth('lr');
23417 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23420 if(typeof h == 'number'){
23421 var tbh = -11; // fixme it needs to tool bar size!
23422 for (var i =0; i < this.toolbars.length;i++) {
23423 // fixme - ask toolbars for heights?
23424 tbh += this.toolbars[i].el.getHeight();
23425 //if (this.toolbars[i].footer) {
23426 // tbh += this.toolbars[i].footer.el.getHeight();
23434 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23435 ah -= 5; // knock a few pixes off for look..
23436 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23440 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23441 this.editorcore.onResize(ew,eh);
23446 * Toggles the editor between standard and source edit mode.
23447 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23449 toggleSourceEdit : function(sourceEditMode)
23451 this.editorcore.toggleSourceEdit(sourceEditMode);
23453 if(this.editorcore.sourceEditMode){
23454 Roo.log('editor - showing textarea');
23457 // Roo.log(this.syncValue());
23459 this.inputEl().removeClass(['hide', 'x-hidden']);
23460 this.inputEl().dom.removeAttribute('tabIndex');
23461 this.inputEl().focus();
23463 Roo.log('editor - hiding textarea');
23465 // Roo.log(this.pushValue());
23468 this.inputEl().addClass(['hide', 'x-hidden']);
23469 this.inputEl().dom.setAttribute('tabIndex', -1);
23470 //this.deferFocus();
23473 if(this.resizable){
23474 this.setSize(this.wrap.getSize());
23477 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23480 // private (for BoxComponent)
23481 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23483 // private (for BoxComponent)
23484 getResizeEl : function(){
23488 // private (for BoxComponent)
23489 getPositionEl : function(){
23494 initEvents : function(){
23495 this.originalValue = this.getValue();
23499 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23502 // markInvalid : Roo.emptyFn,
23504 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23507 // clearInvalid : Roo.emptyFn,
23509 setValue : function(v){
23510 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23511 this.editorcore.pushValue();
23516 deferFocus : function(){
23517 this.focus.defer(10, this);
23521 focus : function(){
23522 this.editorcore.focus();
23528 onDestroy : function(){
23534 for (var i =0; i < this.toolbars.length;i++) {
23535 // fixme - ask toolbars for heights?
23536 this.toolbars[i].onDestroy();
23539 this.wrap.dom.innerHTML = '';
23540 this.wrap.remove();
23545 onFirstFocus : function(){
23546 //Roo.log("onFirstFocus");
23547 this.editorcore.onFirstFocus();
23548 for (var i =0; i < this.toolbars.length;i++) {
23549 this.toolbars[i].onFirstFocus();
23555 syncValue : function()
23557 this.editorcore.syncValue();
23560 pushValue : function()
23562 this.editorcore.pushValue();
23566 // hide stuff that is not compatible
23580 * @event specialkey
23584 * @cfg {String} fieldClass @hide
23587 * @cfg {String} focusClass @hide
23590 * @cfg {String} autoCreate @hide
23593 * @cfg {String} inputType @hide
23596 * @cfg {String} invalidClass @hide
23599 * @cfg {String} invalidText @hide
23602 * @cfg {String} msgFx @hide
23605 * @cfg {String} validateOnBlur @hide
23614 Roo.namespace('Roo.bootstrap.htmleditor');
23616 * @class Roo.bootstrap.HtmlEditorToolbar1
23621 new Roo.bootstrap.HtmlEditor({
23624 new Roo.bootstrap.HtmlEditorToolbar1({
23625 disable : { fonts: 1 , format: 1, ..., ... , ...],
23631 * @cfg {Object} disable List of elements to disable..
23632 * @cfg {Array} btns List of additional buttons.
23636 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23639 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23642 Roo.apply(this, config);
23644 // default disabled, based on 'good practice'..
23645 this.disable = this.disable || {};
23646 Roo.applyIf(this.disable, {
23649 specialElements : true
23651 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23653 this.editor = config.editor;
23654 this.editorcore = config.editor.editorcore;
23656 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23658 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23659 // dont call parent... till later.
23661 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23666 editorcore : false,
23671 "h1","h2","h3","h4","h5","h6",
23673 "abbr", "acronym", "address", "cite", "samp", "var",
23677 onRender : function(ct, position)
23679 // Roo.log("Call onRender: " + this.xtype);
23681 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23683 this.el.dom.style.marginBottom = '0';
23685 var editorcore = this.editorcore;
23686 var editor= this.editor;
23689 var btn = function(id,cmd , toggle, handler, html){
23691 var event = toggle ? 'toggle' : 'click';
23696 xns: Roo.bootstrap,
23699 enableToggle:toggle !== false,
23701 pressed : toggle ? false : null,
23704 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23705 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23711 // var cb_box = function...
23716 xns: Roo.bootstrap,
23717 glyphicon : 'font',
23721 xns: Roo.bootstrap,
23725 Roo.each(this.formats, function(f) {
23726 style.menu.items.push({
23728 xns: Roo.bootstrap,
23729 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23734 editorcore.insertTag(this.tagname);
23741 children.push(style);
23743 btn('bold',false,true);
23744 btn('italic',false,true);
23745 btn('align-left', 'justifyleft',true);
23746 btn('align-center', 'justifycenter',true);
23747 btn('align-right' , 'justifyright',true);
23748 btn('link', false, false, function(btn) {
23749 //Roo.log("create link?");
23750 var url = prompt(this.createLinkText, this.defaultLinkValue);
23751 if(url && url != 'http:/'+'/'){
23752 this.editorcore.relayCmd('createlink', url);
23755 btn('list','insertunorderedlist',true);
23756 btn('pencil', false,true, function(btn){
23758 this.toggleSourceEdit(btn.pressed);
23761 if (this.editor.btns.length > 0) {
23762 for (var i = 0; i<this.editor.btns.length; i++) {
23763 children.push(this.editor.btns[i]);
23771 xns: Roo.bootstrap,
23776 xns: Roo.bootstrap,
23781 cog.menu.items.push({
23783 xns: Roo.bootstrap,
23784 html : Clean styles,
23789 editorcore.insertTag(this.tagname);
23798 this.xtype = 'NavSimplebar';
23800 for(var i=0;i< children.length;i++) {
23802 this.buttons.add(this.addxtypeChild(children[i]));
23806 editor.on('editorevent', this.updateToolbar, this);
23808 onBtnClick : function(id)
23810 this.editorcore.relayCmd(id);
23811 this.editorcore.focus();
23815 * Protected method that will not generally be called directly. It triggers
23816 * a toolbar update by reading the markup state of the current selection in the editor.
23818 updateToolbar: function(){
23820 if(!this.editorcore.activated){
23821 this.editor.onFirstFocus(); // is this neeed?
23825 var btns = this.buttons;
23826 var doc = this.editorcore.doc;
23827 btns.get('bold').setActive(doc.queryCommandState('bold'));
23828 btns.get('italic').setActive(doc.queryCommandState('italic'));
23829 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23831 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23832 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23833 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23835 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23836 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23839 var ans = this.editorcore.getAllAncestors();
23840 if (this.formatCombo) {
23843 var store = this.formatCombo.store;
23844 this.formatCombo.setValue("");
23845 for (var i =0; i < ans.length;i++) {
23846 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23848 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23856 // hides menus... - so this cant be on a menu...
23857 Roo.bootstrap.MenuMgr.hideAll();
23859 Roo.bootstrap.MenuMgr.hideAll();
23860 //this.editorsyncValue();
23862 onFirstFocus: function() {
23863 this.buttons.each(function(item){
23867 toggleSourceEdit : function(sourceEditMode){
23870 if(sourceEditMode){
23871 Roo.log("disabling buttons");
23872 this.buttons.each( function(item){
23873 if(item.cmd != 'pencil'){
23879 Roo.log("enabling buttons");
23880 if(this.editorcore.initialized){
23881 this.buttons.each( function(item){
23887 Roo.log("calling toggole on editor");
23888 // tell the editor that it's been pressed..
23889 this.editor.toggleSourceEdit(sourceEditMode);
23899 * @class Roo.bootstrap.Table.AbstractSelectionModel
23900 * @extends Roo.util.Observable
23901 * Abstract base class for grid SelectionModels. It provides the interface that should be
23902 * implemented by descendant classes. This class should not be directly instantiated.
23905 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23906 this.locked = false;
23907 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23911 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23912 /** @ignore Called by the grid automatically. Do not call directly. */
23913 init : function(grid){
23919 * Locks the selections.
23922 this.locked = true;
23926 * Unlocks the selections.
23928 unlock : function(){
23929 this.locked = false;
23933 * Returns true if the selections are locked.
23934 * @return {Boolean}
23936 isLocked : function(){
23937 return this.locked;
23941 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23942 * @class Roo.bootstrap.Table.RowSelectionModel
23943 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23944 * It supports multiple selections and keyboard selection/navigation.
23946 * @param {Object} config
23949 Roo.bootstrap.Table.RowSelectionModel = function(config){
23950 Roo.apply(this, config);
23951 this.selections = new Roo.util.MixedCollection(false, function(o){
23956 this.lastActive = false;
23960 * @event selectionchange
23961 * Fires when the selection changes
23962 * @param {SelectionModel} this
23964 "selectionchange" : true,
23966 * @event afterselectionchange
23967 * Fires after the selection changes (eg. by key press or clicking)
23968 * @param {SelectionModel} this
23970 "afterselectionchange" : true,
23972 * @event beforerowselect
23973 * Fires when a row is selected being selected, return false to cancel.
23974 * @param {SelectionModel} this
23975 * @param {Number} rowIndex The selected index
23976 * @param {Boolean} keepExisting False if other selections will be cleared
23978 "beforerowselect" : true,
23981 * Fires when a row is selected.
23982 * @param {SelectionModel} this
23983 * @param {Number} rowIndex The selected index
23984 * @param {Roo.data.Record} r The record
23986 "rowselect" : true,
23988 * @event rowdeselect
23989 * Fires when a row is deselected.
23990 * @param {SelectionModel} this
23991 * @param {Number} rowIndex The selected index
23993 "rowdeselect" : true
23995 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23996 this.locked = false;
23999 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24001 * @cfg {Boolean} singleSelect
24002 * True to allow selection of only one row at a time (defaults to false)
24004 singleSelect : false,
24007 initEvents : function()
24010 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24011 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24012 //}else{ // allow click to work like normal
24013 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24015 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24016 this.grid.on("rowclick", this.handleMouseDown, this);
24018 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24019 "up" : function(e){
24021 this.selectPrevious(e.shiftKey);
24022 }else if(this.last !== false && this.lastActive !== false){
24023 var last = this.last;
24024 this.selectRange(this.last, this.lastActive-1);
24025 this.grid.getView().focusRow(this.lastActive);
24026 if(last !== false){
24030 this.selectFirstRow();
24032 this.fireEvent("afterselectionchange", this);
24034 "down" : function(e){
24036 this.selectNext(e.shiftKey);
24037 }else if(this.last !== false && this.lastActive !== false){
24038 var last = this.last;
24039 this.selectRange(this.last, this.lastActive+1);
24040 this.grid.getView().focusRow(this.lastActive);
24041 if(last !== false){
24045 this.selectFirstRow();
24047 this.fireEvent("afterselectionchange", this);
24051 this.grid.store.on('load', function(){
24052 this.selections.clear();
24055 var view = this.grid.view;
24056 view.on("refresh", this.onRefresh, this);
24057 view.on("rowupdated", this.onRowUpdated, this);
24058 view.on("rowremoved", this.onRemove, this);
24063 onRefresh : function()
24065 var ds = this.grid.store, i, v = this.grid.view;
24066 var s = this.selections;
24067 s.each(function(r){
24068 if((i = ds.indexOfId(r.id)) != -1){
24077 onRemove : function(v, index, r){
24078 this.selections.remove(r);
24082 onRowUpdated : function(v, index, r){
24083 if(this.isSelected(r)){
24084 v.onRowSelect(index);
24090 * @param {Array} records The records to select
24091 * @param {Boolean} keepExisting (optional) True to keep existing selections
24093 selectRecords : function(records, keepExisting)
24096 this.clearSelections();
24098 var ds = this.grid.store;
24099 for(var i = 0, len = records.length; i < len; i++){
24100 this.selectRow(ds.indexOf(records[i]), true);
24105 * Gets the number of selected rows.
24108 getCount : function(){
24109 return this.selections.length;
24113 * Selects the first row in the grid.
24115 selectFirstRow : function(){
24120 * Select the last row.
24121 * @param {Boolean} keepExisting (optional) True to keep existing selections
24123 selectLastRow : function(keepExisting){
24124 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24125 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24129 * Selects the row immediately following the last selected row.
24130 * @param {Boolean} keepExisting (optional) True to keep existing selections
24132 selectNext : function(keepExisting)
24134 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24135 this.selectRow(this.last+1, keepExisting);
24136 this.grid.getView().focusRow(this.last);
24141 * Selects the row that precedes the last selected row.
24142 * @param {Boolean} keepExisting (optional) True to keep existing selections
24144 selectPrevious : function(keepExisting){
24146 this.selectRow(this.last-1, keepExisting);
24147 this.grid.getView().focusRow(this.last);
24152 * Returns the selected records
24153 * @return {Array} Array of selected records
24155 getSelections : function(){
24156 return [].concat(this.selections.items);
24160 * Returns the first selected record.
24163 getSelected : function(){
24164 return this.selections.itemAt(0);
24169 * Clears all selections.
24171 clearSelections : function(fast)
24177 var ds = this.grid.store;
24178 var s = this.selections;
24179 s.each(function(r){
24180 this.deselectRow(ds.indexOfId(r.id));
24184 this.selections.clear();
24191 * Selects all rows.
24193 selectAll : function(){
24197 this.selections.clear();
24198 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24199 this.selectRow(i, true);
24204 * Returns True if there is a selection.
24205 * @return {Boolean}
24207 hasSelection : function(){
24208 return this.selections.length > 0;
24212 * Returns True if the specified row is selected.
24213 * @param {Number/Record} record The record or index of the record to check
24214 * @return {Boolean}
24216 isSelected : function(index){
24217 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24218 return (r && this.selections.key(r.id) ? true : false);
24222 * Returns True if the specified record id is selected.
24223 * @param {String} id The id of record to check
24224 * @return {Boolean}
24226 isIdSelected : function(id){
24227 return (this.selections.key(id) ? true : false);
24232 handleMouseDBClick : function(e, t){
24236 handleMouseDown : function(e, t)
24238 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24239 if(this.isLocked() || rowIndex < 0 ){
24242 if(e.shiftKey && this.last !== false){
24243 var last = this.last;
24244 this.selectRange(last, rowIndex, e.ctrlKey);
24245 this.last = last; // reset the last
24249 var isSelected = this.isSelected(rowIndex);
24250 //Roo.log("select row:" + rowIndex);
24252 this.deselectRow(rowIndex);
24254 this.selectRow(rowIndex, true);
24258 if(e.button !== 0 && isSelected){
24259 alert('rowIndex 2: ' + rowIndex);
24260 view.focusRow(rowIndex);
24261 }else if(e.ctrlKey && isSelected){
24262 this.deselectRow(rowIndex);
24263 }else if(!isSelected){
24264 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24265 view.focusRow(rowIndex);
24269 this.fireEvent("afterselectionchange", this);
24272 handleDragableRowClick : function(grid, rowIndex, e)
24274 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24275 this.selectRow(rowIndex, false);
24276 grid.view.focusRow(rowIndex);
24277 this.fireEvent("afterselectionchange", this);
24282 * Selects multiple rows.
24283 * @param {Array} rows Array of the indexes of the row to select
24284 * @param {Boolean} keepExisting (optional) True to keep existing selections
24286 selectRows : function(rows, keepExisting){
24288 this.clearSelections();
24290 for(var i = 0, len = rows.length; i < len; i++){
24291 this.selectRow(rows[i], true);
24296 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24297 * @param {Number} startRow The index of the first row in the range
24298 * @param {Number} endRow The index of the last row in the range
24299 * @param {Boolean} keepExisting (optional) True to retain existing selections
24301 selectRange : function(startRow, endRow, keepExisting){
24306 this.clearSelections();
24308 if(startRow <= endRow){
24309 for(var i = startRow; i <= endRow; i++){
24310 this.selectRow(i, true);
24313 for(var i = startRow; i >= endRow; i--){
24314 this.selectRow(i, true);
24320 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24321 * @param {Number} startRow The index of the first row in the range
24322 * @param {Number} endRow The index of the last row in the range
24324 deselectRange : function(startRow, endRow, preventViewNotify){
24328 for(var i = startRow; i <= endRow; i++){
24329 this.deselectRow(i, preventViewNotify);
24335 * @param {Number} row The index of the row to select
24336 * @param {Boolean} keepExisting (optional) True to keep existing selections
24338 selectRow : function(index, keepExisting, preventViewNotify)
24340 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24343 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24344 if(!keepExisting || this.singleSelect){
24345 this.clearSelections();
24348 var r = this.grid.store.getAt(index);
24349 //console.log('selectRow - record id :' + r.id);
24351 this.selections.add(r);
24352 this.last = this.lastActive = index;
24353 if(!preventViewNotify){
24354 var proxy = new Roo.Element(
24355 this.grid.getRowDom(index)
24357 proxy.addClass('bg-info info');
24359 this.fireEvent("rowselect", this, index, r);
24360 this.fireEvent("selectionchange", this);
24366 * @param {Number} row The index of the row to deselect
24368 deselectRow : function(index, preventViewNotify)
24373 if(this.last == index){
24376 if(this.lastActive == index){
24377 this.lastActive = false;
24380 var r = this.grid.store.getAt(index);
24385 this.selections.remove(r);
24386 //.console.log('deselectRow - record id :' + r.id);
24387 if(!preventViewNotify){
24389 var proxy = new Roo.Element(
24390 this.grid.getRowDom(index)
24392 proxy.removeClass('bg-info info');
24394 this.fireEvent("rowdeselect", this, index);
24395 this.fireEvent("selectionchange", this);
24399 restoreLast : function(){
24401 this.last = this._last;
24406 acceptsNav : function(row, col, cm){
24407 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24411 onEditorKey : function(field, e){
24412 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24417 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24419 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24421 }else if(k == e.ENTER && !e.ctrlKey){
24425 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24427 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24429 }else if(k == e.ESC){
24433 g.startEditing(newCell[0], newCell[1]);
24439 * Ext JS Library 1.1.1
24440 * Copyright(c) 2006-2007, Ext JS, LLC.
24442 * Originally Released Under LGPL - original licence link has changed is not relivant.
24445 * <script type="text/javascript">
24449 * @class Roo.bootstrap.PagingToolbar
24450 * @extends Roo.bootstrap.NavSimplebar
24451 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24453 * Create a new PagingToolbar
24454 * @param {Object} config The config object
24455 * @param {Roo.data.Store} store
24457 Roo.bootstrap.PagingToolbar = function(config)
24459 // old args format still supported... - xtype is prefered..
24460 // created from xtype...
24462 this.ds = config.dataSource;
24464 if (config.store && !this.ds) {
24465 this.store= Roo.factory(config.store, Roo.data);
24466 this.ds = this.store;
24467 this.ds.xmodule = this.xmodule || false;
24470 this.toolbarItems = [];
24471 if (config.items) {
24472 this.toolbarItems = config.items;
24475 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24480 this.bind(this.ds);
24483 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24487 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24489 * @cfg {Roo.data.Store} dataSource
24490 * The underlying data store providing the paged data
24493 * @cfg {String/HTMLElement/Element} container
24494 * container The id or element that will contain the toolbar
24497 * @cfg {Boolean} displayInfo
24498 * True to display the displayMsg (defaults to false)
24501 * @cfg {Number} pageSize
24502 * The number of records to display per page (defaults to 20)
24506 * @cfg {String} displayMsg
24507 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24509 displayMsg : 'Displaying {0} - {1} of {2}',
24511 * @cfg {String} emptyMsg
24512 * The message to display when no records are found (defaults to "No data to display")
24514 emptyMsg : 'No data to display',
24516 * Customizable piece of the default paging text (defaults to "Page")
24519 beforePageText : "Page",
24521 * Customizable piece of the default paging text (defaults to "of %0")
24524 afterPageText : "of {0}",
24526 * Customizable piece of the default paging text (defaults to "First Page")
24529 firstText : "First Page",
24531 * Customizable piece of the default paging text (defaults to "Previous Page")
24534 prevText : "Previous Page",
24536 * Customizable piece of the default paging text (defaults to "Next Page")
24539 nextText : "Next Page",
24541 * Customizable piece of the default paging text (defaults to "Last Page")
24544 lastText : "Last Page",
24546 * Customizable piece of the default paging text (defaults to "Refresh")
24549 refreshText : "Refresh",
24553 onRender : function(ct, position)
24555 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24556 this.navgroup.parentId = this.id;
24557 this.navgroup.onRender(this.el, null);
24558 // add the buttons to the navgroup
24560 if(this.displayInfo){
24561 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24562 this.displayEl = this.el.select('.x-paging-info', true).first();
24563 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24564 // this.displayEl = navel.el.select('span',true).first();
24570 Roo.each(_this.buttons, function(e){ // this might need to use render????
24571 Roo.factory(e).render(_this.el);
24575 Roo.each(_this.toolbarItems, function(e) {
24576 _this.navgroup.addItem(e);
24580 this.first = this.navgroup.addItem({
24581 tooltip: this.firstText,
24583 icon : 'fa fa-backward',
24585 preventDefault: true,
24586 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24589 this.prev = this.navgroup.addItem({
24590 tooltip: this.prevText,
24592 icon : 'fa fa-step-backward',
24594 preventDefault: true,
24595 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24597 //this.addSeparator();
24600 var field = this.navgroup.addItem( {
24602 cls : 'x-paging-position',
24604 html : this.beforePageText +
24605 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24606 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24609 this.field = field.el.select('input', true).first();
24610 this.field.on("keydown", this.onPagingKeydown, this);
24611 this.field.on("focus", function(){this.dom.select();});
24614 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24615 //this.field.setHeight(18);
24616 //this.addSeparator();
24617 this.next = this.navgroup.addItem({
24618 tooltip: this.nextText,
24620 html : ' <i class="fa fa-step-forward">',
24622 preventDefault: true,
24623 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24625 this.last = this.navgroup.addItem({
24626 tooltip: this.lastText,
24627 icon : 'fa fa-forward',
24630 preventDefault: true,
24631 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24633 //this.addSeparator();
24634 this.loading = this.navgroup.addItem({
24635 tooltip: this.refreshText,
24636 icon: 'fa fa-refresh',
24637 preventDefault: true,
24638 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24644 updateInfo : function(){
24645 if(this.displayEl){
24646 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24647 var msg = count == 0 ?
24651 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24653 this.displayEl.update(msg);
24658 onLoad : function(ds, r, o)
24660 this.cursor = o.params.start ? o.params.start : 0;
24662 var d = this.getPageData(),
24667 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24668 this.field.dom.value = ap;
24669 this.first.setDisabled(ap == 1);
24670 this.prev.setDisabled(ap == 1);
24671 this.next.setDisabled(ap == ps);
24672 this.last.setDisabled(ap == ps);
24673 this.loading.enable();
24678 getPageData : function(){
24679 var total = this.ds.getTotalCount();
24682 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24683 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24688 onLoadError : function(){
24689 this.loading.enable();
24693 onPagingKeydown : function(e){
24694 var k = e.getKey();
24695 var d = this.getPageData();
24697 var v = this.field.dom.value, pageNum;
24698 if(!v || isNaN(pageNum = parseInt(v, 10))){
24699 this.field.dom.value = d.activePage;
24702 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24703 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24706 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))
24708 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24709 this.field.dom.value = pageNum;
24710 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24713 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24715 var v = this.field.dom.value, pageNum;
24716 var increment = (e.shiftKey) ? 10 : 1;
24717 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24720 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24721 this.field.dom.value = d.activePage;
24724 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24726 this.field.dom.value = parseInt(v, 10) + increment;
24727 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24728 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24735 beforeLoad : function(){
24737 this.loading.disable();
24742 onClick : function(which){
24751 ds.load({params:{start: 0, limit: this.pageSize}});
24754 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24757 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24760 var total = ds.getTotalCount();
24761 var extra = total % this.pageSize;
24762 var lastStart = extra ? (total - extra) : total-this.pageSize;
24763 ds.load({params:{start: lastStart, limit: this.pageSize}});
24766 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24772 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24773 * @param {Roo.data.Store} store The data store to unbind
24775 unbind : function(ds){
24776 ds.un("beforeload", this.beforeLoad, this);
24777 ds.un("load", this.onLoad, this);
24778 ds.un("loadexception", this.onLoadError, this);
24779 ds.un("remove", this.updateInfo, this);
24780 ds.un("add", this.updateInfo, this);
24781 this.ds = undefined;
24785 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24786 * @param {Roo.data.Store} store The data store to bind
24788 bind : function(ds){
24789 ds.on("beforeload", this.beforeLoad, this);
24790 ds.on("load", this.onLoad, this);
24791 ds.on("loadexception", this.onLoadError, this);
24792 ds.on("remove", this.updateInfo, this);
24793 ds.on("add", this.updateInfo, this);
24804 * @class Roo.bootstrap.MessageBar
24805 * @extends Roo.bootstrap.Component
24806 * Bootstrap MessageBar class
24807 * @cfg {String} html contents of the MessageBar
24808 * @cfg {String} weight (info | success | warning | danger) default info
24809 * @cfg {String} beforeClass insert the bar before the given class
24810 * @cfg {Boolean} closable (true | false) default false
24811 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24814 * Create a new Element
24815 * @param {Object} config The config object
24818 Roo.bootstrap.MessageBar = function(config){
24819 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24822 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24828 beforeClass: 'bootstrap-sticky-wrap',
24830 getAutoCreate : function(){
24834 cls: 'alert alert-dismissable alert-' + this.weight,
24839 html: this.html || ''
24845 cfg.cls += ' alert-messages-fixed';
24859 onRender : function(ct, position)
24861 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24864 var cfg = Roo.apply({}, this.getAutoCreate());
24868 cfg.cls += ' ' + this.cls;
24871 cfg.style = this.style;
24873 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24875 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24878 this.el.select('>button.close').on('click', this.hide, this);
24884 if (!this.rendered) {
24890 this.fireEvent('show', this);
24896 if (!this.rendered) {
24902 this.fireEvent('hide', this);
24905 update : function()
24907 // var e = this.el.dom.firstChild;
24909 // if(this.closable){
24910 // e = e.nextSibling;
24913 // e.data = this.html || '';
24915 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24931 * @class Roo.bootstrap.Graph
24932 * @extends Roo.bootstrap.Component
24933 * Bootstrap Graph class
24937 @cfg {String} graphtype bar | vbar | pie
24938 @cfg {number} g_x coodinator | centre x (pie)
24939 @cfg {number} g_y coodinator | centre y (pie)
24940 @cfg {number} g_r radius (pie)
24941 @cfg {number} g_height height of the chart (respected by all elements in the set)
24942 @cfg {number} g_width width of the chart (respected by all elements in the set)
24943 @cfg {Object} title The title of the chart
24946 -opts (object) options for the chart
24948 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24949 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24951 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.
24952 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24954 o stretch (boolean)
24956 -opts (object) options for the pie
24959 o startAngle (number)
24960 o endAngle (number)
24964 * Create a new Input
24965 * @param {Object} config The config object
24968 Roo.bootstrap.Graph = function(config){
24969 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24975 * The img click event for the img.
24976 * @param {Roo.EventObject} e
24982 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24993 //g_colors: this.colors,
25000 getAutoCreate : function(){
25011 onRender : function(ct,position){
25014 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25016 if (typeof(Raphael) == 'undefined') {
25017 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25021 this.raphael = Raphael(this.el.dom);
25023 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25024 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25025 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25026 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25028 r.text(160, 10, "Single Series Chart").attr(txtattr);
25029 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25030 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25031 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25033 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25034 r.barchart(330, 10, 300, 220, data1);
25035 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25036 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25039 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25040 // r.barchart(30, 30, 560, 250, xdata, {
25041 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25042 // axis : "0 0 1 1",
25043 // axisxlabels : xdata
25044 // //yvalues : cols,
25047 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25049 // this.load(null,xdata,{
25050 // axis : "0 0 1 1",
25051 // axisxlabels : xdata
25056 load : function(graphtype,xdata,opts)
25058 this.raphael.clear();
25060 graphtype = this.graphtype;
25065 var r = this.raphael,
25066 fin = function () {
25067 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25069 fout = function () {
25070 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25072 pfin = function() {
25073 this.sector.stop();
25074 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25077 this.label[0].stop();
25078 this.label[0].attr({ r: 7.5 });
25079 this.label[1].attr({ "font-weight": 800 });
25082 pfout = function() {
25083 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25086 this.label[0].animate({ r: 5 }, 500, "bounce");
25087 this.label[1].attr({ "font-weight": 400 });
25093 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25096 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25099 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25100 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25102 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25109 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25114 setTitle: function(o)
25119 initEvents: function() {
25122 this.el.on('click', this.onClick, this);
25126 onClick : function(e)
25128 Roo.log('img onclick');
25129 this.fireEvent('click', this, e);
25141 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25144 * @class Roo.bootstrap.dash.NumberBox
25145 * @extends Roo.bootstrap.Component
25146 * Bootstrap NumberBox class
25147 * @cfg {String} headline Box headline
25148 * @cfg {String} content Box content
25149 * @cfg {String} icon Box icon
25150 * @cfg {String} footer Footer text
25151 * @cfg {String} fhref Footer href
25154 * Create a new NumberBox
25155 * @param {Object} config The config object
25159 Roo.bootstrap.dash.NumberBox = function(config){
25160 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25164 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25173 getAutoCreate : function(){
25177 cls : 'small-box ',
25185 cls : 'roo-headline',
25186 html : this.headline
25190 cls : 'roo-content',
25191 html : this.content
25205 cls : 'ion ' + this.icon
25214 cls : 'small-box-footer',
25215 href : this.fhref || '#',
25219 cfg.cn.push(footer);
25226 onRender : function(ct,position){
25227 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25234 setHeadline: function (value)
25236 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25239 setFooter: function (value, href)
25241 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25244 this.el.select('a.small-box-footer',true).first().attr('href', href);
25249 setContent: function (value)
25251 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25254 initEvents: function()
25268 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25271 * @class Roo.bootstrap.dash.TabBox
25272 * @extends Roo.bootstrap.Component
25273 * Bootstrap TabBox class
25274 * @cfg {String} title Title of the TabBox
25275 * @cfg {String} icon Icon of the TabBox
25276 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25277 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25280 * Create a new TabBox
25281 * @param {Object} config The config object
25285 Roo.bootstrap.dash.TabBox = function(config){
25286 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25291 * When a pane is added
25292 * @param {Roo.bootstrap.dash.TabPane} pane
25296 * @event activatepane
25297 * When a pane is activated
25298 * @param {Roo.bootstrap.dash.TabPane} pane
25300 "activatepane" : true
25308 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25313 tabScrollable : false,
25315 getChildContainer : function()
25317 return this.el.select('.tab-content', true).first();
25320 getAutoCreate : function(){
25324 cls: 'pull-left header',
25332 cls: 'fa ' + this.icon
25338 cls: 'nav nav-tabs pull-right',
25344 if(this.tabScrollable){
25351 cls: 'nav nav-tabs pull-right',
25362 cls: 'nav-tabs-custom',
25367 cls: 'tab-content no-padding',
25375 initEvents : function()
25377 //Roo.log('add add pane handler');
25378 this.on('addpane', this.onAddPane, this);
25381 * Updates the box title
25382 * @param {String} html to set the title to.
25384 setTitle : function(value)
25386 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25388 onAddPane : function(pane)
25390 this.panes.push(pane);
25391 //Roo.log('addpane');
25393 // tabs are rendere left to right..
25394 if(!this.showtabs){
25398 var ctr = this.el.select('.nav-tabs', true).first();
25401 var existing = ctr.select('.nav-tab',true);
25402 var qty = existing.getCount();;
25405 var tab = ctr.createChild({
25407 cls : 'nav-tab' + (qty ? '' : ' active'),
25415 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25418 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25420 pane.el.addClass('active');
25425 onTabClick : function(ev,un,ob,pane)
25427 //Roo.log('tab - prev default');
25428 ev.preventDefault();
25431 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25432 pane.tab.addClass('active');
25433 //Roo.log(pane.title);
25434 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25435 // technically we should have a deactivate event.. but maybe add later.
25436 // and it should not de-activate the selected tab...
25437 this.fireEvent('activatepane', pane);
25438 pane.el.addClass('active');
25439 pane.fireEvent('activate');
25444 getActivePane : function()
25447 Roo.each(this.panes, function(p) {
25448 if(p.el.hasClass('active')){
25469 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25471 * @class Roo.bootstrap.TabPane
25472 * @extends Roo.bootstrap.Component
25473 * Bootstrap TabPane class
25474 * @cfg {Boolean} active (false | true) Default false
25475 * @cfg {String} title title of panel
25479 * Create a new TabPane
25480 * @param {Object} config The config object
25483 Roo.bootstrap.dash.TabPane = function(config){
25484 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25490 * When a pane is activated
25491 * @param {Roo.bootstrap.dash.TabPane} pane
25498 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25503 // the tabBox that this is attached to.
25506 getAutoCreate : function()
25514 cfg.cls += ' active';
25519 initEvents : function()
25521 //Roo.log('trigger add pane handler');
25522 this.parent().fireEvent('addpane', this)
25526 * Updates the tab title
25527 * @param {String} html to set the title to.
25529 setTitle: function(str)
25535 this.tab.select('a', true).first().dom.innerHTML = str;
25552 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25555 * @class Roo.bootstrap.menu.Menu
25556 * @extends Roo.bootstrap.Component
25557 * Bootstrap Menu class - container for Menu
25558 * @cfg {String} html Text of the menu
25559 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25560 * @cfg {String} icon Font awesome icon
25561 * @cfg {String} pos Menu align to (top | bottom) default bottom
25565 * Create a new Menu
25566 * @param {Object} config The config object
25570 Roo.bootstrap.menu.Menu = function(config){
25571 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25575 * @event beforeshow
25576 * Fires before this menu is displayed
25577 * @param {Roo.bootstrap.menu.Menu} this
25581 * @event beforehide
25582 * Fires before this menu is hidden
25583 * @param {Roo.bootstrap.menu.Menu} this
25588 * Fires after this menu is displayed
25589 * @param {Roo.bootstrap.menu.Menu} this
25594 * Fires after this menu is hidden
25595 * @param {Roo.bootstrap.menu.Menu} this
25600 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25601 * @param {Roo.bootstrap.menu.Menu} this
25602 * @param {Roo.EventObject} e
25609 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25613 weight : 'default',
25618 getChildContainer : function() {
25619 if(this.isSubMenu){
25623 return this.el.select('ul.dropdown-menu', true).first();
25626 getAutoCreate : function()
25631 cls : 'roo-menu-text',
25639 cls : 'fa ' + this.icon
25650 cls : 'dropdown-button btn btn-' + this.weight,
25655 cls : 'dropdown-toggle btn btn-' + this.weight,
25665 cls : 'dropdown-menu'
25671 if(this.pos == 'top'){
25672 cfg.cls += ' dropup';
25675 if(this.isSubMenu){
25678 cls : 'dropdown-menu'
25685 onRender : function(ct, position)
25687 this.isSubMenu = ct.hasClass('dropdown-submenu');
25689 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25692 initEvents : function()
25694 if(this.isSubMenu){
25698 this.hidden = true;
25700 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25701 this.triggerEl.on('click', this.onTriggerPress, this);
25703 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25704 this.buttonEl.on('click', this.onClick, this);
25710 if(this.isSubMenu){
25714 return this.el.select('ul.dropdown-menu', true).first();
25717 onClick : function(e)
25719 this.fireEvent("click", this, e);
25722 onTriggerPress : function(e)
25724 if (this.isVisible()) {
25731 isVisible : function(){
25732 return !this.hidden;
25737 this.fireEvent("beforeshow", this);
25739 this.hidden = false;
25740 this.el.addClass('open');
25742 Roo.get(document).on("mouseup", this.onMouseUp, this);
25744 this.fireEvent("show", this);
25751 this.fireEvent("beforehide", this);
25753 this.hidden = true;
25754 this.el.removeClass('open');
25756 Roo.get(document).un("mouseup", this.onMouseUp);
25758 this.fireEvent("hide", this);
25761 onMouseUp : function()
25775 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25778 * @class Roo.bootstrap.menu.Item
25779 * @extends Roo.bootstrap.Component
25780 * Bootstrap MenuItem class
25781 * @cfg {Boolean} submenu (true | false) default false
25782 * @cfg {String} html text of the item
25783 * @cfg {String} href the link
25784 * @cfg {Boolean} disable (true | false) default false
25785 * @cfg {Boolean} preventDefault (true | false) default true
25786 * @cfg {String} icon Font awesome icon
25787 * @cfg {String} pos Submenu align to (left | right) default right
25791 * Create a new Item
25792 * @param {Object} config The config object
25796 Roo.bootstrap.menu.Item = function(config){
25797 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25801 * Fires when the mouse is hovering over this menu
25802 * @param {Roo.bootstrap.menu.Item} this
25803 * @param {Roo.EventObject} e
25808 * Fires when the mouse exits this menu
25809 * @param {Roo.bootstrap.menu.Item} this
25810 * @param {Roo.EventObject} e
25816 * The raw click event for the entire grid.
25817 * @param {Roo.EventObject} e
25823 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25828 preventDefault: true,
25833 getAutoCreate : function()
25838 cls : 'roo-menu-item-text',
25846 cls : 'fa ' + this.icon
25855 href : this.href || '#',
25862 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25866 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25868 if(this.pos == 'left'){
25869 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25876 initEvents : function()
25878 this.el.on('mouseover', this.onMouseOver, this);
25879 this.el.on('mouseout', this.onMouseOut, this);
25881 this.el.select('a', true).first().on('click', this.onClick, this);
25885 onClick : function(e)
25887 if(this.preventDefault){
25888 e.preventDefault();
25891 this.fireEvent("click", this, e);
25894 onMouseOver : function(e)
25896 if(this.submenu && this.pos == 'left'){
25897 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25900 this.fireEvent("mouseover", this, e);
25903 onMouseOut : function(e)
25905 this.fireEvent("mouseout", this, e);
25917 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25920 * @class Roo.bootstrap.menu.Separator
25921 * @extends Roo.bootstrap.Component
25922 * Bootstrap Separator class
25925 * Create a new Separator
25926 * @param {Object} config The config object
25930 Roo.bootstrap.menu.Separator = function(config){
25931 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25934 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25936 getAutoCreate : function(){
25957 * @class Roo.bootstrap.Tooltip
25958 * Bootstrap Tooltip class
25959 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25960 * to determine which dom element triggers the tooltip.
25962 * It needs to add support for additional attributes like tooltip-position
25965 * Create a new Toolti
25966 * @param {Object} config The config object
25969 Roo.bootstrap.Tooltip = function(config){
25970 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25972 this.alignment = Roo.bootstrap.Tooltip.alignment;
25974 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25975 this.alignment = config.alignment;
25980 Roo.apply(Roo.bootstrap.Tooltip, {
25982 * @function init initialize tooltip monitoring.
25986 currentTip : false,
25987 currentRegion : false,
25993 Roo.get(document).on('mouseover', this.enter ,this);
25994 Roo.get(document).on('mouseout', this.leave, this);
25997 this.currentTip = new Roo.bootstrap.Tooltip();
26000 enter : function(ev)
26002 var dom = ev.getTarget();
26004 //Roo.log(['enter',dom]);
26005 var el = Roo.fly(dom);
26006 if (this.currentEl) {
26008 //Roo.log(this.currentEl);
26009 //Roo.log(this.currentEl.contains(dom));
26010 if (this.currentEl == el) {
26013 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26019 if (this.currentTip.el) {
26020 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26024 if(!el || el.dom == document){
26030 // you can not look for children, as if el is the body.. then everythign is the child..
26031 if (!el.attr('tooltip')) { //
26032 if (!el.select("[tooltip]").elements.length) {
26035 // is the mouse over this child...?
26036 bindEl = el.select("[tooltip]").first();
26037 var xy = ev.getXY();
26038 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26039 //Roo.log("not in region.");
26042 //Roo.log("child element over..");
26045 this.currentEl = bindEl;
26046 this.currentTip.bind(bindEl);
26047 this.currentRegion = Roo.lib.Region.getRegion(dom);
26048 this.currentTip.enter();
26051 leave : function(ev)
26053 var dom = ev.getTarget();
26054 //Roo.log(['leave',dom]);
26055 if (!this.currentEl) {
26060 if (dom != this.currentEl.dom) {
26063 var xy = ev.getXY();
26064 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26067 // only activate leave if mouse cursor is outside... bounding box..
26072 if (this.currentTip) {
26073 this.currentTip.leave();
26075 //Roo.log('clear currentEl');
26076 this.currentEl = false;
26081 'left' : ['r-l', [-2,0], 'right'],
26082 'right' : ['l-r', [2,0], 'left'],
26083 'bottom' : ['t-b', [0,2], 'top'],
26084 'top' : [ 'b-t', [0,-2], 'bottom']
26090 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26095 delay : null, // can be { show : 300 , hide: 500}
26099 hoverState : null, //???
26101 placement : 'bottom',
26105 getAutoCreate : function(){
26112 cls : 'tooltip-arrow'
26115 cls : 'tooltip-inner'
26122 bind : function(el)
26128 enter : function () {
26130 if (this.timeout != null) {
26131 clearTimeout(this.timeout);
26134 this.hoverState = 'in';
26135 //Roo.log("enter - show");
26136 if (!this.delay || !this.delay.show) {
26141 this.timeout = setTimeout(function () {
26142 if (_t.hoverState == 'in') {
26145 }, this.delay.show);
26149 clearTimeout(this.timeout);
26151 this.hoverState = 'out';
26152 if (!this.delay || !this.delay.hide) {
26158 this.timeout = setTimeout(function () {
26159 //Roo.log("leave - timeout");
26161 if (_t.hoverState == 'out') {
26163 Roo.bootstrap.Tooltip.currentEl = false;
26168 show : function (msg)
26171 this.render(document.body);
26174 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26176 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26178 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26180 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26182 var placement = typeof this.placement == 'function' ?
26183 this.placement.call(this, this.el, on_el) :
26186 var autoToken = /\s?auto?\s?/i;
26187 var autoPlace = autoToken.test(placement);
26189 placement = placement.replace(autoToken, '') || 'top';
26193 //this.el.setXY([0,0]);
26195 //this.el.dom.style.display='block';
26197 //this.el.appendTo(on_el);
26199 var p = this.getPosition();
26200 var box = this.el.getBox();
26206 var align = this.alignment[placement];
26208 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26210 if(placement == 'top' || placement == 'bottom'){
26212 placement = 'right';
26215 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26216 placement = 'left';
26219 var scroll = Roo.select('body', true).first().getScroll();
26221 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26225 align = this.alignment[placement];
26228 this.el.alignTo(this.bindEl, align[0],align[1]);
26229 //var arrow = this.el.select('.arrow',true).first();
26230 //arrow.set(align[2],
26232 this.el.addClass(placement);
26234 this.el.addClass('in fade');
26236 this.hoverState = null;
26238 if (this.el.hasClass('fade')) {
26249 //this.el.setXY([0,0]);
26250 this.el.removeClass('in');
26266 * @class Roo.bootstrap.LocationPicker
26267 * @extends Roo.bootstrap.Component
26268 * Bootstrap LocationPicker class
26269 * @cfg {Number} latitude Position when init default 0
26270 * @cfg {Number} longitude Position when init default 0
26271 * @cfg {Number} zoom default 15
26272 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26273 * @cfg {Boolean} mapTypeControl default false
26274 * @cfg {Boolean} disableDoubleClickZoom default false
26275 * @cfg {Boolean} scrollwheel default true
26276 * @cfg {Boolean} streetViewControl default false
26277 * @cfg {Number} radius default 0
26278 * @cfg {String} locationName
26279 * @cfg {Boolean} draggable default true
26280 * @cfg {Boolean} enableAutocomplete default false
26281 * @cfg {Boolean} enableReverseGeocode default true
26282 * @cfg {String} markerTitle
26285 * Create a new LocationPicker
26286 * @param {Object} config The config object
26290 Roo.bootstrap.LocationPicker = function(config){
26292 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26297 * Fires when the picker initialized.
26298 * @param {Roo.bootstrap.LocationPicker} this
26299 * @param {Google Location} location
26303 * @event positionchanged
26304 * Fires when the picker position changed.
26305 * @param {Roo.bootstrap.LocationPicker} this
26306 * @param {Google Location} location
26308 positionchanged : true,
26311 * Fires when the map resize.
26312 * @param {Roo.bootstrap.LocationPicker} this
26317 * Fires when the map show.
26318 * @param {Roo.bootstrap.LocationPicker} this
26323 * Fires when the map hide.
26324 * @param {Roo.bootstrap.LocationPicker} this
26329 * Fires when click the map.
26330 * @param {Roo.bootstrap.LocationPicker} this
26331 * @param {Map event} e
26335 * @event mapRightClick
26336 * Fires when right click the map.
26337 * @param {Roo.bootstrap.LocationPicker} this
26338 * @param {Map event} e
26340 mapRightClick : true,
26342 * @event markerClick
26343 * Fires when click the marker.
26344 * @param {Roo.bootstrap.LocationPicker} this
26345 * @param {Map event} e
26347 markerClick : true,
26349 * @event markerRightClick
26350 * Fires when right click the marker.
26351 * @param {Roo.bootstrap.LocationPicker} this
26352 * @param {Map event} e
26354 markerRightClick : true,
26356 * @event OverlayViewDraw
26357 * Fires when OverlayView Draw
26358 * @param {Roo.bootstrap.LocationPicker} this
26360 OverlayViewDraw : true,
26362 * @event OverlayViewOnAdd
26363 * Fires when OverlayView Draw
26364 * @param {Roo.bootstrap.LocationPicker} this
26366 OverlayViewOnAdd : true,
26368 * @event OverlayViewOnRemove
26369 * Fires when OverlayView Draw
26370 * @param {Roo.bootstrap.LocationPicker} this
26372 OverlayViewOnRemove : true,
26374 * @event OverlayViewShow
26375 * Fires when OverlayView Draw
26376 * @param {Roo.bootstrap.LocationPicker} this
26377 * @param {Pixel} cpx
26379 OverlayViewShow : true,
26381 * @event OverlayViewHide
26382 * Fires when OverlayView Draw
26383 * @param {Roo.bootstrap.LocationPicker} this
26385 OverlayViewHide : true,
26387 * @event loadexception
26388 * Fires when load google lib failed.
26389 * @param {Roo.bootstrap.LocationPicker} this
26391 loadexception : true
26396 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26398 gMapContext: false,
26404 mapTypeControl: false,
26405 disableDoubleClickZoom: false,
26407 streetViewControl: false,
26411 enableAutocomplete: false,
26412 enableReverseGeocode: true,
26415 getAutoCreate: function()
26420 cls: 'roo-location-picker'
26426 initEvents: function(ct, position)
26428 if(!this.el.getWidth() || this.isApplied()){
26432 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26437 initial: function()
26439 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26440 this.fireEvent('loadexception', this);
26444 if(!this.mapTypeId){
26445 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26448 this.gMapContext = this.GMapContext();
26450 this.initOverlayView();
26452 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26456 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26457 _this.setPosition(_this.gMapContext.marker.position);
26460 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26461 _this.fireEvent('mapClick', this, event);
26465 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26466 _this.fireEvent('mapRightClick', this, event);
26470 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26471 _this.fireEvent('markerClick', this, event);
26475 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26476 _this.fireEvent('markerRightClick', this, event);
26480 this.setPosition(this.gMapContext.location);
26482 this.fireEvent('initial', this, this.gMapContext.location);
26485 initOverlayView: function()
26489 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26493 _this.fireEvent('OverlayViewDraw', _this);
26498 _this.fireEvent('OverlayViewOnAdd', _this);
26501 onRemove: function()
26503 _this.fireEvent('OverlayViewOnRemove', _this);
26506 show: function(cpx)
26508 _this.fireEvent('OverlayViewShow', _this, cpx);
26513 _this.fireEvent('OverlayViewHide', _this);
26519 fromLatLngToContainerPixel: function(event)
26521 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26524 isApplied: function()
26526 return this.getGmapContext() == false ? false : true;
26529 getGmapContext: function()
26531 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26534 GMapContext: function()
26536 var position = new google.maps.LatLng(this.latitude, this.longitude);
26538 var _map = new google.maps.Map(this.el.dom, {
26541 mapTypeId: this.mapTypeId,
26542 mapTypeControl: this.mapTypeControl,
26543 disableDoubleClickZoom: this.disableDoubleClickZoom,
26544 scrollwheel: this.scrollwheel,
26545 streetViewControl: this.streetViewControl,
26546 locationName: this.locationName,
26547 draggable: this.draggable,
26548 enableAutocomplete: this.enableAutocomplete,
26549 enableReverseGeocode: this.enableReverseGeocode
26552 var _marker = new google.maps.Marker({
26553 position: position,
26555 title: this.markerTitle,
26556 draggable: this.draggable
26563 location: position,
26564 radius: this.radius,
26565 locationName: this.locationName,
26566 addressComponents: {
26567 formatted_address: null,
26568 addressLine1: null,
26569 addressLine2: null,
26571 streetNumber: null,
26575 stateOrProvince: null
26578 domContainer: this.el.dom,
26579 geodecoder: new google.maps.Geocoder()
26583 drawCircle: function(center, radius, options)
26585 if (this.gMapContext.circle != null) {
26586 this.gMapContext.circle.setMap(null);
26590 options = Roo.apply({}, options, {
26591 strokeColor: "#0000FF",
26592 strokeOpacity: .35,
26594 fillColor: "#0000FF",
26598 options.map = this.gMapContext.map;
26599 options.radius = radius;
26600 options.center = center;
26601 this.gMapContext.circle = new google.maps.Circle(options);
26602 return this.gMapContext.circle;
26608 setPosition: function(location)
26610 this.gMapContext.location = location;
26611 this.gMapContext.marker.setPosition(location);
26612 this.gMapContext.map.panTo(location);
26613 this.drawCircle(location, this.gMapContext.radius, {});
26617 if (this.gMapContext.settings.enableReverseGeocode) {
26618 this.gMapContext.geodecoder.geocode({
26619 latLng: this.gMapContext.location
26620 }, function(results, status) {
26622 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26623 _this.gMapContext.locationName = results[0].formatted_address;
26624 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26626 _this.fireEvent('positionchanged', this, location);
26633 this.fireEvent('positionchanged', this, location);
26638 google.maps.event.trigger(this.gMapContext.map, "resize");
26640 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26642 this.fireEvent('resize', this);
26645 setPositionByLatLng: function(latitude, longitude)
26647 this.setPosition(new google.maps.LatLng(latitude, longitude));
26650 getCurrentPosition: function()
26653 latitude: this.gMapContext.location.lat(),
26654 longitude: this.gMapContext.location.lng()
26658 getAddressName: function()
26660 return this.gMapContext.locationName;
26663 getAddressComponents: function()
26665 return this.gMapContext.addressComponents;
26668 address_component_from_google_geocode: function(address_components)
26672 for (var i = 0; i < address_components.length; i++) {
26673 var component = address_components[i];
26674 if (component.types.indexOf("postal_code") >= 0) {
26675 result.postalCode = component.short_name;
26676 } else if (component.types.indexOf("street_number") >= 0) {
26677 result.streetNumber = component.short_name;
26678 } else if (component.types.indexOf("route") >= 0) {
26679 result.streetName = component.short_name;
26680 } else if (component.types.indexOf("neighborhood") >= 0) {
26681 result.city = component.short_name;
26682 } else if (component.types.indexOf("locality") >= 0) {
26683 result.city = component.short_name;
26684 } else if (component.types.indexOf("sublocality") >= 0) {
26685 result.district = component.short_name;
26686 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26687 result.stateOrProvince = component.short_name;
26688 } else if (component.types.indexOf("country") >= 0) {
26689 result.country = component.short_name;
26693 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26694 result.addressLine2 = "";
26698 setZoomLevel: function(zoom)
26700 this.gMapContext.map.setZoom(zoom);
26713 this.fireEvent('show', this);
26724 this.fireEvent('hide', this);
26729 Roo.apply(Roo.bootstrap.LocationPicker, {
26731 OverlayView : function(map, options)
26733 options = options || {};
26747 * @class Roo.bootstrap.Alert
26748 * @extends Roo.bootstrap.Component
26749 * Bootstrap Alert class
26750 * @cfg {String} title The title of alert
26751 * @cfg {String} html The content of alert
26752 * @cfg {String} weight ( success | info | warning | danger )
26753 * @cfg {String} faicon font-awesomeicon
26756 * Create a new alert
26757 * @param {Object} config The config object
26761 Roo.bootstrap.Alert = function(config){
26762 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26766 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26773 getAutoCreate : function()
26782 cls : 'roo-alert-icon'
26787 cls : 'roo-alert-title',
26792 cls : 'roo-alert-text',
26799 cfg.cn[0].cls += ' fa ' + this.faicon;
26803 cfg.cls += ' alert-' + this.weight;
26809 initEvents: function()
26811 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26814 setTitle : function(str)
26816 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26819 setText : function(str)
26821 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26824 setWeight : function(weight)
26827 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26830 this.weight = weight;
26832 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26835 setIcon : function(icon)
26838 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26841 this.faicon = icon;
26843 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26864 * @class Roo.bootstrap.UploadCropbox
26865 * @extends Roo.bootstrap.Component
26866 * Bootstrap UploadCropbox class
26867 * @cfg {String} emptyText show when image has been loaded
26868 * @cfg {String} rotateNotify show when image too small to rotate
26869 * @cfg {Number} errorTimeout default 3000
26870 * @cfg {Number} minWidth default 300
26871 * @cfg {Number} minHeight default 300
26872 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26873 * @cfg {Boolean} isDocument (true|false) default false
26874 * @cfg {String} url action url
26875 * @cfg {String} paramName default 'imageUpload'
26876 * @cfg {String} method default POST
26877 * @cfg {Boolean} loadMask (true|false) default true
26878 * @cfg {Boolean} loadingText default 'Loading...'
26881 * Create a new UploadCropbox
26882 * @param {Object} config The config object
26885 Roo.bootstrap.UploadCropbox = function(config){
26886 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26890 * @event beforeselectfile
26891 * Fire before select file
26892 * @param {Roo.bootstrap.UploadCropbox} this
26894 "beforeselectfile" : true,
26897 * Fire after initEvent
26898 * @param {Roo.bootstrap.UploadCropbox} this
26903 * Fire after initEvent
26904 * @param {Roo.bootstrap.UploadCropbox} this
26905 * @param {String} data
26910 * Fire when preparing the file data
26911 * @param {Roo.bootstrap.UploadCropbox} this
26912 * @param {Object} file
26917 * Fire when get exception
26918 * @param {Roo.bootstrap.UploadCropbox} this
26919 * @param {XMLHttpRequest} xhr
26921 "exception" : true,
26923 * @event beforeloadcanvas
26924 * Fire before load the canvas
26925 * @param {Roo.bootstrap.UploadCropbox} this
26926 * @param {String} src
26928 "beforeloadcanvas" : true,
26931 * Fire when trash image
26932 * @param {Roo.bootstrap.UploadCropbox} this
26937 * Fire when download the image
26938 * @param {Roo.bootstrap.UploadCropbox} this
26942 * @event footerbuttonclick
26943 * Fire when footerbuttonclick
26944 * @param {Roo.bootstrap.UploadCropbox} this
26945 * @param {String} type
26947 "footerbuttonclick" : true,
26951 * @param {Roo.bootstrap.UploadCropbox} this
26956 * Fire when rotate the image
26957 * @param {Roo.bootstrap.UploadCropbox} this
26958 * @param {String} pos
26963 * Fire when inspect the file
26964 * @param {Roo.bootstrap.UploadCropbox} this
26965 * @param {Object} file
26970 * Fire when xhr upload the file
26971 * @param {Roo.bootstrap.UploadCropbox} this
26972 * @param {Object} data
26977 * Fire when arrange the file data
26978 * @param {Roo.bootstrap.UploadCropbox} this
26979 * @param {Object} formData
26984 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26987 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26989 emptyText : 'Click to upload image',
26990 rotateNotify : 'Image is too small to rotate',
26991 errorTimeout : 3000,
27005 cropType : 'image/jpeg',
27007 canvasLoaded : false,
27008 isDocument : false,
27010 paramName : 'imageUpload',
27012 loadingText : 'Loading...',
27015 getAutoCreate : function()
27019 cls : 'roo-upload-cropbox',
27023 cls : 'roo-upload-cropbox-selector',
27028 cls : 'roo-upload-cropbox-body',
27029 style : 'cursor:pointer',
27033 cls : 'roo-upload-cropbox-preview'
27037 cls : 'roo-upload-cropbox-thumb'
27041 cls : 'roo-upload-cropbox-empty-notify',
27042 html : this.emptyText
27046 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27047 html : this.rotateNotify
27053 cls : 'roo-upload-cropbox-footer',
27056 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27066 onRender : function(ct, position)
27068 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27070 if (this.buttons.length) {
27072 Roo.each(this.buttons, function(bb) {
27074 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27076 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27082 this.maskEl = this.el;
27086 initEvents : function()
27088 this.urlAPI = (window.createObjectURL && window) ||
27089 (window.URL && URL.revokeObjectURL && URL) ||
27090 (window.webkitURL && webkitURL);
27092 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27093 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27095 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27096 this.selectorEl.hide();
27098 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27099 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27101 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27102 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27103 this.thumbEl.hide();
27105 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27106 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27108 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27109 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27110 this.errorEl.hide();
27112 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27113 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27114 this.footerEl.hide();
27116 this.setThumbBoxSize();
27122 this.fireEvent('initial', this);
27129 window.addEventListener("resize", function() { _this.resize(); } );
27131 this.bodyEl.on('click', this.beforeSelectFile, this);
27134 this.bodyEl.on('touchstart', this.onTouchStart, this);
27135 this.bodyEl.on('touchmove', this.onTouchMove, this);
27136 this.bodyEl.on('touchend', this.onTouchEnd, this);
27140 this.bodyEl.on('mousedown', this.onMouseDown, this);
27141 this.bodyEl.on('mousemove', this.onMouseMove, this);
27142 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27143 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27144 Roo.get(document).on('mouseup', this.onMouseUp, this);
27147 this.selectorEl.on('change', this.onFileSelected, this);
27153 this.baseScale = 1;
27155 this.baseRotate = 1;
27156 this.dragable = false;
27157 this.pinching = false;
27160 this.cropData = false;
27161 this.notifyEl.dom.innerHTML = this.emptyText;
27163 this.selectorEl.dom.value = '';
27167 resize : function()
27169 if(this.fireEvent('resize', this) != false){
27170 this.setThumbBoxPosition();
27171 this.setCanvasPosition();
27175 onFooterButtonClick : function(e, el, o, type)
27178 case 'rotate-left' :
27179 this.onRotateLeft(e);
27181 case 'rotate-right' :
27182 this.onRotateRight(e);
27185 this.beforeSelectFile(e);
27200 this.fireEvent('footerbuttonclick', this, type);
27203 beforeSelectFile : function(e)
27205 e.preventDefault();
27207 if(this.fireEvent('beforeselectfile', this) != false){
27208 this.selectorEl.dom.click();
27212 onFileSelected : function(e)
27214 e.preventDefault();
27216 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27220 var file = this.selectorEl.dom.files[0];
27222 if(this.fireEvent('inspect', this, file) != false){
27223 this.prepare(file);
27228 trash : function(e)
27230 this.fireEvent('trash', this);
27233 download : function(e)
27235 this.fireEvent('download', this);
27238 loadCanvas : function(src)
27240 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27244 this.imageEl = document.createElement('img');
27248 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27250 this.imageEl.src = src;
27254 onLoadCanvas : function()
27256 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27257 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27259 this.bodyEl.un('click', this.beforeSelectFile, this);
27261 this.notifyEl.hide();
27262 this.thumbEl.show();
27263 this.footerEl.show();
27265 this.baseRotateLevel();
27267 if(this.isDocument){
27268 this.setThumbBoxSize();
27271 this.setThumbBoxPosition();
27273 this.baseScaleLevel();
27279 this.canvasLoaded = true;
27282 this.maskEl.unmask();
27287 setCanvasPosition : function()
27289 if(!this.canvasEl){
27293 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27294 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27296 this.previewEl.setLeft(pw);
27297 this.previewEl.setTop(ph);
27301 onMouseDown : function(e)
27305 this.dragable = true;
27306 this.pinching = false;
27308 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27309 this.dragable = false;
27313 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27314 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27318 onMouseMove : function(e)
27322 if(!this.canvasLoaded){
27326 if (!this.dragable){
27330 var minX = Math.ceil(this.thumbEl.getLeft(true));
27331 var minY = Math.ceil(this.thumbEl.getTop(true));
27333 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27334 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27336 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27337 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27339 x = x - this.mouseX;
27340 y = y - this.mouseY;
27342 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27343 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27345 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27346 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27348 this.previewEl.setLeft(bgX);
27349 this.previewEl.setTop(bgY);
27351 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27352 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27355 onMouseUp : function(e)
27359 this.dragable = false;
27362 onMouseWheel : function(e)
27366 this.startScale = this.scale;
27368 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27370 if(!this.zoomable()){
27371 this.scale = this.startScale;
27380 zoomable : function()
27382 var minScale = this.thumbEl.getWidth() / this.minWidth;
27384 if(this.minWidth < this.minHeight){
27385 minScale = this.thumbEl.getHeight() / this.minHeight;
27388 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27389 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27393 (this.rotate == 0 || this.rotate == 180) &&
27395 width > this.imageEl.OriginWidth ||
27396 height > this.imageEl.OriginHeight ||
27397 (width < this.minWidth && height < this.minHeight)
27405 (this.rotate == 90 || this.rotate == 270) &&
27407 width > this.imageEl.OriginWidth ||
27408 height > this.imageEl.OriginHeight ||
27409 (width < this.minHeight && height < this.minWidth)
27416 !this.isDocument &&
27417 (this.rotate == 0 || this.rotate == 180) &&
27419 width < this.minWidth ||
27420 width > this.imageEl.OriginWidth ||
27421 height < this.minHeight ||
27422 height > this.imageEl.OriginHeight
27429 !this.isDocument &&
27430 (this.rotate == 90 || this.rotate == 270) &&
27432 width < this.minHeight ||
27433 width > this.imageEl.OriginWidth ||
27434 height < this.minWidth ||
27435 height > this.imageEl.OriginHeight
27445 onRotateLeft : function(e)
27447 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27449 var minScale = this.thumbEl.getWidth() / this.minWidth;
27451 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27452 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27454 this.startScale = this.scale;
27456 while (this.getScaleLevel() < minScale){
27458 this.scale = this.scale + 1;
27460 if(!this.zoomable()){
27465 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27466 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27471 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27478 this.scale = this.startScale;
27480 this.onRotateFail();
27485 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27487 if(this.isDocument){
27488 this.setThumbBoxSize();
27489 this.setThumbBoxPosition();
27490 this.setCanvasPosition();
27495 this.fireEvent('rotate', this, 'left');
27499 onRotateRight : function(e)
27501 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27503 var minScale = this.thumbEl.getWidth() / this.minWidth;
27505 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27506 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27508 this.startScale = this.scale;
27510 while (this.getScaleLevel() < minScale){
27512 this.scale = this.scale + 1;
27514 if(!this.zoomable()){
27519 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27520 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27525 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27532 this.scale = this.startScale;
27534 this.onRotateFail();
27539 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27541 if(this.isDocument){
27542 this.setThumbBoxSize();
27543 this.setThumbBoxPosition();
27544 this.setCanvasPosition();
27549 this.fireEvent('rotate', this, 'right');
27552 onRotateFail : function()
27554 this.errorEl.show(true);
27558 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27563 this.previewEl.dom.innerHTML = '';
27565 var canvasEl = document.createElement("canvas");
27567 var contextEl = canvasEl.getContext("2d");
27569 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27570 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27571 var center = this.imageEl.OriginWidth / 2;
27573 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27574 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27575 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27576 center = this.imageEl.OriginHeight / 2;
27579 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27581 contextEl.translate(center, center);
27582 contextEl.rotate(this.rotate * Math.PI / 180);
27584 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27586 this.canvasEl = document.createElement("canvas");
27588 this.contextEl = this.canvasEl.getContext("2d");
27590 switch (this.rotate) {
27593 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27594 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27596 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27601 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27602 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27604 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27605 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);
27609 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27614 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27615 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27617 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27618 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);
27622 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);
27627 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27628 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27630 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27631 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27635 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);
27642 this.previewEl.appendChild(this.canvasEl);
27644 this.setCanvasPosition();
27649 if(!this.canvasLoaded){
27653 var imageCanvas = document.createElement("canvas");
27655 var imageContext = imageCanvas.getContext("2d");
27657 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27658 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27660 var center = imageCanvas.width / 2;
27662 imageContext.translate(center, center);
27664 imageContext.rotate(this.rotate * Math.PI / 180);
27666 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27668 var canvas = document.createElement("canvas");
27670 var context = canvas.getContext("2d");
27672 canvas.width = this.minWidth;
27673 canvas.height = this.minHeight;
27675 switch (this.rotate) {
27678 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27679 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27681 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27682 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27684 var targetWidth = this.minWidth - 2 * x;
27685 var targetHeight = this.minHeight - 2 * y;
27689 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27690 scale = targetWidth / width;
27693 if(x > 0 && y == 0){
27694 scale = targetHeight / height;
27697 if(x > 0 && y > 0){
27698 scale = targetWidth / width;
27700 if(width < height){
27701 scale = targetHeight / height;
27705 context.scale(scale, scale);
27707 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27708 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27710 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27711 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27713 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27718 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27719 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27721 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27722 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27724 var targetWidth = this.minWidth - 2 * x;
27725 var targetHeight = this.minHeight - 2 * y;
27729 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27730 scale = targetWidth / width;
27733 if(x > 0 && y == 0){
27734 scale = targetHeight / height;
27737 if(x > 0 && y > 0){
27738 scale = targetWidth / width;
27740 if(width < height){
27741 scale = targetHeight / height;
27745 context.scale(scale, scale);
27747 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27748 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27750 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27751 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27753 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27755 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
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 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27796 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27798 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27803 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27804 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27806 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27807 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27809 var targetWidth = this.minWidth - 2 * x;
27810 var targetHeight = this.minHeight - 2 * y;
27814 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27815 scale = targetWidth / width;
27818 if(x > 0 && y == 0){
27819 scale = targetHeight / height;
27822 if(x > 0 && y > 0){
27823 scale = targetWidth / width;
27825 if(width < height){
27826 scale = targetHeight / height;
27830 context.scale(scale, scale);
27832 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27833 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27835 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27836 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27838 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27840 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27847 this.cropData = canvas.toDataURL(this.cropType);
27849 if(this.fireEvent('crop', this, this.cropData) !== false){
27850 this.process(this.file, this.cropData);
27857 setThumbBoxSize : function()
27861 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27862 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27863 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27865 this.minWidth = width;
27866 this.minHeight = height;
27868 if(this.rotate == 90 || this.rotate == 270){
27869 this.minWidth = height;
27870 this.minHeight = width;
27875 width = Math.ceil(this.minWidth * height / this.minHeight);
27877 if(this.minWidth > this.minHeight){
27879 height = Math.ceil(this.minHeight * width / this.minWidth);
27882 this.thumbEl.setStyle({
27883 width : width + 'px',
27884 height : height + 'px'
27891 setThumbBoxPosition : function()
27893 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27894 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27896 this.thumbEl.setLeft(x);
27897 this.thumbEl.setTop(y);
27901 baseRotateLevel : function()
27903 this.baseRotate = 1;
27906 typeof(this.exif) != 'undefined' &&
27907 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27908 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27910 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27913 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27917 baseScaleLevel : function()
27921 if(this.isDocument){
27923 if(this.baseRotate == 6 || this.baseRotate == 8){
27925 height = this.thumbEl.getHeight();
27926 this.baseScale = height / this.imageEl.OriginWidth;
27928 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27929 width = this.thumbEl.getWidth();
27930 this.baseScale = width / this.imageEl.OriginHeight;
27936 height = this.thumbEl.getHeight();
27937 this.baseScale = height / this.imageEl.OriginHeight;
27939 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27940 width = this.thumbEl.getWidth();
27941 this.baseScale = width / this.imageEl.OriginWidth;
27947 if(this.baseRotate == 6 || this.baseRotate == 8){
27949 width = this.thumbEl.getHeight();
27950 this.baseScale = width / this.imageEl.OriginHeight;
27952 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27953 height = this.thumbEl.getWidth();
27954 this.baseScale = height / this.imageEl.OriginHeight;
27957 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27958 height = this.thumbEl.getWidth();
27959 this.baseScale = height / this.imageEl.OriginHeight;
27961 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27962 width = this.thumbEl.getHeight();
27963 this.baseScale = width / this.imageEl.OriginWidth;
27970 width = this.thumbEl.getWidth();
27971 this.baseScale = width / this.imageEl.OriginWidth;
27973 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27974 height = this.thumbEl.getHeight();
27975 this.baseScale = height / this.imageEl.OriginHeight;
27978 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27980 height = this.thumbEl.getHeight();
27981 this.baseScale = height / this.imageEl.OriginHeight;
27983 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27984 width = this.thumbEl.getWidth();
27985 this.baseScale = width / this.imageEl.OriginWidth;
27993 getScaleLevel : function()
27995 return this.baseScale * Math.pow(1.1, this.scale);
27998 onTouchStart : function(e)
28000 if(!this.canvasLoaded){
28001 this.beforeSelectFile(e);
28005 var touches = e.browserEvent.touches;
28011 if(touches.length == 1){
28012 this.onMouseDown(e);
28016 if(touches.length != 2){
28022 for(var i = 0, finger; finger = touches[i]; i++){
28023 coords.push(finger.pageX, finger.pageY);
28026 var x = Math.pow(coords[0] - coords[2], 2);
28027 var y = Math.pow(coords[1] - coords[3], 2);
28029 this.startDistance = Math.sqrt(x + y);
28031 this.startScale = this.scale;
28033 this.pinching = true;
28034 this.dragable = false;
28038 onTouchMove : function(e)
28040 if(!this.pinching && !this.dragable){
28044 var touches = e.browserEvent.touches;
28051 this.onMouseMove(e);
28057 for(var i = 0, finger; finger = touches[i]; i++){
28058 coords.push(finger.pageX, finger.pageY);
28061 var x = Math.pow(coords[0] - coords[2], 2);
28062 var y = Math.pow(coords[1] - coords[3], 2);
28064 this.endDistance = Math.sqrt(x + y);
28066 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28068 if(!this.zoomable()){
28069 this.scale = this.startScale;
28077 onTouchEnd : function(e)
28079 this.pinching = false;
28080 this.dragable = false;
28084 process : function(file, crop)
28087 this.maskEl.mask(this.loadingText);
28090 this.xhr = new XMLHttpRequest();
28092 file.xhr = this.xhr;
28094 this.xhr.open(this.method, this.url, true);
28097 "Accept": "application/json",
28098 "Cache-Control": "no-cache",
28099 "X-Requested-With": "XMLHttpRequest"
28102 for (var headerName in headers) {
28103 var headerValue = headers[headerName];
28105 this.xhr.setRequestHeader(headerName, headerValue);
28111 this.xhr.onload = function()
28113 _this.xhrOnLoad(_this.xhr);
28116 this.xhr.onerror = function()
28118 _this.xhrOnError(_this.xhr);
28121 var formData = new FormData();
28123 formData.append('returnHTML', 'NO');
28126 formData.append('crop', crop);
28129 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28130 formData.append(this.paramName, file, file.name);
28133 if(typeof(file.filename) != 'undefined'){
28134 formData.append('filename', file.filename);
28137 if(typeof(file.mimetype) != 'undefined'){
28138 formData.append('mimetype', file.mimetype);
28141 if(this.fireEvent('arrange', this, formData) != false){
28142 this.xhr.send(formData);
28146 xhrOnLoad : function(xhr)
28149 this.maskEl.unmask();
28152 if (xhr.readyState !== 4) {
28153 this.fireEvent('exception', this, xhr);
28157 var response = Roo.decode(xhr.responseText);
28159 if(!response.success){
28160 this.fireEvent('exception', this, xhr);
28164 var response = Roo.decode(xhr.responseText);
28166 this.fireEvent('upload', this, response);
28170 xhrOnError : function()
28173 this.maskEl.unmask();
28176 Roo.log('xhr on error');
28178 var response = Roo.decode(xhr.responseText);
28184 prepare : function(file)
28187 this.maskEl.mask(this.loadingText);
28193 if(typeof(file) === 'string'){
28194 this.loadCanvas(file);
28198 if(!file || !this.urlAPI){
28203 this.cropType = file.type;
28207 if(this.fireEvent('prepare', this, this.file) != false){
28209 var reader = new FileReader();
28211 reader.onload = function (e) {
28212 if (e.target.error) {
28213 Roo.log(e.target.error);
28217 var buffer = e.target.result,
28218 dataView = new DataView(buffer),
28220 maxOffset = dataView.byteLength - 4,
28224 if (dataView.getUint16(0) === 0xffd8) {
28225 while (offset < maxOffset) {
28226 markerBytes = dataView.getUint16(offset);
28228 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28229 markerLength = dataView.getUint16(offset + 2) + 2;
28230 if (offset + markerLength > dataView.byteLength) {
28231 Roo.log('Invalid meta data: Invalid segment size.');
28235 if(markerBytes == 0xffe1){
28236 _this.parseExifData(
28243 offset += markerLength;
28253 var url = _this.urlAPI.createObjectURL(_this.file);
28255 _this.loadCanvas(url);
28260 reader.readAsArrayBuffer(this.file);
28266 parseExifData : function(dataView, offset, length)
28268 var tiffOffset = offset + 10,
28272 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28273 // No Exif data, might be XMP data instead
28277 // Check for the ASCII code for "Exif" (0x45786966):
28278 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28279 // No Exif data, might be XMP data instead
28282 if (tiffOffset + 8 > dataView.byteLength) {
28283 Roo.log('Invalid Exif data: Invalid segment size.');
28286 // Check for the two null bytes:
28287 if (dataView.getUint16(offset + 8) !== 0x0000) {
28288 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28291 // Check the byte alignment:
28292 switch (dataView.getUint16(tiffOffset)) {
28294 littleEndian = true;
28297 littleEndian = false;
28300 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28303 // Check for the TIFF tag marker (0x002A):
28304 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28305 Roo.log('Invalid Exif data: Missing TIFF marker.');
28308 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28309 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28311 this.parseExifTags(
28314 tiffOffset + dirOffset,
28319 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28324 if (dirOffset + 6 > dataView.byteLength) {
28325 Roo.log('Invalid Exif data: Invalid directory offset.');
28328 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28329 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28330 if (dirEndOffset + 4 > dataView.byteLength) {
28331 Roo.log('Invalid Exif data: Invalid directory size.');
28334 for (i = 0; i < tagsNumber; i += 1) {
28338 dirOffset + 2 + 12 * i, // tag offset
28342 // Return the offset to the next directory:
28343 return dataView.getUint32(dirEndOffset, littleEndian);
28346 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28348 var tag = dataView.getUint16(offset, littleEndian);
28350 this.exif[tag] = this.getExifValue(
28354 dataView.getUint16(offset + 2, littleEndian), // tag type
28355 dataView.getUint32(offset + 4, littleEndian), // tag length
28360 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28362 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28371 Roo.log('Invalid Exif data: Invalid tag type.');
28375 tagSize = tagType.size * length;
28376 // Determine if the value is contained in the dataOffset bytes,
28377 // or if the value at the dataOffset is a pointer to the actual data:
28378 dataOffset = tagSize > 4 ?
28379 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28380 if (dataOffset + tagSize > dataView.byteLength) {
28381 Roo.log('Invalid Exif data: Invalid data offset.');
28384 if (length === 1) {
28385 return tagType.getValue(dataView, dataOffset, littleEndian);
28388 for (i = 0; i < length; i += 1) {
28389 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28392 if (tagType.ascii) {
28394 // Concatenate the chars:
28395 for (i = 0; i < values.length; i += 1) {
28397 // Ignore the terminating NULL byte(s):
28398 if (c === '\u0000') {
28410 Roo.apply(Roo.bootstrap.UploadCropbox, {
28412 'Orientation': 0x0112
28416 1: 0, //'top-left',
28418 3: 180, //'bottom-right',
28419 // 4: 'bottom-left',
28421 6: 90, //'right-top',
28422 // 7: 'right-bottom',
28423 8: 270 //'left-bottom'
28427 // byte, 8-bit unsigned int:
28429 getValue: function (dataView, dataOffset) {
28430 return dataView.getUint8(dataOffset);
28434 // ascii, 8-bit byte:
28436 getValue: function (dataView, dataOffset) {
28437 return String.fromCharCode(dataView.getUint8(dataOffset));
28442 // short, 16 bit int:
28444 getValue: function (dataView, dataOffset, littleEndian) {
28445 return dataView.getUint16(dataOffset, littleEndian);
28449 // long, 32 bit int:
28451 getValue: function (dataView, dataOffset, littleEndian) {
28452 return dataView.getUint32(dataOffset, littleEndian);
28456 // rational = two long values, first is numerator, second is denominator:
28458 getValue: function (dataView, dataOffset, littleEndian) {
28459 return dataView.getUint32(dataOffset, littleEndian) /
28460 dataView.getUint32(dataOffset + 4, littleEndian);
28464 // slong, 32 bit signed int:
28466 getValue: function (dataView, dataOffset, littleEndian) {
28467 return dataView.getInt32(dataOffset, littleEndian);
28471 // srational, two slongs, first is numerator, second is denominator:
28473 getValue: function (dataView, dataOffset, littleEndian) {
28474 return dataView.getInt32(dataOffset, littleEndian) /
28475 dataView.getInt32(dataOffset + 4, littleEndian);
28485 cls : 'btn-group roo-upload-cropbox-rotate-left',
28486 action : 'rotate-left',
28490 cls : 'btn btn-default',
28491 html : '<i class="fa fa-undo"></i>'
28497 cls : 'btn-group roo-upload-cropbox-picture',
28498 action : 'picture',
28502 cls : 'btn btn-default',
28503 html : '<i class="fa fa-picture-o"></i>'
28509 cls : 'btn-group roo-upload-cropbox-rotate-right',
28510 action : 'rotate-right',
28514 cls : 'btn btn-default',
28515 html : '<i class="fa fa-repeat"></i>'
28523 cls : 'btn-group roo-upload-cropbox-rotate-left',
28524 action : 'rotate-left',
28528 cls : 'btn btn-default',
28529 html : '<i class="fa fa-undo"></i>'
28535 cls : 'btn-group roo-upload-cropbox-download',
28536 action : 'download',
28540 cls : 'btn btn-default',
28541 html : '<i class="fa fa-download"></i>'
28547 cls : 'btn-group roo-upload-cropbox-crop',
28552 cls : 'btn btn-default',
28553 html : '<i class="fa fa-crop"></i>'
28559 cls : 'btn-group roo-upload-cropbox-trash',
28564 cls : 'btn btn-default',
28565 html : '<i class="fa fa-trash"></i>'
28571 cls : 'btn-group roo-upload-cropbox-rotate-right',
28572 action : 'rotate-right',
28576 cls : 'btn btn-default',
28577 html : '<i class="fa fa-repeat"></i>'
28585 cls : 'btn-group roo-upload-cropbox-rotate-left',
28586 action : 'rotate-left',
28590 cls : 'btn btn-default',
28591 html : '<i class="fa fa-undo"></i>'
28597 cls : 'btn-group roo-upload-cropbox-rotate-right',
28598 action : 'rotate-right',
28602 cls : 'btn btn-default',
28603 html : '<i class="fa fa-repeat"></i>'
28616 * @class Roo.bootstrap.DocumentManager
28617 * @extends Roo.bootstrap.Component
28618 * Bootstrap DocumentManager class
28619 * @cfg {String} paramName default 'imageUpload'
28620 * @cfg {String} toolTipName default 'filename'
28621 * @cfg {String} method default POST
28622 * @cfg {String} url action url
28623 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28624 * @cfg {Boolean} multiple multiple upload default true
28625 * @cfg {Number} thumbSize default 300
28626 * @cfg {String} fieldLabel
28627 * @cfg {Number} labelWidth default 4
28628 * @cfg {String} labelAlign (left|top) default left
28629 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28630 * @cfg {Number} labellg set the width of label (1-12)
28631 * @cfg {Number} labelmd set the width of label (1-12)
28632 * @cfg {Number} labelsm set the width of label (1-12)
28633 * @cfg {Number} labelxs set the width of label (1-12)
28636 * Create a new DocumentManager
28637 * @param {Object} config The config object
28640 Roo.bootstrap.DocumentManager = function(config){
28641 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28644 this.delegates = [];
28649 * Fire when initial the DocumentManager
28650 * @param {Roo.bootstrap.DocumentManager} this
28655 * inspect selected file
28656 * @param {Roo.bootstrap.DocumentManager} this
28657 * @param {File} file
28662 * Fire when xhr load exception
28663 * @param {Roo.bootstrap.DocumentManager} this
28664 * @param {XMLHttpRequest} xhr
28666 "exception" : true,
28668 * @event afterupload
28669 * Fire when xhr load exception
28670 * @param {Roo.bootstrap.DocumentManager} this
28671 * @param {XMLHttpRequest} xhr
28673 "afterupload" : true,
28676 * prepare the form data
28677 * @param {Roo.bootstrap.DocumentManager} this
28678 * @param {Object} formData
28683 * Fire when remove the file
28684 * @param {Roo.bootstrap.DocumentManager} this
28685 * @param {Object} file
28690 * Fire after refresh the file
28691 * @param {Roo.bootstrap.DocumentManager} this
28696 * Fire after click the image
28697 * @param {Roo.bootstrap.DocumentManager} this
28698 * @param {Object} file
28703 * Fire when upload a image and editable set to true
28704 * @param {Roo.bootstrap.DocumentManager} this
28705 * @param {Object} file
28709 * @event beforeselectfile
28710 * Fire before select file
28711 * @param {Roo.bootstrap.DocumentManager} this
28713 "beforeselectfile" : true,
28716 * Fire before process file
28717 * @param {Roo.bootstrap.DocumentManager} this
28718 * @param {Object} file
28722 * @event previewrendered
28723 * Fire when preview rendered
28724 * @param {Roo.bootstrap.DocumentManager} this
28725 * @param {Object} file
28727 "previewrendered" : true,
28730 "previewResize" : true
28735 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28744 paramName : 'imageUpload',
28745 toolTipName : 'filename',
28748 labelAlign : 'left',
28758 getAutoCreate : function()
28760 var managerWidget = {
28762 cls : 'roo-document-manager',
28766 cls : 'roo-document-manager-selector',
28771 cls : 'roo-document-manager-uploader',
28775 cls : 'roo-document-manager-upload-btn',
28776 html : '<i class="fa fa-plus"></i>'
28787 cls : 'column col-md-12',
28792 if(this.fieldLabel.length){
28797 cls : 'column col-md-12',
28798 html : this.fieldLabel
28802 cls : 'column col-md-12',
28807 if(this.labelAlign == 'left'){
28812 html : this.fieldLabel
28821 if(this.labelWidth > 12){
28822 content[0].style = "width: " + this.labelWidth + 'px';
28825 if(this.labelWidth < 13 && this.labelmd == 0){
28826 this.labelmd = this.labelWidth;
28829 if(this.labellg > 0){
28830 content[0].cls += ' col-lg-' + this.labellg;
28831 content[1].cls += ' col-lg-' + (12 - this.labellg);
28834 if(this.labelmd > 0){
28835 content[0].cls += ' col-md-' + this.labelmd;
28836 content[1].cls += ' col-md-' + (12 - this.labelmd);
28839 if(this.labelsm > 0){
28840 content[0].cls += ' col-sm-' + this.labelsm;
28841 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28844 if(this.labelxs > 0){
28845 content[0].cls += ' col-xs-' + this.labelxs;
28846 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28854 cls : 'row clearfix',
28862 initEvents : function()
28864 this.managerEl = this.el.select('.roo-document-manager', true).first();
28865 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28867 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28868 this.selectorEl.hide();
28871 this.selectorEl.attr('multiple', 'multiple');
28874 this.selectorEl.on('change', this.onFileSelected, this);
28876 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28877 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28879 this.uploader.on('click', this.onUploaderClick, this);
28881 this.renderProgressDialog();
28885 window.addEventListener("resize", function() { _this.refresh(); } );
28887 this.fireEvent('initial', this);
28890 renderProgressDialog : function()
28894 this.progressDialog = new Roo.bootstrap.Modal({
28895 cls : 'roo-document-manager-progress-dialog',
28896 allow_close : false,
28906 btnclick : function() {
28907 _this.uploadCancel();
28913 this.progressDialog.render(Roo.get(document.body));
28915 this.progress = new Roo.bootstrap.Progress({
28916 cls : 'roo-document-manager-progress',
28921 this.progress.render(this.progressDialog.getChildContainer());
28923 this.progressBar = new Roo.bootstrap.ProgressBar({
28924 cls : 'roo-document-manager-progress-bar',
28927 aria_valuemax : 12,
28931 this.progressBar.render(this.progress.getChildContainer());
28934 onUploaderClick : function(e)
28936 e.preventDefault();
28938 if(this.fireEvent('beforeselectfile', this) != false){
28939 this.selectorEl.dom.click();
28944 onFileSelected : function(e)
28946 e.preventDefault();
28948 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28952 Roo.each(this.selectorEl.dom.files, function(file){
28953 if(this.fireEvent('inspect', this, file) != false){
28954 this.files.push(file);
28964 this.selectorEl.dom.value = '';
28966 if(!this.files || !this.files.length){
28970 if(this.boxes > 0 && this.files.length > this.boxes){
28971 this.files = this.files.slice(0, this.boxes);
28974 this.uploader.show();
28976 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28977 this.uploader.hide();
28986 Roo.each(this.files, function(file){
28988 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28989 var f = this.renderPreview(file);
28994 if(file.type.indexOf('image') != -1){
28995 this.delegates.push(
28997 _this.process(file);
28998 }).createDelegate(this)
29006 _this.process(file);
29007 }).createDelegate(this)
29012 this.files = files;
29014 this.delegates = this.delegates.concat(docs);
29016 if(!this.delegates.length){
29021 this.progressBar.aria_valuemax = this.delegates.length;
29028 arrange : function()
29030 if(!this.delegates.length){
29031 this.progressDialog.hide();
29036 var delegate = this.delegates.shift();
29038 this.progressDialog.show();
29040 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29042 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29047 refresh : function()
29049 this.uploader.show();
29051 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29052 this.uploader.hide();
29055 Roo.isTouch ? this.closable(false) : this.closable(true);
29057 this.fireEvent('refresh', this);
29060 onRemove : function(e, el, o)
29062 e.preventDefault();
29064 this.fireEvent('remove', this, o);
29068 remove : function(o)
29072 Roo.each(this.files, function(file){
29073 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29082 this.files = files;
29089 Roo.each(this.files, function(file){
29094 file.target.remove();
29103 onClick : function(e, el, o)
29105 e.preventDefault();
29107 this.fireEvent('click', this, o);
29111 closable : function(closable)
29113 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29115 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29127 xhrOnLoad : function(xhr)
29129 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29133 if (xhr.readyState !== 4) {
29135 this.fireEvent('exception', this, xhr);
29139 var response = Roo.decode(xhr.responseText);
29141 if(!response.success){
29143 this.fireEvent('exception', this, xhr);
29147 var file = this.renderPreview(response.data);
29149 this.files.push(file);
29153 this.fireEvent('afterupload', this, xhr);
29157 xhrOnError : function(xhr)
29159 Roo.log('xhr on error');
29161 var response = Roo.decode(xhr.responseText);
29168 process : function(file)
29170 if(this.fireEvent('process', this, file) !== false){
29171 if(this.editable && file.type.indexOf('image') != -1){
29172 this.fireEvent('edit', this, file);
29176 this.uploadStart(file, false);
29183 uploadStart : function(file, crop)
29185 this.xhr = new XMLHttpRequest();
29187 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29192 file.xhr = this.xhr;
29194 this.managerEl.createChild({
29196 cls : 'roo-document-manager-loading',
29200 tooltip : file.name,
29201 cls : 'roo-document-manager-thumb',
29202 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29208 this.xhr.open(this.method, this.url, true);
29211 "Accept": "application/json",
29212 "Cache-Control": "no-cache",
29213 "X-Requested-With": "XMLHttpRequest"
29216 for (var headerName in headers) {
29217 var headerValue = headers[headerName];
29219 this.xhr.setRequestHeader(headerName, headerValue);
29225 this.xhr.onload = function()
29227 _this.xhrOnLoad(_this.xhr);
29230 this.xhr.onerror = function()
29232 _this.xhrOnError(_this.xhr);
29235 var formData = new FormData();
29237 formData.append('returnHTML', 'NO');
29240 formData.append('crop', crop);
29243 formData.append(this.paramName, file, file.name);
29250 if(this.fireEvent('prepare', this, formData, options) != false){
29252 if(options.manually){
29256 this.xhr.send(formData);
29260 this.uploadCancel();
29263 uploadCancel : function()
29269 this.delegates = [];
29271 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29278 renderPreview : function(file)
29280 if(typeof(file.target) != 'undefined' && file.target){
29284 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29286 var previewEl = this.managerEl.createChild({
29288 cls : 'roo-document-manager-preview',
29292 tooltip : file[this.toolTipName],
29293 cls : 'roo-document-manager-thumb',
29294 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29299 html : '<i class="fa fa-times-circle"></i>'
29304 var close = previewEl.select('button.close', true).first();
29306 close.on('click', this.onRemove, this, file);
29308 file.target = previewEl;
29310 var image = previewEl.select('img', true).first();
29314 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29316 image.on('click', this.onClick, this, file);
29318 this.fireEvent('previewrendered', this, file);
29324 onPreviewLoad : function(file, image)
29326 if(typeof(file.target) == 'undefined' || !file.target){
29330 var width = image.dom.naturalWidth || image.dom.width;
29331 var height = image.dom.naturalHeight || image.dom.height;
29333 if(!this.previewResize) {
29337 if(width > height){
29338 file.target.addClass('wide');
29342 file.target.addClass('tall');
29347 uploadFromSource : function(file, crop)
29349 this.xhr = new XMLHttpRequest();
29351 this.managerEl.createChild({
29353 cls : 'roo-document-manager-loading',
29357 tooltip : file.name,
29358 cls : 'roo-document-manager-thumb',
29359 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29365 this.xhr.open(this.method, this.url, true);
29368 "Accept": "application/json",
29369 "Cache-Control": "no-cache",
29370 "X-Requested-With": "XMLHttpRequest"
29373 for (var headerName in headers) {
29374 var headerValue = headers[headerName];
29376 this.xhr.setRequestHeader(headerName, headerValue);
29382 this.xhr.onload = function()
29384 _this.xhrOnLoad(_this.xhr);
29387 this.xhr.onerror = function()
29389 _this.xhrOnError(_this.xhr);
29392 var formData = new FormData();
29394 formData.append('returnHTML', 'NO');
29396 formData.append('crop', crop);
29398 if(typeof(file.filename) != 'undefined'){
29399 formData.append('filename', file.filename);
29402 if(typeof(file.mimetype) != 'undefined'){
29403 formData.append('mimetype', file.mimetype);
29408 if(this.fireEvent('prepare', this, formData) != false){
29409 this.xhr.send(formData);
29419 * @class Roo.bootstrap.DocumentViewer
29420 * @extends Roo.bootstrap.Component
29421 * Bootstrap DocumentViewer class
29422 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29423 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29426 * Create a new DocumentViewer
29427 * @param {Object} config The config object
29430 Roo.bootstrap.DocumentViewer = function(config){
29431 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29436 * Fire after initEvent
29437 * @param {Roo.bootstrap.DocumentViewer} this
29443 * @param {Roo.bootstrap.DocumentViewer} this
29448 * Fire after download button
29449 * @param {Roo.bootstrap.DocumentViewer} this
29454 * Fire after trash button
29455 * @param {Roo.bootstrap.DocumentViewer} this
29462 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29464 showDownload : true,
29468 getAutoCreate : function()
29472 cls : 'roo-document-viewer',
29476 cls : 'roo-document-viewer-body',
29480 cls : 'roo-document-viewer-thumb',
29484 cls : 'roo-document-viewer-image'
29492 cls : 'roo-document-viewer-footer',
29495 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29499 cls : 'btn-group roo-document-viewer-download',
29503 cls : 'btn btn-default',
29504 html : '<i class="fa fa-download"></i>'
29510 cls : 'btn-group roo-document-viewer-trash',
29514 cls : 'btn btn-default',
29515 html : '<i class="fa fa-trash"></i>'
29528 initEvents : function()
29530 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29531 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29533 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29534 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29536 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29537 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29539 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29540 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29542 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29543 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29545 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29546 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29548 this.bodyEl.on('click', this.onClick, this);
29549 this.downloadBtn.on('click', this.onDownload, this);
29550 this.trashBtn.on('click', this.onTrash, this);
29552 this.downloadBtn.hide();
29553 this.trashBtn.hide();
29555 if(this.showDownload){
29556 this.downloadBtn.show();
29559 if(this.showTrash){
29560 this.trashBtn.show();
29563 if(!this.showDownload && !this.showTrash) {
29564 this.footerEl.hide();
29569 initial : function()
29571 this.fireEvent('initial', this);
29575 onClick : function(e)
29577 e.preventDefault();
29579 this.fireEvent('click', this);
29582 onDownload : function(e)
29584 e.preventDefault();
29586 this.fireEvent('download', this);
29589 onTrash : function(e)
29591 e.preventDefault();
29593 this.fireEvent('trash', this);
29605 * @class Roo.bootstrap.NavProgressBar
29606 * @extends Roo.bootstrap.Component
29607 * Bootstrap NavProgressBar class
29610 * Create a new nav progress bar
29611 * @param {Object} config The config object
29614 Roo.bootstrap.NavProgressBar = function(config){
29615 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29617 this.bullets = this.bullets || [];
29619 // Roo.bootstrap.NavProgressBar.register(this);
29623 * Fires when the active item changes
29624 * @param {Roo.bootstrap.NavProgressBar} this
29625 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29626 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29633 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29638 getAutoCreate : function()
29640 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29644 cls : 'roo-navigation-bar-group',
29648 cls : 'roo-navigation-top-bar'
29652 cls : 'roo-navigation-bullets-bar',
29656 cls : 'roo-navigation-bar'
29663 cls : 'roo-navigation-bottom-bar'
29673 initEvents: function()
29678 onRender : function(ct, position)
29680 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29682 if(this.bullets.length){
29683 Roo.each(this.bullets, function(b){
29692 addItem : function(cfg)
29694 var item = new Roo.bootstrap.NavProgressItem(cfg);
29696 item.parentId = this.id;
29697 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29700 var top = new Roo.bootstrap.Element({
29702 cls : 'roo-navigation-bar-text'
29705 var bottom = new Roo.bootstrap.Element({
29707 cls : 'roo-navigation-bar-text'
29710 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29711 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29713 var topText = new Roo.bootstrap.Element({
29715 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29718 var bottomText = new Roo.bootstrap.Element({
29720 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29723 topText.onRender(top.el, null);
29724 bottomText.onRender(bottom.el, null);
29727 item.bottomEl = bottom;
29730 this.barItems.push(item);
29735 getActive : function()
29737 var active = false;
29739 Roo.each(this.barItems, function(v){
29741 if (!v.isActive()) {
29753 setActiveItem : function(item)
29757 Roo.each(this.barItems, function(v){
29758 if (v.rid == item.rid) {
29762 if (v.isActive()) {
29763 v.setActive(false);
29768 item.setActive(true);
29770 this.fireEvent('changed', this, item, prev);
29773 getBarItem: function(rid)
29777 Roo.each(this.barItems, function(e) {
29778 if (e.rid != rid) {
29789 indexOfItem : function(item)
29793 Roo.each(this.barItems, function(v, i){
29795 if (v.rid != item.rid) {
29806 setActiveNext : function()
29808 var i = this.indexOfItem(this.getActive());
29810 if (i > this.barItems.length) {
29814 this.setActiveItem(this.barItems[i+1]);
29817 setActivePrev : function()
29819 var i = this.indexOfItem(this.getActive());
29825 this.setActiveItem(this.barItems[i-1]);
29828 format : function()
29830 if(!this.barItems.length){
29834 var width = 100 / this.barItems.length;
29836 Roo.each(this.barItems, function(i){
29837 i.el.setStyle('width', width + '%');
29838 i.topEl.el.setStyle('width', width + '%');
29839 i.bottomEl.el.setStyle('width', width + '%');
29848 * Nav Progress Item
29853 * @class Roo.bootstrap.NavProgressItem
29854 * @extends Roo.bootstrap.Component
29855 * Bootstrap NavProgressItem class
29856 * @cfg {String} rid the reference id
29857 * @cfg {Boolean} active (true|false) Is item active default false
29858 * @cfg {Boolean} disabled (true|false) Is item active default false
29859 * @cfg {String} html
29860 * @cfg {String} position (top|bottom) text position default bottom
29861 * @cfg {String} icon show icon instead of number
29864 * Create a new NavProgressItem
29865 * @param {Object} config The config object
29867 Roo.bootstrap.NavProgressItem = function(config){
29868 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29873 * The raw click event for the entire grid.
29874 * @param {Roo.bootstrap.NavProgressItem} this
29875 * @param {Roo.EventObject} e
29882 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29888 position : 'bottom',
29891 getAutoCreate : function()
29893 var iconCls = 'roo-navigation-bar-item-icon';
29895 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29899 cls: 'roo-navigation-bar-item',
29909 cfg.cls += ' active';
29912 cfg.cls += ' disabled';
29918 disable : function()
29920 this.setDisabled(true);
29923 enable : function()
29925 this.setDisabled(false);
29928 initEvents: function()
29930 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29932 this.iconEl.on('click', this.onClick, this);
29935 onClick : function(e)
29937 e.preventDefault();
29943 if(this.fireEvent('click', this, e) === false){
29947 this.parent().setActiveItem(this);
29950 isActive: function ()
29952 return this.active;
29955 setActive : function(state)
29957 if(this.active == state){
29961 this.active = state;
29964 this.el.addClass('active');
29968 this.el.removeClass('active');
29973 setDisabled : function(state)
29975 if(this.disabled == state){
29979 this.disabled = state;
29982 this.el.addClass('disabled');
29986 this.el.removeClass('disabled');
29989 tooltipEl : function()
29991 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30004 * @class Roo.bootstrap.FieldLabel
30005 * @extends Roo.bootstrap.Component
30006 * Bootstrap FieldLabel class
30007 * @cfg {String} html contents of the element
30008 * @cfg {String} tag tag of the element default label
30009 * @cfg {String} cls class of the element
30010 * @cfg {String} target label target
30011 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30012 * @cfg {String} invalidClass default "text-warning"
30013 * @cfg {String} validClass default "text-success"
30014 * @cfg {String} iconTooltip default "This field is required"
30015 * @cfg {String} indicatorpos (left|right) default left
30018 * Create a new FieldLabel
30019 * @param {Object} config The config object
30022 Roo.bootstrap.FieldLabel = function(config){
30023 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30028 * Fires after the field has been marked as invalid.
30029 * @param {Roo.form.FieldLabel} this
30030 * @param {String} msg The validation message
30035 * Fires after the field has been validated with no errors.
30036 * @param {Roo.form.FieldLabel} this
30042 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30049 invalidClass : 'has-warning',
30050 validClass : 'has-success',
30051 iconTooltip : 'This field is required',
30052 indicatorpos : 'left',
30054 getAutoCreate : function(){
30057 if (!this.allowBlank) {
30063 cls : 'roo-bootstrap-field-label ' + this.cls,
30068 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30069 tooltip : this.iconTooltip
30078 if(this.indicatorpos == 'right'){
30081 cls : 'roo-bootstrap-field-label ' + this.cls,
30090 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30091 tooltip : this.iconTooltip
30100 initEvents: function()
30102 Roo.bootstrap.Element.superclass.initEvents.call(this);
30104 this.indicator = this.indicatorEl();
30106 if(this.indicator){
30107 this.indicator.removeClass('visible');
30108 this.indicator.addClass('invisible');
30111 Roo.bootstrap.FieldLabel.register(this);
30114 indicatorEl : function()
30116 var indicator = this.el.select('i.roo-required-indicator',true).first();
30127 * Mark this field as valid
30129 markValid : function()
30131 if(this.indicator){
30132 this.indicator.removeClass('visible');
30133 this.indicator.addClass('invisible');
30136 this.el.removeClass(this.invalidClass);
30138 this.el.addClass(this.validClass);
30140 this.fireEvent('valid', this);
30144 * Mark this field as invalid
30145 * @param {String} msg The validation message
30147 markInvalid : function(msg)
30149 if(this.indicator){
30150 this.indicator.removeClass('invisible');
30151 this.indicator.addClass('visible');
30154 this.el.removeClass(this.validClass);
30156 this.el.addClass(this.invalidClass);
30158 this.fireEvent('invalid', this, msg);
30164 Roo.apply(Roo.bootstrap.FieldLabel, {
30169 * register a FieldLabel Group
30170 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30172 register : function(label)
30174 if(this.groups.hasOwnProperty(label.target)){
30178 this.groups[label.target] = label;
30182 * fetch a FieldLabel Group based on the target
30183 * @param {string} target
30184 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30186 get: function(target) {
30187 if (typeof(this.groups[target]) == 'undefined') {
30191 return this.groups[target] ;
30200 * page DateSplitField.
30206 * @class Roo.bootstrap.DateSplitField
30207 * @extends Roo.bootstrap.Component
30208 * Bootstrap DateSplitField class
30209 * @cfg {string} fieldLabel - the label associated
30210 * @cfg {Number} labelWidth set the width of label (0-12)
30211 * @cfg {String} labelAlign (top|left)
30212 * @cfg {Boolean} dayAllowBlank (true|false) default false
30213 * @cfg {Boolean} monthAllowBlank (true|false) default false
30214 * @cfg {Boolean} yearAllowBlank (true|false) default false
30215 * @cfg {string} dayPlaceholder
30216 * @cfg {string} monthPlaceholder
30217 * @cfg {string} yearPlaceholder
30218 * @cfg {string} dayFormat default 'd'
30219 * @cfg {string} monthFormat default 'm'
30220 * @cfg {string} yearFormat default 'Y'
30221 * @cfg {Number} labellg set the width of label (1-12)
30222 * @cfg {Number} labelmd set the width of label (1-12)
30223 * @cfg {Number} labelsm set the width of label (1-12)
30224 * @cfg {Number} labelxs set the width of label (1-12)
30228 * Create a new DateSplitField
30229 * @param {Object} config The config object
30232 Roo.bootstrap.DateSplitField = function(config){
30233 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30239 * getting the data of years
30240 * @param {Roo.bootstrap.DateSplitField} this
30241 * @param {Object} years
30246 * getting the data of days
30247 * @param {Roo.bootstrap.DateSplitField} this
30248 * @param {Object} days
30253 * Fires after the field has been marked as invalid.
30254 * @param {Roo.form.Field} this
30255 * @param {String} msg The validation message
30260 * Fires after the field has been validated with no errors.
30261 * @param {Roo.form.Field} this
30267 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30270 labelAlign : 'top',
30272 dayAllowBlank : false,
30273 monthAllowBlank : false,
30274 yearAllowBlank : false,
30275 dayPlaceholder : '',
30276 monthPlaceholder : '',
30277 yearPlaceholder : '',
30281 isFormField : true,
30287 getAutoCreate : function()
30291 cls : 'row roo-date-split-field-group',
30296 cls : 'form-hidden-field roo-date-split-field-group-value',
30302 var labelCls = 'col-md-12';
30303 var contentCls = 'col-md-4';
30305 if(this.fieldLabel){
30309 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30313 html : this.fieldLabel
30318 if(this.labelAlign == 'left'){
30320 if(this.labelWidth > 12){
30321 label.style = "width: " + this.labelWidth + 'px';
30324 if(this.labelWidth < 13 && this.labelmd == 0){
30325 this.labelmd = this.labelWidth;
30328 if(this.labellg > 0){
30329 labelCls = ' col-lg-' + this.labellg;
30330 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30333 if(this.labelmd > 0){
30334 labelCls = ' col-md-' + this.labelmd;
30335 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30338 if(this.labelsm > 0){
30339 labelCls = ' col-sm-' + this.labelsm;
30340 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30343 if(this.labelxs > 0){
30344 labelCls = ' col-xs-' + this.labelxs;
30345 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30349 label.cls += ' ' + labelCls;
30351 cfg.cn.push(label);
30354 Roo.each(['day', 'month', 'year'], function(t){
30357 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30364 inputEl: function ()
30366 return this.el.select('.roo-date-split-field-group-value', true).first();
30369 onRender : function(ct, position)
30373 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30375 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30377 this.dayField = new Roo.bootstrap.ComboBox({
30378 allowBlank : this.dayAllowBlank,
30379 alwaysQuery : true,
30380 displayField : 'value',
30383 forceSelection : true,
30385 placeholder : this.dayPlaceholder,
30386 selectOnFocus : true,
30387 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30388 triggerAction : 'all',
30390 valueField : 'value',
30391 store : new Roo.data.SimpleStore({
30392 data : (function() {
30394 _this.fireEvent('days', _this, days);
30397 fields : [ 'value' ]
30400 select : function (_self, record, index)
30402 _this.setValue(_this.getValue());
30407 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30409 this.monthField = new Roo.bootstrap.MonthField({
30410 after : '<i class=\"fa fa-calendar\"></i>',
30411 allowBlank : this.monthAllowBlank,
30412 placeholder : this.monthPlaceholder,
30415 render : function (_self)
30417 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30418 e.preventDefault();
30422 select : function (_self, oldvalue, newvalue)
30424 _this.setValue(_this.getValue());
30429 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30431 this.yearField = new Roo.bootstrap.ComboBox({
30432 allowBlank : this.yearAllowBlank,
30433 alwaysQuery : true,
30434 displayField : 'value',
30437 forceSelection : true,
30439 placeholder : this.yearPlaceholder,
30440 selectOnFocus : true,
30441 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30442 triggerAction : 'all',
30444 valueField : 'value',
30445 store : new Roo.data.SimpleStore({
30446 data : (function() {
30448 _this.fireEvent('years', _this, years);
30451 fields : [ 'value' ]
30454 select : function (_self, record, index)
30456 _this.setValue(_this.getValue());
30461 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30464 setValue : function(v, format)
30466 this.inputEl.dom.value = v;
30468 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30470 var d = Date.parseDate(v, f);
30477 this.setDay(d.format(this.dayFormat));
30478 this.setMonth(d.format(this.monthFormat));
30479 this.setYear(d.format(this.yearFormat));
30486 setDay : function(v)
30488 this.dayField.setValue(v);
30489 this.inputEl.dom.value = this.getValue();
30494 setMonth : function(v)
30496 this.monthField.setValue(v, true);
30497 this.inputEl.dom.value = this.getValue();
30502 setYear : function(v)
30504 this.yearField.setValue(v);
30505 this.inputEl.dom.value = this.getValue();
30510 getDay : function()
30512 return this.dayField.getValue();
30515 getMonth : function()
30517 return this.monthField.getValue();
30520 getYear : function()
30522 return this.yearField.getValue();
30525 getValue : function()
30527 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30529 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30539 this.inputEl.dom.value = '';
30544 validate : function()
30546 var d = this.dayField.validate();
30547 var m = this.monthField.validate();
30548 var y = this.yearField.validate();
30553 (!this.dayAllowBlank && !d) ||
30554 (!this.monthAllowBlank && !m) ||
30555 (!this.yearAllowBlank && !y)
30560 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30569 this.markInvalid();
30574 markValid : function()
30577 var label = this.el.select('label', true).first();
30578 var icon = this.el.select('i.fa-star', true).first();
30584 this.fireEvent('valid', this);
30588 * Mark this field as invalid
30589 * @param {String} msg The validation message
30591 markInvalid : function(msg)
30594 var label = this.el.select('label', true).first();
30595 var icon = this.el.select('i.fa-star', true).first();
30597 if(label && !icon){
30598 this.el.select('.roo-date-split-field-label', true).createChild({
30600 cls : 'text-danger fa fa-lg fa-star',
30601 tooltip : 'This field is required',
30602 style : 'margin-right:5px;'
30606 this.fireEvent('invalid', this, msg);
30609 clearInvalid : function()
30611 var label = this.el.select('label', true).first();
30612 var icon = this.el.select('i.fa-star', true).first();
30618 this.fireEvent('valid', this);
30621 getName: function()
30631 * http://masonry.desandro.com
30633 * The idea is to render all the bricks based on vertical width...
30635 * The original code extends 'outlayer' - we might need to use that....
30641 * @class Roo.bootstrap.LayoutMasonry
30642 * @extends Roo.bootstrap.Component
30643 * Bootstrap Layout Masonry class
30646 * Create a new Element
30647 * @param {Object} config The config object
30650 Roo.bootstrap.LayoutMasonry = function(config){
30652 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30656 Roo.bootstrap.LayoutMasonry.register(this);
30662 * Fire after layout the items
30663 * @param {Roo.bootstrap.LayoutMasonry} this
30664 * @param {Roo.EventObject} e
30671 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30674 * @cfg {Boolean} isLayoutInstant = no animation?
30676 isLayoutInstant : false, // needed?
30679 * @cfg {Number} boxWidth width of the columns
30684 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30689 * @cfg {Number} padWidth padding below box..
30694 * @cfg {Number} gutter gutter width..
30699 * @cfg {Number} maxCols maximum number of columns
30705 * @cfg {Boolean} isAutoInitial defalut true
30707 isAutoInitial : true,
30712 * @cfg {Boolean} isHorizontal defalut false
30714 isHorizontal : false,
30716 currentSize : null,
30722 bricks: null, //CompositeElement
30726 _isLayoutInited : false,
30728 // isAlternative : false, // only use for vertical layout...
30731 * @cfg {Number} alternativePadWidth padding below box..
30733 alternativePadWidth : 50,
30735 selectedBrick : [],
30737 getAutoCreate : function(){
30739 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30743 cls: 'blog-masonary-wrapper ' + this.cls,
30745 cls : 'mas-boxes masonary'
30752 getChildContainer: function( )
30754 if (this.boxesEl) {
30755 return this.boxesEl;
30758 this.boxesEl = this.el.select('.mas-boxes').first();
30760 return this.boxesEl;
30764 initEvents : function()
30768 if(this.isAutoInitial){
30769 Roo.log('hook children rendered');
30770 this.on('childrenrendered', function() {
30771 Roo.log('children rendered');
30777 initial : function()
30779 this.selectedBrick = [];
30781 this.currentSize = this.el.getBox(true);
30783 Roo.EventManager.onWindowResize(this.resize, this);
30785 if(!this.isAutoInitial){
30793 //this.layout.defer(500,this);
30797 resize : function()
30799 var cs = this.el.getBox(true);
30802 this.currentSize.width == cs.width &&
30803 this.currentSize.x == cs.x &&
30804 this.currentSize.height == cs.height &&
30805 this.currentSize.y == cs.y
30807 Roo.log("no change in with or X or Y");
30811 this.currentSize = cs;
30817 layout : function()
30819 this._resetLayout();
30821 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30823 this.layoutItems( isInstant );
30825 this._isLayoutInited = true;
30827 this.fireEvent('layout', this);
30831 _resetLayout : function()
30833 if(this.isHorizontal){
30834 this.horizontalMeasureColumns();
30838 this.verticalMeasureColumns();
30842 verticalMeasureColumns : function()
30844 this.getContainerWidth();
30846 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30847 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30851 var boxWidth = this.boxWidth + this.padWidth;
30853 if(this.containerWidth < this.boxWidth){
30854 boxWidth = this.containerWidth
30857 var containerWidth = this.containerWidth;
30859 var cols = Math.floor(containerWidth / boxWidth);
30861 this.cols = Math.max( cols, 1 );
30863 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30865 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30867 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30869 this.colWidth = boxWidth + avail - this.padWidth;
30871 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30872 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30875 horizontalMeasureColumns : function()
30877 this.getContainerWidth();
30879 var boxWidth = this.boxWidth;
30881 if(this.containerWidth < boxWidth){
30882 boxWidth = this.containerWidth;
30885 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30887 this.el.setHeight(boxWidth);
30891 getContainerWidth : function()
30893 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30896 layoutItems : function( isInstant )
30898 Roo.log(this.bricks);
30900 var items = Roo.apply([], this.bricks);
30902 if(this.isHorizontal){
30903 this._horizontalLayoutItems( items , isInstant );
30907 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30908 // this._verticalAlternativeLayoutItems( items , isInstant );
30912 this._verticalLayoutItems( items , isInstant );
30916 _verticalLayoutItems : function ( items , isInstant)
30918 if ( !items || !items.length ) {
30923 ['xs', 'xs', 'xs', 'tall'],
30924 ['xs', 'xs', 'tall'],
30925 ['xs', 'xs', 'sm'],
30926 ['xs', 'xs', 'xs'],
30932 ['sm', 'xs', 'xs'],
30936 ['tall', 'xs', 'xs', 'xs'],
30937 ['tall', 'xs', 'xs'],
30949 Roo.each(items, function(item, k){
30951 switch (item.size) {
30952 // these layouts take up a full box,
30963 boxes.push([item]);
30986 var filterPattern = function(box, length)
30994 var pattern = box.slice(0, length);
30998 Roo.each(pattern, function(i){
30999 format.push(i.size);
31002 Roo.each(standard, function(s){
31004 if(String(s) != String(format)){
31013 if(!match && length == 1){
31018 filterPattern(box, length - 1);
31022 queue.push(pattern);
31024 box = box.slice(length, box.length);
31026 filterPattern(box, 4);
31032 Roo.each(boxes, function(box, k){
31038 if(box.length == 1){
31043 filterPattern(box, 4);
31047 this._processVerticalLayoutQueue( queue, isInstant );
31051 // _verticalAlternativeLayoutItems : function( items , isInstant )
31053 // if ( !items || !items.length ) {
31057 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31061 _horizontalLayoutItems : function ( items , isInstant)
31063 if ( !items || !items.length || items.length < 3) {
31069 var eItems = items.slice(0, 3);
31071 items = items.slice(3, items.length);
31074 ['xs', 'xs', 'xs', 'wide'],
31075 ['xs', 'xs', 'wide'],
31076 ['xs', 'xs', 'sm'],
31077 ['xs', 'xs', 'xs'],
31083 ['sm', 'xs', 'xs'],
31087 ['wide', 'xs', 'xs', 'xs'],
31088 ['wide', 'xs', 'xs'],
31101 Roo.each(items, function(item, k){
31103 switch (item.size) {
31114 boxes.push([item]);
31138 var filterPattern = function(box, length)
31146 var pattern = box.slice(0, length);
31150 Roo.each(pattern, function(i){
31151 format.push(i.size);
31154 Roo.each(standard, function(s){
31156 if(String(s) != String(format)){
31165 if(!match && length == 1){
31170 filterPattern(box, length - 1);
31174 queue.push(pattern);
31176 box = box.slice(length, box.length);
31178 filterPattern(box, 4);
31184 Roo.each(boxes, function(box, k){
31190 if(box.length == 1){
31195 filterPattern(box, 4);
31202 var pos = this.el.getBox(true);
31206 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31208 var hit_end = false;
31210 Roo.each(queue, function(box){
31214 Roo.each(box, function(b){
31216 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31226 Roo.each(box, function(b){
31228 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31231 mx = Math.max(mx, b.x);
31235 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31239 Roo.each(box, function(b){
31241 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31255 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31258 /** Sets position of item in DOM
31259 * @param {Element} item
31260 * @param {Number} x - horizontal position
31261 * @param {Number} y - vertical position
31262 * @param {Boolean} isInstant - disables transitions
31264 _processVerticalLayoutQueue : function( queue, isInstant )
31266 var pos = this.el.getBox(true);
31271 for (var i = 0; i < this.cols; i++){
31275 Roo.each(queue, function(box, k){
31277 var col = k % this.cols;
31279 Roo.each(box, function(b,kk){
31281 b.el.position('absolute');
31283 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31284 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31286 if(b.size == 'md-left' || b.size == 'md-right'){
31287 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31288 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31291 b.el.setWidth(width);
31292 b.el.setHeight(height);
31294 b.el.select('iframe',true).setSize(width,height);
31298 for (var i = 0; i < this.cols; i++){
31300 if(maxY[i] < maxY[col]){
31305 col = Math.min(col, i);
31309 x = pos.x + col * (this.colWidth + this.padWidth);
31313 var positions = [];
31315 switch (box.length){
31317 positions = this.getVerticalOneBoxColPositions(x, y, box);
31320 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31323 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31326 positions = this.getVerticalFourBoxColPositions(x, y, box);
31332 Roo.each(box, function(b,kk){
31334 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31336 var sz = b.el.getSize();
31338 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31346 for (var i = 0; i < this.cols; i++){
31347 mY = Math.max(mY, maxY[i]);
31350 this.el.setHeight(mY - pos.y);
31354 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31356 // var pos = this.el.getBox(true);
31359 // var maxX = pos.right;
31361 // var maxHeight = 0;
31363 // Roo.each(items, function(item, k){
31367 // item.el.position('absolute');
31369 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31371 // item.el.setWidth(width);
31373 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31375 // item.el.setHeight(height);
31378 // item.el.setXY([x, y], isInstant ? false : true);
31380 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31383 // y = y + height + this.alternativePadWidth;
31385 // maxHeight = maxHeight + height + this.alternativePadWidth;
31389 // this.el.setHeight(maxHeight);
31393 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31395 var pos = this.el.getBox(true);
31400 var maxX = pos.right;
31402 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31404 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31406 Roo.each(queue, function(box, k){
31408 Roo.each(box, function(b, kk){
31410 b.el.position('absolute');
31412 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31413 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31415 if(b.size == 'md-left' || b.size == 'md-right'){
31416 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31417 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31420 b.el.setWidth(width);
31421 b.el.setHeight(height);
31429 var positions = [];
31431 switch (box.length){
31433 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31436 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31439 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31442 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31448 Roo.each(box, function(b,kk){
31450 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31452 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31460 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31462 Roo.each(eItems, function(b,k){
31464 b.size = (k == 0) ? 'sm' : 'xs';
31465 b.x = (k == 0) ? 2 : 1;
31466 b.y = (k == 0) ? 2 : 1;
31468 b.el.position('absolute');
31470 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31472 b.el.setWidth(width);
31474 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31476 b.el.setHeight(height);
31480 var positions = [];
31483 x : maxX - this.unitWidth * 2 - this.gutter,
31488 x : maxX - this.unitWidth,
31489 y : minY + (this.unitWidth + this.gutter) * 2
31493 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31497 Roo.each(eItems, function(b,k){
31499 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31505 getVerticalOneBoxColPositions : function(x, y, box)
31509 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31511 if(box[0].size == 'md-left'){
31515 if(box[0].size == 'md-right'){
31520 x : x + (this.unitWidth + this.gutter) * rand,
31527 getVerticalTwoBoxColPositions : function(x, y, box)
31531 if(box[0].size == 'xs'){
31535 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31539 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31553 x : x + (this.unitWidth + this.gutter) * 2,
31554 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31561 getVerticalThreeBoxColPositions : function(x, y, box)
31565 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31573 x : x + (this.unitWidth + this.gutter) * 1,
31578 x : x + (this.unitWidth + this.gutter) * 2,
31586 if(box[0].size == 'xs' && box[1].size == 'xs'){
31595 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31599 x : x + (this.unitWidth + this.gutter) * 1,
31613 x : x + (this.unitWidth + this.gutter) * 2,
31618 x : x + (this.unitWidth + this.gutter) * 2,
31619 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31626 getVerticalFourBoxColPositions : function(x, y, box)
31630 if(box[0].size == 'xs'){
31639 y : y + (this.unitHeight + this.gutter) * 1
31644 y : y + (this.unitHeight + this.gutter) * 2
31648 x : x + (this.unitWidth + this.gutter) * 1,
31662 x : x + (this.unitWidth + this.gutter) * 2,
31667 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31668 y : y + (this.unitHeight + this.gutter) * 1
31672 x : x + (this.unitWidth + this.gutter) * 2,
31673 y : y + (this.unitWidth + this.gutter) * 2
31680 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31684 if(box[0].size == 'md-left'){
31686 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31693 if(box[0].size == 'md-right'){
31695 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31696 y : minY + (this.unitWidth + this.gutter) * 1
31702 var rand = Math.floor(Math.random() * (4 - box[0].y));
31705 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31706 y : minY + (this.unitWidth + this.gutter) * rand
31713 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31717 if(box[0].size == 'xs'){
31720 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31725 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31726 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31734 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31739 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31740 y : minY + (this.unitWidth + this.gutter) * 2
31747 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31751 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31754 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31759 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31760 y : minY + (this.unitWidth + this.gutter) * 1
31764 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31765 y : minY + (this.unitWidth + this.gutter) * 2
31772 if(box[0].size == 'xs' && box[1].size == 'xs'){
31775 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31780 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31785 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31786 y : minY + (this.unitWidth + this.gutter) * 1
31794 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31799 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31800 y : minY + (this.unitWidth + this.gutter) * 2
31804 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31805 y : minY + (this.unitWidth + this.gutter) * 2
31812 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31816 if(box[0].size == 'xs'){
31819 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31824 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31829 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),
31834 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31835 y : minY + (this.unitWidth + this.gutter) * 1
31843 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31848 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31849 y : minY + (this.unitWidth + this.gutter) * 2
31853 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31854 y : minY + (this.unitWidth + this.gutter) * 2
31858 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),
31859 y : minY + (this.unitWidth + this.gutter) * 2
31867 * remove a Masonry Brick
31868 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31870 removeBrick : function(brick_id)
31876 for (var i = 0; i<this.bricks.length; i++) {
31877 if (this.bricks[i].id == brick_id) {
31878 this.bricks.splice(i,1);
31879 this.el.dom.removeChild(Roo.get(brick_id).dom);
31886 * adds a Masonry Brick
31887 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31889 addBrick : function(cfg)
31891 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31892 //this.register(cn);
31893 cn.parentId = this.id;
31894 cn.onRender(this.el, null);
31899 * register a Masonry Brick
31900 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31903 register : function(brick)
31905 this.bricks.push(brick);
31906 brick.masonryId = this.id;
31910 * clear all the Masonry Brick
31912 clearAll : function()
31915 //this.getChildContainer().dom.innerHTML = "";
31916 this.el.dom.innerHTML = '';
31919 getSelected : function()
31921 if (!this.selectedBrick) {
31925 return this.selectedBrick;
31929 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31933 * register a Masonry Layout
31934 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31937 register : function(layout)
31939 this.groups[layout.id] = layout;
31942 * fetch a Masonry Layout based on the masonry layout ID
31943 * @param {string} the masonry layout to add
31944 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31947 get: function(layout_id) {
31948 if (typeof(this.groups[layout_id]) == 'undefined') {
31951 return this.groups[layout_id] ;
31963 * http://masonry.desandro.com
31965 * The idea is to render all the bricks based on vertical width...
31967 * The original code extends 'outlayer' - we might need to use that....
31973 * @class Roo.bootstrap.LayoutMasonryAuto
31974 * @extends Roo.bootstrap.Component
31975 * Bootstrap Layout Masonry class
31978 * Create a new Element
31979 * @param {Object} config The config object
31982 Roo.bootstrap.LayoutMasonryAuto = function(config){
31983 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31986 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31989 * @cfg {Boolean} isFitWidth - resize the width..
31991 isFitWidth : false, // options..
31993 * @cfg {Boolean} isOriginLeft = left align?
31995 isOriginLeft : true,
31997 * @cfg {Boolean} isOriginTop = top align?
31999 isOriginTop : false,
32001 * @cfg {Boolean} isLayoutInstant = no animation?
32003 isLayoutInstant : false, // needed?
32005 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32007 isResizingContainer : true,
32009 * @cfg {Number} columnWidth width of the columns
32015 * @cfg {Number} maxCols maximum number of columns
32020 * @cfg {Number} padHeight padding below box..
32026 * @cfg {Boolean} isAutoInitial defalut true
32029 isAutoInitial : true,
32035 initialColumnWidth : 0,
32036 currentSize : null,
32038 colYs : null, // array.
32045 bricks: null, //CompositeElement
32046 cols : 0, // array?
32047 // element : null, // wrapped now this.el
32048 _isLayoutInited : null,
32051 getAutoCreate : function(){
32055 cls: 'blog-masonary-wrapper ' + this.cls,
32057 cls : 'mas-boxes masonary'
32064 getChildContainer: function( )
32066 if (this.boxesEl) {
32067 return this.boxesEl;
32070 this.boxesEl = this.el.select('.mas-boxes').first();
32072 return this.boxesEl;
32076 initEvents : function()
32080 if(this.isAutoInitial){
32081 Roo.log('hook children rendered');
32082 this.on('childrenrendered', function() {
32083 Roo.log('children rendered');
32090 initial : function()
32092 this.reloadItems();
32094 this.currentSize = this.el.getBox(true);
32096 /// was window resize... - let's see if this works..
32097 Roo.EventManager.onWindowResize(this.resize, this);
32099 if(!this.isAutoInitial){
32104 this.layout.defer(500,this);
32107 reloadItems: function()
32109 this.bricks = this.el.select('.masonry-brick', true);
32111 this.bricks.each(function(b) {
32112 //Roo.log(b.getSize());
32113 if (!b.attr('originalwidth')) {
32114 b.attr('originalwidth', b.getSize().width);
32119 Roo.log(this.bricks.elements.length);
32122 resize : function()
32125 var cs = this.el.getBox(true);
32127 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32128 Roo.log("no change in with or X");
32131 this.currentSize = cs;
32135 layout : function()
32138 this._resetLayout();
32139 //this._manageStamps();
32141 // don't animate first layout
32142 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32143 this.layoutItems( isInstant );
32145 // flag for initalized
32146 this._isLayoutInited = true;
32149 layoutItems : function( isInstant )
32151 //var items = this._getItemsForLayout( this.items );
32152 // original code supports filtering layout items.. we just ignore it..
32154 this._layoutItems( this.bricks , isInstant );
32156 this._postLayout();
32158 _layoutItems : function ( items , isInstant)
32160 //this.fireEvent( 'layout', this, items );
32163 if ( !items || !items.elements.length ) {
32164 // no items, emit event with empty array
32169 items.each(function(item) {
32170 Roo.log("layout item");
32172 // get x/y object from method
32173 var position = this._getItemLayoutPosition( item );
32175 position.item = item;
32176 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32177 queue.push( position );
32180 this._processLayoutQueue( queue );
32182 /** Sets position of item in DOM
32183 * @param {Element} item
32184 * @param {Number} x - horizontal position
32185 * @param {Number} y - vertical position
32186 * @param {Boolean} isInstant - disables transitions
32188 _processLayoutQueue : function( queue )
32190 for ( var i=0, len = queue.length; i < len; i++ ) {
32191 var obj = queue[i];
32192 obj.item.position('absolute');
32193 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32199 * Any logic you want to do after each layout,
32200 * i.e. size the container
32202 _postLayout : function()
32204 this.resizeContainer();
32207 resizeContainer : function()
32209 if ( !this.isResizingContainer ) {
32212 var size = this._getContainerSize();
32214 this.el.setSize(size.width,size.height);
32215 this.boxesEl.setSize(size.width,size.height);
32221 _resetLayout : function()
32223 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32224 this.colWidth = this.el.getWidth();
32225 //this.gutter = this.el.getWidth();
32227 this.measureColumns();
32233 this.colYs.push( 0 );
32239 measureColumns : function()
32241 this.getContainerWidth();
32242 // if columnWidth is 0, default to outerWidth of first item
32243 if ( !this.columnWidth ) {
32244 var firstItem = this.bricks.first();
32245 Roo.log(firstItem);
32246 this.columnWidth = this.containerWidth;
32247 if (firstItem && firstItem.attr('originalwidth') ) {
32248 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32250 // columnWidth fall back to item of first element
32251 Roo.log("set column width?");
32252 this.initialColumnWidth = this.columnWidth ;
32254 // if first elem has no width, default to size of container
32259 if (this.initialColumnWidth) {
32260 this.columnWidth = this.initialColumnWidth;
32265 // column width is fixed at the top - however if container width get's smaller we should
32268 // this bit calcs how man columns..
32270 var columnWidth = this.columnWidth += this.gutter;
32272 // calculate columns
32273 var containerWidth = this.containerWidth + this.gutter;
32275 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32276 // fix rounding errors, typically with gutters
32277 var excess = columnWidth - containerWidth % columnWidth;
32280 // if overshoot is less than a pixel, round up, otherwise floor it
32281 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32282 cols = Math[ mathMethod ]( cols );
32283 this.cols = Math.max( cols, 1 );
32284 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32286 // padding positioning..
32287 var totalColWidth = this.cols * this.columnWidth;
32288 var padavail = this.containerWidth - totalColWidth;
32289 // so for 2 columns - we need 3 'pads'
32291 var padNeeded = (1+this.cols) * this.padWidth;
32293 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32295 this.columnWidth += padExtra
32296 //this.padWidth = Math.floor(padavail / ( this.cols));
32298 // adjust colum width so that padding is fixed??
32300 // we have 3 columns ... total = width * 3
32301 // we have X left over... that should be used by
32303 //if (this.expandC) {
32311 getContainerWidth : function()
32313 /* // container is parent if fit width
32314 var container = this.isFitWidth ? this.element.parentNode : this.element;
32315 // check that this.size and size are there
32316 // IE8 triggers resize on body size change, so they might not be
32318 var size = getSize( container ); //FIXME
32319 this.containerWidth = size && size.innerWidth; //FIXME
32322 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32326 _getItemLayoutPosition : function( item ) // what is item?
32328 // we resize the item to our columnWidth..
32330 item.setWidth(this.columnWidth);
32331 item.autoBoxAdjust = false;
32333 var sz = item.getSize();
32335 // how many columns does this brick span
32336 var remainder = this.containerWidth % this.columnWidth;
32338 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32339 // round if off by 1 pixel, otherwise use ceil
32340 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32341 colSpan = Math.min( colSpan, this.cols );
32343 // normally this should be '1' as we dont' currently allow multi width columns..
32345 var colGroup = this._getColGroup( colSpan );
32346 // get the minimum Y value from the columns
32347 var minimumY = Math.min.apply( Math, colGroup );
32348 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32350 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32352 // position the brick
32354 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32355 y: this.currentSize.y + minimumY + this.padHeight
32359 // apply setHeight to necessary columns
32360 var setHeight = minimumY + sz.height + this.padHeight;
32361 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32363 var setSpan = this.cols + 1 - colGroup.length;
32364 for ( var i = 0; i < setSpan; i++ ) {
32365 this.colYs[ shortColIndex + i ] = setHeight ;
32372 * @param {Number} colSpan - number of columns the element spans
32373 * @returns {Array} colGroup
32375 _getColGroup : function( colSpan )
32377 if ( colSpan < 2 ) {
32378 // if brick spans only one column, use all the column Ys
32383 // how many different places could this brick fit horizontally
32384 var groupCount = this.cols + 1 - colSpan;
32385 // for each group potential horizontal position
32386 for ( var i = 0; i < groupCount; i++ ) {
32387 // make an array of colY values for that one group
32388 var groupColYs = this.colYs.slice( i, i + colSpan );
32389 // and get the max value of the array
32390 colGroup[i] = Math.max.apply( Math, groupColYs );
32395 _manageStamp : function( stamp )
32397 var stampSize = stamp.getSize();
32398 var offset = stamp.getBox();
32399 // get the columns that this stamp affects
32400 var firstX = this.isOriginLeft ? offset.x : offset.right;
32401 var lastX = firstX + stampSize.width;
32402 var firstCol = Math.floor( firstX / this.columnWidth );
32403 firstCol = Math.max( 0, firstCol );
32405 var lastCol = Math.floor( lastX / this.columnWidth );
32406 // lastCol should not go over if multiple of columnWidth #425
32407 lastCol -= lastX % this.columnWidth ? 0 : 1;
32408 lastCol = Math.min( this.cols - 1, lastCol );
32410 // set colYs to bottom of the stamp
32411 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32414 for ( var i = firstCol; i <= lastCol; i++ ) {
32415 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32420 _getContainerSize : function()
32422 this.maxY = Math.max.apply( Math, this.colYs );
32427 if ( this.isFitWidth ) {
32428 size.width = this._getContainerFitWidth();
32434 _getContainerFitWidth : function()
32436 var unusedCols = 0;
32437 // count unused columns
32440 if ( this.colYs[i] !== 0 ) {
32445 // fit container to columns that have been used
32446 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32449 needsResizeLayout : function()
32451 var previousWidth = this.containerWidth;
32452 this.getContainerWidth();
32453 return previousWidth !== this.containerWidth;
32468 * @class Roo.bootstrap.MasonryBrick
32469 * @extends Roo.bootstrap.Component
32470 * Bootstrap MasonryBrick class
32473 * Create a new MasonryBrick
32474 * @param {Object} config The config object
32477 Roo.bootstrap.MasonryBrick = function(config){
32479 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32481 Roo.bootstrap.MasonryBrick.register(this);
32487 * When a MasonryBrick is clcik
32488 * @param {Roo.bootstrap.MasonryBrick} this
32489 * @param {Roo.EventObject} e
32495 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32498 * @cfg {String} title
32502 * @cfg {String} html
32506 * @cfg {String} bgimage
32510 * @cfg {String} videourl
32514 * @cfg {String} cls
32518 * @cfg {String} href
32522 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32527 * @cfg {String} placetitle (center|bottom)
32532 * @cfg {Boolean} isFitContainer defalut true
32534 isFitContainer : true,
32537 * @cfg {Boolean} preventDefault defalut false
32539 preventDefault : false,
32542 * @cfg {Boolean} inverse defalut false
32544 maskInverse : false,
32546 getAutoCreate : function()
32548 if(!this.isFitContainer){
32549 return this.getSplitAutoCreate();
32552 var cls = 'masonry-brick masonry-brick-full';
32554 if(this.href.length){
32555 cls += ' masonry-brick-link';
32558 if(this.bgimage.length){
32559 cls += ' masonry-brick-image';
32562 if(this.maskInverse){
32563 cls += ' mask-inverse';
32566 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32567 cls += ' enable-mask';
32571 cls += ' masonry-' + this.size + '-brick';
32574 if(this.placetitle.length){
32576 switch (this.placetitle) {
32578 cls += ' masonry-center-title';
32581 cls += ' masonry-bottom-title';
32588 if(!this.html.length && !this.bgimage.length){
32589 cls += ' masonry-center-title';
32592 if(!this.html.length && this.bgimage.length){
32593 cls += ' masonry-bottom-title';
32598 cls += ' ' + this.cls;
32602 tag: (this.href.length) ? 'a' : 'div',
32607 cls: 'masonry-brick-mask'
32611 cls: 'masonry-brick-paragraph',
32617 if(this.href.length){
32618 cfg.href = this.href;
32621 var cn = cfg.cn[1].cn;
32623 if(this.title.length){
32626 cls: 'masonry-brick-title',
32631 if(this.html.length){
32634 cls: 'masonry-brick-text',
32639 if (!this.title.length && !this.html.length) {
32640 cfg.cn[1].cls += ' hide';
32643 if(this.bgimage.length){
32646 cls: 'masonry-brick-image-view',
32651 if(this.videourl.length){
32652 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32653 // youtube support only?
32656 cls: 'masonry-brick-image-view',
32659 allowfullscreen : true
32667 getSplitAutoCreate : function()
32669 var cls = 'masonry-brick masonry-brick-split';
32671 if(this.href.length){
32672 cls += ' masonry-brick-link';
32675 if(this.bgimage.length){
32676 cls += ' masonry-brick-image';
32680 cls += ' masonry-' + this.size + '-brick';
32683 switch (this.placetitle) {
32685 cls += ' masonry-center-title';
32688 cls += ' masonry-bottom-title';
32691 if(!this.bgimage.length){
32692 cls += ' masonry-center-title';
32695 if(this.bgimage.length){
32696 cls += ' masonry-bottom-title';
32702 cls += ' ' + this.cls;
32706 tag: (this.href.length) ? 'a' : 'div',
32711 cls: 'masonry-brick-split-head',
32715 cls: 'masonry-brick-paragraph',
32722 cls: 'masonry-brick-split-body',
32728 if(this.href.length){
32729 cfg.href = this.href;
32732 if(this.title.length){
32733 cfg.cn[0].cn[0].cn.push({
32735 cls: 'masonry-brick-title',
32740 if(this.html.length){
32741 cfg.cn[1].cn.push({
32743 cls: 'masonry-brick-text',
32748 if(this.bgimage.length){
32749 cfg.cn[0].cn.push({
32751 cls: 'masonry-brick-image-view',
32756 if(this.videourl.length){
32757 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32758 // youtube support only?
32759 cfg.cn[0].cn.cn.push({
32761 cls: 'masonry-brick-image-view',
32764 allowfullscreen : true
32771 initEvents: function()
32773 switch (this.size) {
32806 this.el.on('touchstart', this.onTouchStart, this);
32807 this.el.on('touchmove', this.onTouchMove, this);
32808 this.el.on('touchend', this.onTouchEnd, this);
32809 this.el.on('contextmenu', this.onContextMenu, this);
32811 this.el.on('mouseenter' ,this.enter, this);
32812 this.el.on('mouseleave', this.leave, this);
32813 this.el.on('click', this.onClick, this);
32816 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32817 this.parent().bricks.push(this);
32822 onClick: function(e, el)
32824 var time = this.endTimer - this.startTimer;
32825 // Roo.log(e.preventDefault());
32828 e.preventDefault();
32833 if(!this.preventDefault){
32837 e.preventDefault();
32839 if (this.activeClass != '') {
32840 this.selectBrick();
32843 this.fireEvent('click', this, e);
32846 enter: function(e, el)
32848 e.preventDefault();
32850 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32854 if(this.bgimage.length && this.html.length){
32855 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32859 leave: function(e, el)
32861 e.preventDefault();
32863 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32867 if(this.bgimage.length && this.html.length){
32868 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32872 onTouchStart: function(e, el)
32874 // e.preventDefault();
32876 this.touchmoved = false;
32878 if(!this.isFitContainer){
32882 if(!this.bgimage.length || !this.html.length){
32886 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32888 this.timer = new Date().getTime();
32892 onTouchMove: function(e, el)
32894 this.touchmoved = true;
32897 onContextMenu : function(e,el)
32899 e.preventDefault();
32900 e.stopPropagation();
32904 onTouchEnd: function(e, el)
32906 // e.preventDefault();
32908 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32915 if(!this.bgimage.length || !this.html.length){
32917 if(this.href.length){
32918 window.location.href = this.href;
32924 if(!this.isFitContainer){
32928 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32930 window.location.href = this.href;
32933 //selection on single brick only
32934 selectBrick : function() {
32936 if (!this.parentId) {
32940 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32941 var index = m.selectedBrick.indexOf(this.id);
32944 m.selectedBrick.splice(index,1);
32945 this.el.removeClass(this.activeClass);
32949 for(var i = 0; i < m.selectedBrick.length; i++) {
32950 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32951 b.el.removeClass(b.activeClass);
32954 m.selectedBrick = [];
32956 m.selectedBrick.push(this.id);
32957 this.el.addClass(this.activeClass);
32961 isSelected : function(){
32962 return this.el.hasClass(this.activeClass);
32967 Roo.apply(Roo.bootstrap.MasonryBrick, {
32970 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32972 * register a Masonry Brick
32973 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32976 register : function(brick)
32978 //this.groups[brick.id] = brick;
32979 this.groups.add(brick.id, brick);
32982 * fetch a masonry brick based on the masonry brick ID
32983 * @param {string} the masonry brick to add
32984 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32987 get: function(brick_id)
32989 // if (typeof(this.groups[brick_id]) == 'undefined') {
32992 // return this.groups[brick_id] ;
32994 if(this.groups.key(brick_id)) {
32995 return this.groups.key(brick_id);
33013 * @class Roo.bootstrap.Brick
33014 * @extends Roo.bootstrap.Component
33015 * Bootstrap Brick class
33018 * Create a new Brick
33019 * @param {Object} config The config object
33022 Roo.bootstrap.Brick = function(config){
33023 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33029 * When a Brick is click
33030 * @param {Roo.bootstrap.Brick} this
33031 * @param {Roo.EventObject} e
33037 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33040 * @cfg {String} title
33044 * @cfg {String} html
33048 * @cfg {String} bgimage
33052 * @cfg {String} cls
33056 * @cfg {String} href
33060 * @cfg {String} video
33064 * @cfg {Boolean} square
33068 getAutoCreate : function()
33070 var cls = 'roo-brick';
33072 if(this.href.length){
33073 cls += ' roo-brick-link';
33076 if(this.bgimage.length){
33077 cls += ' roo-brick-image';
33080 if(!this.html.length && !this.bgimage.length){
33081 cls += ' roo-brick-center-title';
33084 if(!this.html.length && this.bgimage.length){
33085 cls += ' roo-brick-bottom-title';
33089 cls += ' ' + this.cls;
33093 tag: (this.href.length) ? 'a' : 'div',
33098 cls: 'roo-brick-paragraph',
33104 if(this.href.length){
33105 cfg.href = this.href;
33108 var cn = cfg.cn[0].cn;
33110 if(this.title.length){
33113 cls: 'roo-brick-title',
33118 if(this.html.length){
33121 cls: 'roo-brick-text',
33128 if(this.bgimage.length){
33131 cls: 'roo-brick-image-view',
33139 initEvents: function()
33141 if(this.title.length || this.html.length){
33142 this.el.on('mouseenter' ,this.enter, this);
33143 this.el.on('mouseleave', this.leave, this);
33146 Roo.EventManager.onWindowResize(this.resize, this);
33148 if(this.bgimage.length){
33149 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33150 this.imageEl.on('load', this.onImageLoad, this);
33157 onImageLoad : function()
33162 resize : function()
33164 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33166 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33168 if(this.bgimage.length){
33169 var image = this.el.select('.roo-brick-image-view', true).first();
33171 image.setWidth(paragraph.getWidth());
33174 image.setHeight(paragraph.getWidth());
33177 this.el.setHeight(image.getHeight());
33178 paragraph.setHeight(image.getHeight());
33184 enter: function(e, el)
33186 e.preventDefault();
33188 if(this.bgimage.length){
33189 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33190 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33194 leave: function(e, el)
33196 e.preventDefault();
33198 if(this.bgimage.length){
33199 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33200 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33215 * @class Roo.bootstrap.NumberField
33216 * @extends Roo.bootstrap.Input
33217 * Bootstrap NumberField class
33223 * Create a new NumberField
33224 * @param {Object} config The config object
33227 Roo.bootstrap.NumberField = function(config){
33228 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33231 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33234 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33236 allowDecimals : true,
33238 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33240 decimalSeparator : ".",
33242 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33244 decimalPrecision : 2,
33246 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33248 allowNegative : true,
33251 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33255 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33257 minValue : Number.NEGATIVE_INFINITY,
33259 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33261 maxValue : Number.MAX_VALUE,
33263 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33265 minText : "The minimum value for this field is {0}",
33267 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33269 maxText : "The maximum value for this field is {0}",
33271 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33272 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33274 nanText : "{0} is not a valid number",
33276 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33278 thousandsDelimiter : false,
33280 * @cfg {String} valueAlign alignment of value
33282 valueAlign : "left",
33284 getAutoCreate : function()
33286 var hiddenInput = {
33290 cls: 'hidden-number-input'
33294 hiddenInput.name = this.name;
33299 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33301 this.name = hiddenInput.name;
33303 if(cfg.cn.length > 0) {
33304 cfg.cn.push(hiddenInput);
33311 initEvents : function()
33313 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33315 var allowed = "0123456789";
33317 if(this.allowDecimals){
33318 allowed += this.decimalSeparator;
33321 if(this.allowNegative){
33325 if(this.thousandsDelimiter) {
33329 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33331 var keyPress = function(e){
33333 var k = e.getKey();
33335 var c = e.getCharCode();
33338 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33339 allowed.indexOf(String.fromCharCode(c)) === -1
33345 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33349 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33354 this.el.on("keypress", keyPress, this);
33357 validateValue : function(value)
33360 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33364 var num = this.parseValue(value);
33367 this.markInvalid(String.format(this.nanText, value));
33371 if(num < this.minValue){
33372 this.markInvalid(String.format(this.minText, this.minValue));
33376 if(num > this.maxValue){
33377 this.markInvalid(String.format(this.maxText, this.maxValue));
33384 getValue : function()
33386 var v = this.hiddenEl().getValue();
33388 return this.fixPrecision(this.parseValue(v));
33391 parseValue : function(value)
33393 if(this.thousandsDelimiter) {
33395 r = new RegExp(",", "g");
33396 value = value.replace(r, "");
33399 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33400 return isNaN(value) ? '' : value;
33403 fixPrecision : function(value)
33405 if(this.thousandsDelimiter) {
33407 r = new RegExp(",", "g");
33408 value = value.replace(r, "");
33411 var nan = isNaN(value);
33413 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33414 return nan ? '' : value;
33416 return parseFloat(value).toFixed(this.decimalPrecision);
33419 setValue : function(v)
33421 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33427 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33429 this.inputEl().dom.value = (v == '') ? '' :
33430 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33432 if(!this.allowZero && v === '0') {
33433 this.hiddenEl().dom.value = '';
33434 this.inputEl().dom.value = '';
33441 decimalPrecisionFcn : function(v)
33443 return Math.floor(v);
33446 beforeBlur : function()
33448 var v = this.parseValue(this.getRawValue());
33450 if(v || v === 0 || v === ''){
33455 hiddenEl : function()
33457 return this.el.select('input.hidden-number-input',true).first();
33469 * @class Roo.bootstrap.DocumentSlider
33470 * @extends Roo.bootstrap.Component
33471 * Bootstrap DocumentSlider class
33474 * Create a new DocumentViewer
33475 * @param {Object} config The config object
33478 Roo.bootstrap.DocumentSlider = function(config){
33479 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33486 * Fire after initEvent
33487 * @param {Roo.bootstrap.DocumentSlider} this
33492 * Fire after update
33493 * @param {Roo.bootstrap.DocumentSlider} this
33499 * @param {Roo.bootstrap.DocumentSlider} this
33505 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33511 getAutoCreate : function()
33515 cls : 'roo-document-slider',
33519 cls : 'roo-document-slider-header',
33523 cls : 'roo-document-slider-header-title'
33529 cls : 'roo-document-slider-body',
33533 cls : 'roo-document-slider-prev',
33537 cls : 'fa fa-chevron-left'
33543 cls : 'roo-document-slider-thumb',
33547 cls : 'roo-document-slider-image'
33553 cls : 'roo-document-slider-next',
33557 cls : 'fa fa-chevron-right'
33569 initEvents : function()
33571 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33572 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33574 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33575 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33577 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33578 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33580 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33581 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33583 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33584 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33586 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33587 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33589 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33590 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33592 this.thumbEl.on('click', this.onClick, this);
33594 this.prevIndicator.on('click', this.prev, this);
33596 this.nextIndicator.on('click', this.next, this);
33600 initial : function()
33602 if(this.files.length){
33603 this.indicator = 1;
33607 this.fireEvent('initial', this);
33610 update : function()
33612 this.imageEl.attr('src', this.files[this.indicator - 1]);
33614 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33616 this.prevIndicator.show();
33618 if(this.indicator == 1){
33619 this.prevIndicator.hide();
33622 this.nextIndicator.show();
33624 if(this.indicator == this.files.length){
33625 this.nextIndicator.hide();
33628 this.thumbEl.scrollTo('top');
33630 this.fireEvent('update', this);
33633 onClick : function(e)
33635 e.preventDefault();
33637 this.fireEvent('click', this);
33642 e.preventDefault();
33644 this.indicator = Math.max(1, this.indicator - 1);
33651 e.preventDefault();
33653 this.indicator = Math.min(this.files.length, this.indicator + 1);
33667 * @class Roo.bootstrap.RadioSet
33668 * @extends Roo.bootstrap.Input
33669 * Bootstrap RadioSet class
33670 * @cfg {String} indicatorpos (left|right) default left
33671 * @cfg {Boolean} inline (true|false) inline the element (default true)
33672 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33674 * Create a new RadioSet
33675 * @param {Object} config The config object
33678 Roo.bootstrap.RadioSet = function(config){
33680 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33684 Roo.bootstrap.RadioSet.register(this);
33689 * Fires when the element is checked or unchecked.
33690 * @param {Roo.bootstrap.RadioSet} this This radio
33691 * @param {Roo.bootstrap.Radio} item The checked item
33696 * Fires when the element is click.
33697 * @param {Roo.bootstrap.RadioSet} this This radio set
33698 * @param {Roo.bootstrap.Radio} item The checked item
33699 * @param {Roo.EventObject} e The event object
33706 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33714 indicatorpos : 'left',
33716 getAutoCreate : function()
33720 cls : 'roo-radio-set-label',
33724 html : this.fieldLabel
33729 if(this.indicatorpos == 'left'){
33732 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33733 tooltip : 'This field is required'
33738 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33739 tooltip : 'This field is required'
33745 cls : 'roo-radio-set-items'
33748 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33750 if (align === 'left' && this.fieldLabel.length) {
33753 cls : "roo-radio-set-right",
33759 if(this.labelWidth > 12){
33760 label.style = "width: " + this.labelWidth + 'px';
33763 if(this.labelWidth < 13 && this.labelmd == 0){
33764 this.labelmd = this.labelWidth;
33767 if(this.labellg > 0){
33768 label.cls += ' col-lg-' + this.labellg;
33769 items.cls += ' col-lg-' + (12 - this.labellg);
33772 if(this.labelmd > 0){
33773 label.cls += ' col-md-' + this.labelmd;
33774 items.cls += ' col-md-' + (12 - this.labelmd);
33777 if(this.labelsm > 0){
33778 label.cls += ' col-sm-' + this.labelsm;
33779 items.cls += ' col-sm-' + (12 - this.labelsm);
33782 if(this.labelxs > 0){
33783 label.cls += ' col-xs-' + this.labelxs;
33784 items.cls += ' col-xs-' + (12 - this.labelxs);
33790 cls : 'roo-radio-set',
33794 cls : 'roo-radio-set-input',
33797 value : this.value ? this.value : ''
33804 if(this.weight.length){
33805 cfg.cls += ' roo-radio-' + this.weight;
33809 cfg.cls += ' roo-radio-set-inline';
33813 ['xs','sm','md','lg'].map(function(size){
33814 if (settings[size]) {
33815 cfg.cls += ' col-' + size + '-' + settings[size];
33823 initEvents : function()
33825 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33826 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33828 if(!this.fieldLabel.length){
33829 this.labelEl.hide();
33832 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33833 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33835 this.indicator = this.indicatorEl();
33837 if(this.indicator){
33838 this.indicator.addClass('invisible');
33841 this.originalValue = this.getValue();
33845 inputEl: function ()
33847 return this.el.select('.roo-radio-set-input', true).first();
33850 getChildContainer : function()
33852 return this.itemsEl;
33855 register : function(item)
33857 this.radioes.push(item);
33861 validate : function()
33863 if(this.getVisibilityEl().hasClass('hidden')){
33869 Roo.each(this.radioes, function(i){
33878 if(this.allowBlank) {
33882 if(this.disabled || valid){
33887 this.markInvalid();
33892 markValid : function()
33894 if(this.labelEl.isVisible(true)){
33895 this.indicatorEl().removeClass('visible');
33896 this.indicatorEl().addClass('invisible');
33899 this.el.removeClass([this.invalidClass, this.validClass]);
33900 this.el.addClass(this.validClass);
33902 this.fireEvent('valid', this);
33905 markInvalid : function(msg)
33907 if(this.allowBlank || this.disabled){
33911 if(this.labelEl.isVisible(true)){
33912 this.indicatorEl().removeClass('invisible');
33913 this.indicatorEl().addClass('visible');
33916 this.el.removeClass([this.invalidClass, this.validClass]);
33917 this.el.addClass(this.invalidClass);
33919 this.fireEvent('invalid', this, msg);
33923 setValue : function(v, suppressEvent)
33925 if(this.value === v){
33932 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33935 Roo.each(this.radioes, function(i){
33937 i.el.removeClass('checked');
33940 Roo.each(this.radioes, function(i){
33942 if(i.value === v || i.value.toString() === v.toString()){
33944 i.el.addClass('checked');
33946 if(suppressEvent !== true){
33947 this.fireEvent('check', this, i);
33958 clearInvalid : function(){
33960 if(!this.el || this.preventMark){
33964 this.el.removeClass([this.invalidClass]);
33966 this.fireEvent('valid', this);
33971 Roo.apply(Roo.bootstrap.RadioSet, {
33975 register : function(set)
33977 this.groups[set.name] = set;
33980 get: function(name)
33982 if (typeof(this.groups[name]) == 'undefined') {
33986 return this.groups[name] ;
33992 * Ext JS Library 1.1.1
33993 * Copyright(c) 2006-2007, Ext JS, LLC.
33995 * Originally Released Under LGPL - original licence link has changed is not relivant.
33998 * <script type="text/javascript">
34003 * @class Roo.bootstrap.SplitBar
34004 * @extends Roo.util.Observable
34005 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34009 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34010 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34011 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34012 split.minSize = 100;
34013 split.maxSize = 600;
34014 split.animate = true;
34015 split.on('moved', splitterMoved);
34018 * Create a new SplitBar
34019 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34020 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34021 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34022 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34023 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34024 position of the SplitBar).
34026 Roo.bootstrap.SplitBar = function(cfg){
34031 // dragElement : elm
34032 // resizingElement: el,
34034 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34035 // placement : Roo.bootstrap.SplitBar.LEFT ,
34036 // existingProxy ???
34039 this.el = Roo.get(cfg.dragElement, true);
34040 this.el.dom.unselectable = "on";
34042 this.resizingEl = Roo.get(cfg.resizingElement, true);
34046 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34047 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34050 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34053 * The minimum size of the resizing element. (Defaults to 0)
34059 * The maximum size of the resizing element. (Defaults to 2000)
34062 this.maxSize = 2000;
34065 * Whether to animate the transition to the new size
34068 this.animate = false;
34071 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34074 this.useShim = false;
34079 if(!cfg.existingProxy){
34081 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34083 this.proxy = Roo.get(cfg.existingProxy).dom;
34086 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34089 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34092 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34095 this.dragSpecs = {};
34098 * @private The adapter to use to positon and resize elements
34100 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34101 this.adapter.init(this);
34103 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34105 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34106 this.el.addClass("roo-splitbar-h");
34109 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34110 this.el.addClass("roo-splitbar-v");
34116 * Fires when the splitter is moved (alias for {@link #event-moved})
34117 * @param {Roo.bootstrap.SplitBar} this
34118 * @param {Number} newSize the new width or height
34123 * Fires when the splitter is moved
34124 * @param {Roo.bootstrap.SplitBar} this
34125 * @param {Number} newSize the new width or height
34129 * @event beforeresize
34130 * Fires before the splitter is dragged
34131 * @param {Roo.bootstrap.SplitBar} this
34133 "beforeresize" : true,
34135 "beforeapply" : true
34138 Roo.util.Observable.call(this);
34141 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34142 onStartProxyDrag : function(x, y){
34143 this.fireEvent("beforeresize", this);
34145 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34147 o.enableDisplayMode("block");
34148 // all splitbars share the same overlay
34149 Roo.bootstrap.SplitBar.prototype.overlay = o;
34151 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34152 this.overlay.show();
34153 Roo.get(this.proxy).setDisplayed("block");
34154 var size = this.adapter.getElementSize(this);
34155 this.activeMinSize = this.getMinimumSize();;
34156 this.activeMaxSize = this.getMaximumSize();;
34157 var c1 = size - this.activeMinSize;
34158 var c2 = Math.max(this.activeMaxSize - size, 0);
34159 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34160 this.dd.resetConstraints();
34161 this.dd.setXConstraint(
34162 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34163 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34165 this.dd.setYConstraint(0, 0);
34167 this.dd.resetConstraints();
34168 this.dd.setXConstraint(0, 0);
34169 this.dd.setYConstraint(
34170 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34171 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34174 this.dragSpecs.startSize = size;
34175 this.dragSpecs.startPoint = [x, y];
34176 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34180 * @private Called after the drag operation by the DDProxy
34182 onEndProxyDrag : function(e){
34183 Roo.get(this.proxy).setDisplayed(false);
34184 var endPoint = Roo.lib.Event.getXY(e);
34186 this.overlay.hide();
34189 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34190 newSize = this.dragSpecs.startSize +
34191 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34192 endPoint[0] - this.dragSpecs.startPoint[0] :
34193 this.dragSpecs.startPoint[0] - endPoint[0]
34196 newSize = this.dragSpecs.startSize +
34197 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34198 endPoint[1] - this.dragSpecs.startPoint[1] :
34199 this.dragSpecs.startPoint[1] - endPoint[1]
34202 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34203 if(newSize != this.dragSpecs.startSize){
34204 if(this.fireEvent('beforeapply', this, newSize) !== false){
34205 this.adapter.setElementSize(this, newSize);
34206 this.fireEvent("moved", this, newSize);
34207 this.fireEvent("resize", this, newSize);
34213 * Get the adapter this SplitBar uses
34214 * @return The adapter object
34216 getAdapter : function(){
34217 return this.adapter;
34221 * Set the adapter this SplitBar uses
34222 * @param {Object} adapter A SplitBar adapter object
34224 setAdapter : function(adapter){
34225 this.adapter = adapter;
34226 this.adapter.init(this);
34230 * Gets the minimum size for the resizing element
34231 * @return {Number} The minimum size
34233 getMinimumSize : function(){
34234 return this.minSize;
34238 * Sets the minimum size for the resizing element
34239 * @param {Number} minSize The minimum size
34241 setMinimumSize : function(minSize){
34242 this.minSize = minSize;
34246 * Gets the maximum size for the resizing element
34247 * @return {Number} The maximum size
34249 getMaximumSize : function(){
34250 return this.maxSize;
34254 * Sets the maximum size for the resizing element
34255 * @param {Number} maxSize The maximum size
34257 setMaximumSize : function(maxSize){
34258 this.maxSize = maxSize;
34262 * Sets the initialize size for the resizing element
34263 * @param {Number} size The initial size
34265 setCurrentSize : function(size){
34266 var oldAnimate = this.animate;
34267 this.animate = false;
34268 this.adapter.setElementSize(this, size);
34269 this.animate = oldAnimate;
34273 * Destroy this splitbar.
34274 * @param {Boolean} removeEl True to remove the element
34276 destroy : function(removeEl){
34278 this.shim.remove();
34281 this.proxy.parentNode.removeChild(this.proxy);
34289 * @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.
34291 Roo.bootstrap.SplitBar.createProxy = function(dir){
34292 var proxy = new Roo.Element(document.createElement("div"));
34293 proxy.unselectable();
34294 var cls = 'roo-splitbar-proxy';
34295 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34296 document.body.appendChild(proxy.dom);
34301 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34302 * Default Adapter. It assumes the splitter and resizing element are not positioned
34303 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34305 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34308 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34309 // do nothing for now
34310 init : function(s){
34314 * Called before drag operations to get the current size of the resizing element.
34315 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34317 getElementSize : function(s){
34318 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34319 return s.resizingEl.getWidth();
34321 return s.resizingEl.getHeight();
34326 * Called after drag operations to set the size of the resizing element.
34327 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34328 * @param {Number} newSize The new size to set
34329 * @param {Function} onComplete A function to be invoked when resizing is complete
34331 setElementSize : function(s, newSize, onComplete){
34332 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34334 s.resizingEl.setWidth(newSize);
34336 onComplete(s, newSize);
34339 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34344 s.resizingEl.setHeight(newSize);
34346 onComplete(s, newSize);
34349 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34356 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34357 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34358 * Adapter that moves the splitter element to align with the resized sizing element.
34359 * Used with an absolute positioned SplitBar.
34360 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34361 * document.body, make sure you assign an id to the body element.
34363 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34364 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34365 this.container = Roo.get(container);
34368 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34369 init : function(s){
34370 this.basic.init(s);
34373 getElementSize : function(s){
34374 return this.basic.getElementSize(s);
34377 setElementSize : function(s, newSize, onComplete){
34378 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34381 moveSplitter : function(s){
34382 var yes = Roo.bootstrap.SplitBar;
34383 switch(s.placement){
34385 s.el.setX(s.resizingEl.getRight());
34388 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34391 s.el.setY(s.resizingEl.getBottom());
34394 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34401 * Orientation constant - Create a vertical SplitBar
34405 Roo.bootstrap.SplitBar.VERTICAL = 1;
34408 * Orientation constant - Create a horizontal SplitBar
34412 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34415 * Placement constant - The resizing element is to the left of the splitter element
34419 Roo.bootstrap.SplitBar.LEFT = 1;
34422 * Placement constant - The resizing element is to the right of the splitter element
34426 Roo.bootstrap.SplitBar.RIGHT = 2;
34429 * Placement constant - The resizing element is positioned above the splitter element
34433 Roo.bootstrap.SplitBar.TOP = 3;
34436 * Placement constant - The resizing element is positioned under splitter element
34440 Roo.bootstrap.SplitBar.BOTTOM = 4;
34441 Roo.namespace("Roo.bootstrap.layout");/*
34443 * Ext JS Library 1.1.1
34444 * Copyright(c) 2006-2007, Ext JS, LLC.
34446 * Originally Released Under LGPL - original licence link has changed is not relivant.
34449 * <script type="text/javascript">
34453 * @class Roo.bootstrap.layout.Manager
34454 * @extends Roo.bootstrap.Component
34455 * Base class for layout managers.
34457 Roo.bootstrap.layout.Manager = function(config)
34459 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34465 /** false to disable window resize monitoring @type Boolean */
34466 this.monitorWindowResize = true;
34471 * Fires when a layout is performed.
34472 * @param {Roo.LayoutManager} this
34476 * @event regionresized
34477 * Fires when the user resizes a region.
34478 * @param {Roo.LayoutRegion} region The resized region
34479 * @param {Number} newSize The new size (width for east/west, height for north/south)
34481 "regionresized" : true,
34483 * @event regioncollapsed
34484 * Fires when a region is collapsed.
34485 * @param {Roo.LayoutRegion} region The collapsed region
34487 "regioncollapsed" : true,
34489 * @event regionexpanded
34490 * Fires when a region is expanded.
34491 * @param {Roo.LayoutRegion} region The expanded region
34493 "regionexpanded" : true
34495 this.updating = false;
34498 this.el = Roo.get(config.el);
34504 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34509 monitorWindowResize : true,
34515 onRender : function(ct, position)
34518 this.el = Roo.get(ct);
34521 //this.fireEvent('render',this);
34525 initEvents: function()
34529 // ie scrollbar fix
34530 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34531 document.body.scroll = "no";
34532 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34533 this.el.position('relative');
34535 this.id = this.el.id;
34536 this.el.addClass("roo-layout-container");
34537 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34538 if(this.el.dom != document.body ) {
34539 this.el.on('resize', this.layout,this);
34540 this.el.on('show', this.layout,this);
34546 * Returns true if this layout is currently being updated
34547 * @return {Boolean}
34549 isUpdating : function(){
34550 return this.updating;
34554 * Suspend the LayoutManager from doing auto-layouts while
34555 * making multiple add or remove calls
34557 beginUpdate : function(){
34558 this.updating = true;
34562 * Restore auto-layouts and optionally disable the manager from performing a layout
34563 * @param {Boolean} noLayout true to disable a layout update
34565 endUpdate : function(noLayout){
34566 this.updating = false;
34572 layout: function(){
34576 onRegionResized : function(region, newSize){
34577 this.fireEvent("regionresized", region, newSize);
34581 onRegionCollapsed : function(region){
34582 this.fireEvent("regioncollapsed", region);
34585 onRegionExpanded : function(region){
34586 this.fireEvent("regionexpanded", region);
34590 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34591 * performs box-model adjustments.
34592 * @return {Object} The size as an object {width: (the width), height: (the height)}
34594 getViewSize : function()
34597 if(this.el.dom != document.body){
34598 size = this.el.getSize();
34600 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34602 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34603 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34608 * Returns the Element this layout is bound to.
34609 * @return {Roo.Element}
34611 getEl : function(){
34616 * Returns the specified region.
34617 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34618 * @return {Roo.LayoutRegion}
34620 getRegion : function(target){
34621 return this.regions[target.toLowerCase()];
34624 onWindowResize : function(){
34625 if(this.monitorWindowResize){
34632 * Ext JS Library 1.1.1
34633 * Copyright(c) 2006-2007, Ext JS, LLC.
34635 * Originally Released Under LGPL - original licence link has changed is not relivant.
34638 * <script type="text/javascript">
34641 * @class Roo.bootstrap.layout.Border
34642 * @extends Roo.bootstrap.layout.Manager
34643 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34644 * please see: examples/bootstrap/nested.html<br><br>
34646 <b>The container the layout is rendered into can be either the body element or any other element.
34647 If it is not the body element, the container needs to either be an absolute positioned element,
34648 or you will need to add "position:relative" to the css of the container. You will also need to specify
34649 the container size if it is not the body element.</b>
34652 * Create a new Border
34653 * @param {Object} config Configuration options
34655 Roo.bootstrap.layout.Border = function(config){
34656 config = config || {};
34657 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34661 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34662 if(config[region]){
34663 config[region].region = region;
34664 this.addRegion(config[region]);
34670 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34672 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34674 * Creates and adds a new region if it doesn't already exist.
34675 * @param {String} target The target region key (north, south, east, west or center).
34676 * @param {Object} config The regions config object
34677 * @return {BorderLayoutRegion} The new region
34679 addRegion : function(config)
34681 if(!this.regions[config.region]){
34682 var r = this.factory(config);
34683 this.bindRegion(r);
34685 return this.regions[config.region];
34689 bindRegion : function(r){
34690 this.regions[r.config.region] = r;
34692 r.on("visibilitychange", this.layout, this);
34693 r.on("paneladded", this.layout, this);
34694 r.on("panelremoved", this.layout, this);
34695 r.on("invalidated", this.layout, this);
34696 r.on("resized", this.onRegionResized, this);
34697 r.on("collapsed", this.onRegionCollapsed, this);
34698 r.on("expanded", this.onRegionExpanded, this);
34702 * Performs a layout update.
34704 layout : function()
34706 if(this.updating) {
34710 // render all the rebions if they have not been done alreayd?
34711 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34712 if(this.regions[region] && !this.regions[region].bodyEl){
34713 this.regions[region].onRender(this.el)
34717 var size = this.getViewSize();
34718 var w = size.width;
34719 var h = size.height;
34724 //var x = 0, y = 0;
34726 var rs = this.regions;
34727 var north = rs["north"];
34728 var south = rs["south"];
34729 var west = rs["west"];
34730 var east = rs["east"];
34731 var center = rs["center"];
34732 //if(this.hideOnLayout){ // not supported anymore
34733 //c.el.setStyle("display", "none");
34735 if(north && north.isVisible()){
34736 var b = north.getBox();
34737 var m = north.getMargins();
34738 b.width = w - (m.left+m.right);
34741 centerY = b.height + b.y + m.bottom;
34742 centerH -= centerY;
34743 north.updateBox(this.safeBox(b));
34745 if(south && south.isVisible()){
34746 var b = south.getBox();
34747 var m = south.getMargins();
34748 b.width = w - (m.left+m.right);
34750 var totalHeight = (b.height + m.top + m.bottom);
34751 b.y = h - totalHeight + m.top;
34752 centerH -= totalHeight;
34753 south.updateBox(this.safeBox(b));
34755 if(west && west.isVisible()){
34756 var b = west.getBox();
34757 var m = west.getMargins();
34758 b.height = centerH - (m.top+m.bottom);
34760 b.y = centerY + m.top;
34761 var totalWidth = (b.width + m.left + m.right);
34762 centerX += totalWidth;
34763 centerW -= totalWidth;
34764 west.updateBox(this.safeBox(b));
34766 if(east && east.isVisible()){
34767 var b = east.getBox();
34768 var m = east.getMargins();
34769 b.height = centerH - (m.top+m.bottom);
34770 var totalWidth = (b.width + m.left + m.right);
34771 b.x = w - totalWidth + m.left;
34772 b.y = centerY + m.top;
34773 centerW -= totalWidth;
34774 east.updateBox(this.safeBox(b));
34777 var m = center.getMargins();
34779 x: centerX + m.left,
34780 y: centerY + m.top,
34781 width: centerW - (m.left+m.right),
34782 height: centerH - (m.top+m.bottom)
34784 //if(this.hideOnLayout){
34785 //center.el.setStyle("display", "block");
34787 center.updateBox(this.safeBox(centerBox));
34790 this.fireEvent("layout", this);
34794 safeBox : function(box){
34795 box.width = Math.max(0, box.width);
34796 box.height = Math.max(0, box.height);
34801 * Adds a ContentPanel (or subclass) to this layout.
34802 * @param {String} target The target region key (north, south, east, west or center).
34803 * @param {Roo.ContentPanel} panel The panel to add
34804 * @return {Roo.ContentPanel} The added panel
34806 add : function(target, panel){
34808 target = target.toLowerCase();
34809 return this.regions[target].add(panel);
34813 * Remove a ContentPanel (or subclass) to this layout.
34814 * @param {String} target The target region key (north, south, east, west or center).
34815 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34816 * @return {Roo.ContentPanel} The removed panel
34818 remove : function(target, panel){
34819 target = target.toLowerCase();
34820 return this.regions[target].remove(panel);
34824 * Searches all regions for a panel with the specified id
34825 * @param {String} panelId
34826 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34828 findPanel : function(panelId){
34829 var rs = this.regions;
34830 for(var target in rs){
34831 if(typeof rs[target] != "function"){
34832 var p = rs[target].getPanel(panelId);
34842 * Searches all regions for a panel with the specified id and activates (shows) it.
34843 * @param {String/ContentPanel} panelId The panels id or the panel itself
34844 * @return {Roo.ContentPanel} The shown panel or null
34846 showPanel : function(panelId) {
34847 var rs = this.regions;
34848 for(var target in rs){
34849 var r = rs[target];
34850 if(typeof r != "function"){
34851 if(r.hasPanel(panelId)){
34852 return r.showPanel(panelId);
34860 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34861 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34864 restoreState : function(provider){
34866 provider = Roo.state.Manager;
34868 var sm = new Roo.LayoutStateManager();
34869 sm.init(this, provider);
34875 * Adds a xtype elements to the layout.
34879 xtype : 'ContentPanel',
34886 xtype : 'NestedLayoutPanel',
34892 items : [ ... list of content panels or nested layout panels.. ]
34896 * @param {Object} cfg Xtype definition of item to add.
34898 addxtype : function(cfg)
34900 // basically accepts a pannel...
34901 // can accept a layout region..!?!?
34902 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34905 // theory? children can only be panels??
34907 //if (!cfg.xtype.match(/Panel$/)) {
34912 if (typeof(cfg.region) == 'undefined') {
34913 Roo.log("Failed to add Panel, region was not set");
34917 var region = cfg.region;
34923 xitems = cfg.items;
34930 case 'Content': // ContentPanel (el, cfg)
34931 case 'Scroll': // ContentPanel (el, cfg)
34933 cfg.autoCreate = true;
34934 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34936 // var el = this.el.createChild();
34937 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34940 this.add(region, ret);
34944 case 'TreePanel': // our new panel!
34945 cfg.el = this.el.createChild();
34946 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34947 this.add(region, ret);
34952 // create a new Layout (which is a Border Layout...
34954 var clayout = cfg.layout;
34955 clayout.el = this.el.createChild();
34956 clayout.items = clayout.items || [];
34960 // replace this exitems with the clayout ones..
34961 xitems = clayout.items;
34963 // force background off if it's in center...
34964 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34965 cfg.background = false;
34967 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34970 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34971 //console.log('adding nested layout panel ' + cfg.toSource());
34972 this.add(region, ret);
34973 nb = {}; /// find first...
34978 // needs grid and region
34980 //var el = this.getRegion(region).el.createChild();
34982 *var el = this.el.createChild();
34983 // create the grid first...
34984 cfg.grid.container = el;
34985 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34988 if (region == 'center' && this.active ) {
34989 cfg.background = false;
34992 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34994 this.add(region, ret);
34996 if (cfg.background) {
34997 // render grid on panel activation (if panel background)
34998 ret.on('activate', function(gp) {
34999 if (!gp.grid.rendered) {
35000 // gp.grid.render(el);
35004 // cfg.grid.render(el);
35010 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35011 // it was the old xcomponent building that caused this before.
35012 // espeically if border is the top element in the tree.
35022 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35024 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35025 this.add(region, ret);
35029 throw "Can not add '" + cfg.xtype + "' to Border";
35035 this.beginUpdate();
35039 Roo.each(xitems, function(i) {
35040 region = nb && i.region ? i.region : false;
35042 var add = ret.addxtype(i);
35045 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35046 if (!i.background) {
35047 abn[region] = nb[region] ;
35054 // make the last non-background panel active..
35055 //if (nb) { Roo.log(abn); }
35058 for(var r in abn) {
35059 region = this.getRegion(r);
35061 // tried using nb[r], but it does not work..
35063 region.showPanel(abn[r]);
35074 factory : function(cfg)
35077 var validRegions = Roo.bootstrap.layout.Border.regions;
35079 var target = cfg.region;
35082 var r = Roo.bootstrap.layout;
35086 return new r.North(cfg);
35088 return new r.South(cfg);
35090 return new r.East(cfg);
35092 return new r.West(cfg);
35094 return new r.Center(cfg);
35096 throw 'Layout region "'+target+'" not supported.';
35103 * Ext JS Library 1.1.1
35104 * Copyright(c) 2006-2007, Ext JS, LLC.
35106 * Originally Released Under LGPL - original licence link has changed is not relivant.
35109 * <script type="text/javascript">
35113 * @class Roo.bootstrap.layout.Basic
35114 * @extends Roo.util.Observable
35115 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35116 * and does not have a titlebar, tabs or any other features. All it does is size and position
35117 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35118 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35119 * @cfg {string} region the region that it inhabits..
35120 * @cfg {bool} skipConfig skip config?
35124 Roo.bootstrap.layout.Basic = function(config){
35126 this.mgr = config.mgr;
35128 this.position = config.region;
35130 var skipConfig = config.skipConfig;
35134 * @scope Roo.BasicLayoutRegion
35138 * @event beforeremove
35139 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35140 * @param {Roo.LayoutRegion} this
35141 * @param {Roo.ContentPanel} panel The panel
35142 * @param {Object} e The cancel event object
35144 "beforeremove" : true,
35146 * @event invalidated
35147 * Fires when the layout for this region is changed.
35148 * @param {Roo.LayoutRegion} this
35150 "invalidated" : true,
35152 * @event visibilitychange
35153 * Fires when this region is shown or hidden
35154 * @param {Roo.LayoutRegion} this
35155 * @param {Boolean} visibility true or false
35157 "visibilitychange" : true,
35159 * @event paneladded
35160 * Fires when a panel is added.
35161 * @param {Roo.LayoutRegion} this
35162 * @param {Roo.ContentPanel} panel The panel
35164 "paneladded" : true,
35166 * @event panelremoved
35167 * Fires when a panel is removed.
35168 * @param {Roo.LayoutRegion} this
35169 * @param {Roo.ContentPanel} panel The panel
35171 "panelremoved" : true,
35173 * @event beforecollapse
35174 * Fires when this region before collapse.
35175 * @param {Roo.LayoutRegion} this
35177 "beforecollapse" : true,
35180 * Fires when this region is collapsed.
35181 * @param {Roo.LayoutRegion} this
35183 "collapsed" : true,
35186 * Fires when this region is expanded.
35187 * @param {Roo.LayoutRegion} this
35192 * Fires when this region is slid into view.
35193 * @param {Roo.LayoutRegion} this
35195 "slideshow" : true,
35198 * Fires when this region slides out of view.
35199 * @param {Roo.LayoutRegion} this
35201 "slidehide" : true,
35203 * @event panelactivated
35204 * Fires when a panel is activated.
35205 * @param {Roo.LayoutRegion} this
35206 * @param {Roo.ContentPanel} panel The activated panel
35208 "panelactivated" : true,
35211 * Fires when the user resizes this region.
35212 * @param {Roo.LayoutRegion} this
35213 * @param {Number} newSize The new size (width for east/west, height for north/south)
35217 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35218 this.panels = new Roo.util.MixedCollection();
35219 this.panels.getKey = this.getPanelId.createDelegate(this);
35221 this.activePanel = null;
35222 // ensure listeners are added...
35224 if (config.listeners || config.events) {
35225 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35226 listeners : config.listeners || {},
35227 events : config.events || {}
35231 if(skipConfig !== true){
35232 this.applyConfig(config);
35236 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35238 getPanelId : function(p){
35242 applyConfig : function(config){
35243 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35244 this.config = config;
35249 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35250 * the width, for horizontal (north, south) the height.
35251 * @param {Number} newSize The new width or height
35253 resizeTo : function(newSize){
35254 var el = this.el ? this.el :
35255 (this.activePanel ? this.activePanel.getEl() : null);
35257 switch(this.position){
35260 el.setWidth(newSize);
35261 this.fireEvent("resized", this, newSize);
35265 el.setHeight(newSize);
35266 this.fireEvent("resized", this, newSize);
35272 getBox : function(){
35273 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35276 getMargins : function(){
35277 return this.margins;
35280 updateBox : function(box){
35282 var el = this.activePanel.getEl();
35283 el.dom.style.left = box.x + "px";
35284 el.dom.style.top = box.y + "px";
35285 this.activePanel.setSize(box.width, box.height);
35289 * Returns the container element for this region.
35290 * @return {Roo.Element}
35292 getEl : function(){
35293 return this.activePanel;
35297 * Returns true if this region is currently visible.
35298 * @return {Boolean}
35300 isVisible : function(){
35301 return this.activePanel ? true : false;
35304 setActivePanel : function(panel){
35305 panel = this.getPanel(panel);
35306 if(this.activePanel && this.activePanel != panel){
35307 this.activePanel.setActiveState(false);
35308 this.activePanel.getEl().setLeftTop(-10000,-10000);
35310 this.activePanel = panel;
35311 panel.setActiveState(true);
35313 panel.setSize(this.box.width, this.box.height);
35315 this.fireEvent("panelactivated", this, panel);
35316 this.fireEvent("invalidated");
35320 * Show the specified panel.
35321 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35322 * @return {Roo.ContentPanel} The shown panel or null
35324 showPanel : function(panel){
35325 panel = this.getPanel(panel);
35327 this.setActivePanel(panel);
35333 * Get the active panel for this region.
35334 * @return {Roo.ContentPanel} The active panel or null
35336 getActivePanel : function(){
35337 return this.activePanel;
35341 * Add the passed ContentPanel(s)
35342 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35343 * @return {Roo.ContentPanel} The panel added (if only one was added)
35345 add : function(panel){
35346 if(arguments.length > 1){
35347 for(var i = 0, len = arguments.length; i < len; i++) {
35348 this.add(arguments[i]);
35352 if(this.hasPanel(panel)){
35353 this.showPanel(panel);
35356 var el = panel.getEl();
35357 if(el.dom.parentNode != this.mgr.el.dom){
35358 this.mgr.el.dom.appendChild(el.dom);
35360 if(panel.setRegion){
35361 panel.setRegion(this);
35363 this.panels.add(panel);
35364 el.setStyle("position", "absolute");
35365 if(!panel.background){
35366 this.setActivePanel(panel);
35367 if(this.config.initialSize && this.panels.getCount()==1){
35368 this.resizeTo(this.config.initialSize);
35371 this.fireEvent("paneladded", this, panel);
35376 * Returns true if the panel is in this region.
35377 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35378 * @return {Boolean}
35380 hasPanel : function(panel){
35381 if(typeof panel == "object"){ // must be panel obj
35382 panel = panel.getId();
35384 return this.getPanel(panel) ? true : false;
35388 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35389 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35390 * @param {Boolean} preservePanel Overrides the config preservePanel option
35391 * @return {Roo.ContentPanel} The panel that was removed
35393 remove : function(panel, preservePanel){
35394 panel = this.getPanel(panel);
35399 this.fireEvent("beforeremove", this, panel, e);
35400 if(e.cancel === true){
35403 var panelId = panel.getId();
35404 this.panels.removeKey(panelId);
35409 * Returns the panel specified or null if it's not in this region.
35410 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35411 * @return {Roo.ContentPanel}
35413 getPanel : function(id){
35414 if(typeof id == "object"){ // must be panel obj
35417 return this.panels.get(id);
35421 * Returns this regions position (north/south/east/west/center).
35424 getPosition: function(){
35425 return this.position;
35429 * Ext JS Library 1.1.1
35430 * Copyright(c) 2006-2007, Ext JS, LLC.
35432 * Originally Released Under LGPL - original licence link has changed is not relivant.
35435 * <script type="text/javascript">
35439 * @class Roo.bootstrap.layout.Region
35440 * @extends Roo.bootstrap.layout.Basic
35441 * This class represents a region in a layout manager.
35443 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35444 * @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})
35445 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35446 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35447 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35448 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35449 * @cfg {String} title The title for the region (overrides panel titles)
35450 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35451 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35452 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35453 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35454 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35455 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35456 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35457 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35458 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35459 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35461 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35462 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35463 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35464 * @cfg {Number} width For East/West panels
35465 * @cfg {Number} height For North/South panels
35466 * @cfg {Boolean} split To show the splitter
35467 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35469 * @cfg {string} cls Extra CSS classes to add to region
35471 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35472 * @cfg {string} region the region that it inhabits..
35475 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35476 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35478 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35479 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35480 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35482 Roo.bootstrap.layout.Region = function(config)
35484 this.applyConfig(config);
35486 var mgr = config.mgr;
35487 var pos = config.region;
35488 config.skipConfig = true;
35489 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35492 this.onRender(mgr.el);
35495 this.visible = true;
35496 this.collapsed = false;
35497 this.unrendered_panels = [];
35500 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35502 position: '', // set by wrapper (eg. north/south etc..)
35503 unrendered_panels : null, // unrendered panels.
35504 createBody : function(){
35505 /** This region's body element
35506 * @type Roo.Element */
35507 this.bodyEl = this.el.createChild({
35509 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35513 onRender: function(ctr, pos)
35515 var dh = Roo.DomHelper;
35516 /** This region's container element
35517 * @type Roo.Element */
35518 this.el = dh.append(ctr.dom, {
35520 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35522 /** This region's title element
35523 * @type Roo.Element */
35525 this.titleEl = dh.append(this.el.dom,
35528 unselectable: "on",
35529 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35531 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35532 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35535 this.titleEl.enableDisplayMode();
35536 /** This region's title text element
35537 * @type HTMLElement */
35538 this.titleTextEl = this.titleEl.dom.firstChild;
35539 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35541 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35542 this.closeBtn.enableDisplayMode();
35543 this.closeBtn.on("click", this.closeClicked, this);
35544 this.closeBtn.hide();
35546 this.createBody(this.config);
35547 if(this.config.hideWhenEmpty){
35549 this.on("paneladded", this.validateVisibility, this);
35550 this.on("panelremoved", this.validateVisibility, this);
35552 if(this.autoScroll){
35553 this.bodyEl.setStyle("overflow", "auto");
35555 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35557 //if(c.titlebar !== false){
35558 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35559 this.titleEl.hide();
35561 this.titleEl.show();
35562 if(this.config.title){
35563 this.titleTextEl.innerHTML = this.config.title;
35567 if(this.config.collapsed){
35568 this.collapse(true);
35570 if(this.config.hidden){
35574 if (this.unrendered_panels && this.unrendered_panels.length) {
35575 for (var i =0;i< this.unrendered_panels.length; i++) {
35576 this.add(this.unrendered_panels[i]);
35578 this.unrendered_panels = null;
35584 applyConfig : function(c)
35587 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35588 var dh = Roo.DomHelper;
35589 if(c.titlebar !== false){
35590 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35591 this.collapseBtn.on("click", this.collapse, this);
35592 this.collapseBtn.enableDisplayMode();
35594 if(c.showPin === true || this.showPin){
35595 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35596 this.stickBtn.enableDisplayMode();
35597 this.stickBtn.on("click", this.expand, this);
35598 this.stickBtn.hide();
35603 /** This region's collapsed element
35604 * @type Roo.Element */
35607 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35608 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35611 if(c.floatable !== false){
35612 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35613 this.collapsedEl.on("click", this.collapseClick, this);
35616 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35617 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35618 id: "message", unselectable: "on", style:{"float":"left"}});
35619 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35621 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35622 this.expandBtn.on("click", this.expand, this);
35626 if(this.collapseBtn){
35627 this.collapseBtn.setVisible(c.collapsible == true);
35630 this.cmargins = c.cmargins || this.cmargins ||
35631 (this.position == "west" || this.position == "east" ?
35632 {top: 0, left: 2, right:2, bottom: 0} :
35633 {top: 2, left: 0, right:0, bottom: 2});
35635 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35638 this.bottomTabs = c.tabPosition != "top";
35640 this.autoScroll = c.autoScroll || false;
35645 this.duration = c.duration || .30;
35646 this.slideDuration = c.slideDuration || .45;
35651 * Returns true if this region is currently visible.
35652 * @return {Boolean}
35654 isVisible : function(){
35655 return this.visible;
35659 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35660 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35662 //setCollapsedTitle : function(title){
35663 // title = title || " ";
35664 // if(this.collapsedTitleTextEl){
35665 // this.collapsedTitleTextEl.innerHTML = title;
35669 getBox : function(){
35671 // if(!this.collapsed){
35672 b = this.el.getBox(false, true);
35674 // b = this.collapsedEl.getBox(false, true);
35679 getMargins : function(){
35680 return this.margins;
35681 //return this.collapsed ? this.cmargins : this.margins;
35684 highlight : function(){
35685 this.el.addClass("x-layout-panel-dragover");
35688 unhighlight : function(){
35689 this.el.removeClass("x-layout-panel-dragover");
35692 updateBox : function(box)
35694 if (!this.bodyEl) {
35695 return; // not rendered yet..
35699 if(!this.collapsed){
35700 this.el.dom.style.left = box.x + "px";
35701 this.el.dom.style.top = box.y + "px";
35702 this.updateBody(box.width, box.height);
35704 this.collapsedEl.dom.style.left = box.x + "px";
35705 this.collapsedEl.dom.style.top = box.y + "px";
35706 this.collapsedEl.setSize(box.width, box.height);
35709 this.tabs.autoSizeTabs();
35713 updateBody : function(w, h)
35716 this.el.setWidth(w);
35717 w -= this.el.getBorderWidth("rl");
35718 if(this.config.adjustments){
35719 w += this.config.adjustments[0];
35722 if(h !== null && h > 0){
35723 this.el.setHeight(h);
35724 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35725 h -= this.el.getBorderWidth("tb");
35726 if(this.config.adjustments){
35727 h += this.config.adjustments[1];
35729 this.bodyEl.setHeight(h);
35731 h = this.tabs.syncHeight(h);
35734 if(this.panelSize){
35735 w = w !== null ? w : this.panelSize.width;
35736 h = h !== null ? h : this.panelSize.height;
35738 if(this.activePanel){
35739 var el = this.activePanel.getEl();
35740 w = w !== null ? w : el.getWidth();
35741 h = h !== null ? h : el.getHeight();
35742 this.panelSize = {width: w, height: h};
35743 this.activePanel.setSize(w, h);
35745 if(Roo.isIE && this.tabs){
35746 this.tabs.el.repaint();
35751 * Returns the container element for this region.
35752 * @return {Roo.Element}
35754 getEl : function(){
35759 * Hides this region.
35762 //if(!this.collapsed){
35763 this.el.dom.style.left = "-2000px";
35766 // this.collapsedEl.dom.style.left = "-2000px";
35767 // this.collapsedEl.hide();
35769 this.visible = false;
35770 this.fireEvent("visibilitychange", this, false);
35774 * Shows this region if it was previously hidden.
35777 //if(!this.collapsed){
35780 // this.collapsedEl.show();
35782 this.visible = true;
35783 this.fireEvent("visibilitychange", this, true);
35786 closeClicked : function(){
35787 if(this.activePanel){
35788 this.remove(this.activePanel);
35792 collapseClick : function(e){
35794 e.stopPropagation();
35797 e.stopPropagation();
35803 * Collapses this region.
35804 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35807 collapse : function(skipAnim, skipCheck = false){
35808 if(this.collapsed) {
35812 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35814 this.collapsed = true;
35816 this.split.el.hide();
35818 if(this.config.animate && skipAnim !== true){
35819 this.fireEvent("invalidated", this);
35820 this.animateCollapse();
35822 this.el.setLocation(-20000,-20000);
35824 this.collapsedEl.show();
35825 this.fireEvent("collapsed", this);
35826 this.fireEvent("invalidated", this);
35832 animateCollapse : function(){
35837 * Expands this region if it was previously collapsed.
35838 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35839 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35842 expand : function(e, skipAnim){
35844 e.stopPropagation();
35846 if(!this.collapsed || this.el.hasActiveFx()) {
35850 this.afterSlideIn();
35853 this.collapsed = false;
35854 if(this.config.animate && skipAnim !== true){
35855 this.animateExpand();
35859 this.split.el.show();
35861 this.collapsedEl.setLocation(-2000,-2000);
35862 this.collapsedEl.hide();
35863 this.fireEvent("invalidated", this);
35864 this.fireEvent("expanded", this);
35868 animateExpand : function(){
35872 initTabs : function()
35874 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35876 var ts = new Roo.bootstrap.panel.Tabs({
35877 el: this.bodyEl.dom,
35878 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35879 disableTooltips: this.config.disableTabTips,
35880 toolbar : this.config.toolbar
35883 if(this.config.hideTabs){
35884 ts.stripWrap.setDisplayed(false);
35887 ts.resizeTabs = this.config.resizeTabs === true;
35888 ts.minTabWidth = this.config.minTabWidth || 40;
35889 ts.maxTabWidth = this.config.maxTabWidth || 250;
35890 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35891 ts.monitorResize = false;
35892 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35893 ts.bodyEl.addClass('roo-layout-tabs-body');
35894 this.panels.each(this.initPanelAsTab, this);
35897 initPanelAsTab : function(panel){
35898 var ti = this.tabs.addTab(
35902 this.config.closeOnTab && panel.isClosable(),
35905 if(panel.tabTip !== undefined){
35906 ti.setTooltip(panel.tabTip);
35908 ti.on("activate", function(){
35909 this.setActivePanel(panel);
35912 if(this.config.closeOnTab){
35913 ti.on("beforeclose", function(t, e){
35915 this.remove(panel);
35919 panel.tabItem = ti;
35924 updatePanelTitle : function(panel, title)
35926 if(this.activePanel == panel){
35927 this.updateTitle(title);
35930 var ti = this.tabs.getTab(panel.getEl().id);
35932 if(panel.tabTip !== undefined){
35933 ti.setTooltip(panel.tabTip);
35938 updateTitle : function(title){
35939 if(this.titleTextEl && !this.config.title){
35940 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35944 setActivePanel : function(panel)
35946 panel = this.getPanel(panel);
35947 if(this.activePanel && this.activePanel != panel){
35948 if(this.activePanel.setActiveState(false) === false){
35952 this.activePanel = panel;
35953 panel.setActiveState(true);
35954 if(this.panelSize){
35955 panel.setSize(this.panelSize.width, this.panelSize.height);
35958 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35960 this.updateTitle(panel.getTitle());
35962 this.fireEvent("invalidated", this);
35964 this.fireEvent("panelactivated", this, panel);
35968 * Shows the specified panel.
35969 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35970 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35972 showPanel : function(panel)
35974 panel = this.getPanel(panel);
35977 var tab = this.tabs.getTab(panel.getEl().id);
35978 if(tab.isHidden()){
35979 this.tabs.unhideTab(tab.id);
35983 this.setActivePanel(panel);
35990 * Get the active panel for this region.
35991 * @return {Roo.ContentPanel} The active panel or null
35993 getActivePanel : function(){
35994 return this.activePanel;
35997 validateVisibility : function(){
35998 if(this.panels.getCount() < 1){
35999 this.updateTitle(" ");
36000 this.closeBtn.hide();
36003 if(!this.isVisible()){
36010 * Adds the passed ContentPanel(s) to this region.
36011 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36012 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36014 add : function(panel)
36016 if(arguments.length > 1){
36017 for(var i = 0, len = arguments.length; i < len; i++) {
36018 this.add(arguments[i]);
36023 // if we have not been rendered yet, then we can not really do much of this..
36024 if (!this.bodyEl) {
36025 this.unrendered_panels.push(panel);
36032 if(this.hasPanel(panel)){
36033 this.showPanel(panel);
36036 panel.setRegion(this);
36037 this.panels.add(panel);
36038 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36039 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36040 // and hide them... ???
36041 this.bodyEl.dom.appendChild(panel.getEl().dom);
36042 if(panel.background !== true){
36043 this.setActivePanel(panel);
36045 this.fireEvent("paneladded", this, panel);
36052 this.initPanelAsTab(panel);
36056 if(panel.background !== true){
36057 this.tabs.activate(panel.getEl().id);
36059 this.fireEvent("paneladded", this, panel);
36064 * Hides the tab for the specified panel.
36065 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36067 hidePanel : function(panel){
36068 if(this.tabs && (panel = this.getPanel(panel))){
36069 this.tabs.hideTab(panel.getEl().id);
36074 * Unhides the tab for a previously hidden panel.
36075 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36077 unhidePanel : function(panel){
36078 if(this.tabs && (panel = this.getPanel(panel))){
36079 this.tabs.unhideTab(panel.getEl().id);
36083 clearPanels : function(){
36084 while(this.panels.getCount() > 0){
36085 this.remove(this.panels.first());
36090 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36091 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36092 * @param {Boolean} preservePanel Overrides the config preservePanel option
36093 * @return {Roo.ContentPanel} The panel that was removed
36095 remove : function(panel, preservePanel)
36097 panel = this.getPanel(panel);
36102 this.fireEvent("beforeremove", this, panel, e);
36103 if(e.cancel === true){
36106 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36107 var panelId = panel.getId();
36108 this.panels.removeKey(panelId);
36110 document.body.appendChild(panel.getEl().dom);
36113 this.tabs.removeTab(panel.getEl().id);
36114 }else if (!preservePanel){
36115 this.bodyEl.dom.removeChild(panel.getEl().dom);
36117 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36118 var p = this.panels.first();
36119 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36120 tempEl.appendChild(p.getEl().dom);
36121 this.bodyEl.update("");
36122 this.bodyEl.dom.appendChild(p.getEl().dom);
36124 this.updateTitle(p.getTitle());
36126 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36127 this.setActivePanel(p);
36129 panel.setRegion(null);
36130 if(this.activePanel == panel){
36131 this.activePanel = null;
36133 if(this.config.autoDestroy !== false && preservePanel !== true){
36134 try{panel.destroy();}catch(e){}
36136 this.fireEvent("panelremoved", this, panel);
36141 * Returns the TabPanel component used by this region
36142 * @return {Roo.TabPanel}
36144 getTabs : function(){
36148 createTool : function(parentEl, className){
36149 var btn = Roo.DomHelper.append(parentEl, {
36151 cls: "x-layout-tools-button",
36154 cls: "roo-layout-tools-button-inner " + className,
36158 btn.addClassOnOver("roo-layout-tools-button-over");
36163 * Ext JS Library 1.1.1
36164 * Copyright(c) 2006-2007, Ext JS, LLC.
36166 * Originally Released Under LGPL - original licence link has changed is not relivant.
36169 * <script type="text/javascript">
36175 * @class Roo.SplitLayoutRegion
36176 * @extends Roo.LayoutRegion
36177 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36179 Roo.bootstrap.layout.Split = function(config){
36180 this.cursor = config.cursor;
36181 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36184 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36186 splitTip : "Drag to resize.",
36187 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36188 useSplitTips : false,
36190 applyConfig : function(config){
36191 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36194 onRender : function(ctr,pos) {
36196 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36197 if(!this.config.split){
36202 var splitEl = Roo.DomHelper.append(ctr.dom, {
36204 id: this.el.id + "-split",
36205 cls: "roo-layout-split roo-layout-split-"+this.position,
36208 /** The SplitBar for this region
36209 * @type Roo.SplitBar */
36210 // does not exist yet...
36211 Roo.log([this.position, this.orientation]);
36213 this.split = new Roo.bootstrap.SplitBar({
36214 dragElement : splitEl,
36215 resizingElement: this.el,
36216 orientation : this.orientation
36219 this.split.on("moved", this.onSplitMove, this);
36220 this.split.useShim = this.config.useShim === true;
36221 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36222 if(this.useSplitTips){
36223 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36225 //if(config.collapsible){
36226 // this.split.el.on("dblclick", this.collapse, this);
36229 if(typeof this.config.minSize != "undefined"){
36230 this.split.minSize = this.config.minSize;
36232 if(typeof this.config.maxSize != "undefined"){
36233 this.split.maxSize = this.config.maxSize;
36235 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36236 this.hideSplitter();
36241 getHMaxSize : function(){
36242 var cmax = this.config.maxSize || 10000;
36243 var center = this.mgr.getRegion("center");
36244 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36247 getVMaxSize : function(){
36248 var cmax = this.config.maxSize || 10000;
36249 var center = this.mgr.getRegion("center");
36250 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36253 onSplitMove : function(split, newSize){
36254 this.fireEvent("resized", this, newSize);
36258 * Returns the {@link Roo.SplitBar} for this region.
36259 * @return {Roo.SplitBar}
36261 getSplitBar : function(){
36266 this.hideSplitter();
36267 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36270 hideSplitter : function(){
36272 this.split.el.setLocation(-2000,-2000);
36273 this.split.el.hide();
36279 this.split.el.show();
36281 Roo.bootstrap.layout.Split.superclass.show.call(this);
36284 beforeSlide: function(){
36285 if(Roo.isGecko){// firefox overflow auto bug workaround
36286 this.bodyEl.clip();
36288 this.tabs.bodyEl.clip();
36290 if(this.activePanel){
36291 this.activePanel.getEl().clip();
36293 if(this.activePanel.beforeSlide){
36294 this.activePanel.beforeSlide();
36300 afterSlide : function(){
36301 if(Roo.isGecko){// firefox overflow auto bug workaround
36302 this.bodyEl.unclip();
36304 this.tabs.bodyEl.unclip();
36306 if(this.activePanel){
36307 this.activePanel.getEl().unclip();
36308 if(this.activePanel.afterSlide){
36309 this.activePanel.afterSlide();
36315 initAutoHide : function(){
36316 if(this.autoHide !== false){
36317 if(!this.autoHideHd){
36318 var st = new Roo.util.DelayedTask(this.slideIn, this);
36319 this.autoHideHd = {
36320 "mouseout": function(e){
36321 if(!e.within(this.el, true)){
36325 "mouseover" : function(e){
36331 this.el.on(this.autoHideHd);
36335 clearAutoHide : function(){
36336 if(this.autoHide !== false){
36337 this.el.un("mouseout", this.autoHideHd.mouseout);
36338 this.el.un("mouseover", this.autoHideHd.mouseover);
36342 clearMonitor : function(){
36343 Roo.get(document).un("click", this.slideInIf, this);
36346 // these names are backwards but not changed for compat
36347 slideOut : function(){
36348 if(this.isSlid || this.el.hasActiveFx()){
36351 this.isSlid = true;
36352 if(this.collapseBtn){
36353 this.collapseBtn.hide();
36355 this.closeBtnState = this.closeBtn.getStyle('display');
36356 this.closeBtn.hide();
36358 this.stickBtn.show();
36361 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36362 this.beforeSlide();
36363 this.el.setStyle("z-index", 10001);
36364 this.el.slideIn(this.getSlideAnchor(), {
36365 callback: function(){
36367 this.initAutoHide();
36368 Roo.get(document).on("click", this.slideInIf, this);
36369 this.fireEvent("slideshow", this);
36376 afterSlideIn : function(){
36377 this.clearAutoHide();
36378 this.isSlid = false;
36379 this.clearMonitor();
36380 this.el.setStyle("z-index", "");
36381 if(this.collapseBtn){
36382 this.collapseBtn.show();
36384 this.closeBtn.setStyle('display', this.closeBtnState);
36386 this.stickBtn.hide();
36388 this.fireEvent("slidehide", this);
36391 slideIn : function(cb){
36392 if(!this.isSlid || this.el.hasActiveFx()){
36396 this.isSlid = false;
36397 this.beforeSlide();
36398 this.el.slideOut(this.getSlideAnchor(), {
36399 callback: function(){
36400 this.el.setLeftTop(-10000, -10000);
36402 this.afterSlideIn();
36410 slideInIf : function(e){
36411 if(!e.within(this.el)){
36416 animateCollapse : function(){
36417 this.beforeSlide();
36418 this.el.setStyle("z-index", 20000);
36419 var anchor = this.getSlideAnchor();
36420 this.el.slideOut(anchor, {
36421 callback : function(){
36422 this.el.setStyle("z-index", "");
36423 this.collapsedEl.slideIn(anchor, {duration:.3});
36425 this.el.setLocation(-10000,-10000);
36427 this.fireEvent("collapsed", this);
36434 animateExpand : function(){
36435 this.beforeSlide();
36436 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36437 this.el.setStyle("z-index", 20000);
36438 this.collapsedEl.hide({
36441 this.el.slideIn(this.getSlideAnchor(), {
36442 callback : function(){
36443 this.el.setStyle("z-index", "");
36446 this.split.el.show();
36448 this.fireEvent("invalidated", this);
36449 this.fireEvent("expanded", this);
36477 getAnchor : function(){
36478 return this.anchors[this.position];
36481 getCollapseAnchor : function(){
36482 return this.canchors[this.position];
36485 getSlideAnchor : function(){
36486 return this.sanchors[this.position];
36489 getAlignAdj : function(){
36490 var cm = this.cmargins;
36491 switch(this.position){
36507 getExpandAdj : function(){
36508 var c = this.collapsedEl, cm = this.cmargins;
36509 switch(this.position){
36511 return [-(cm.right+c.getWidth()+cm.left), 0];
36514 return [cm.right+c.getWidth()+cm.left, 0];
36517 return [0, -(cm.top+cm.bottom+c.getHeight())];
36520 return [0, cm.top+cm.bottom+c.getHeight()];
36526 * Ext JS Library 1.1.1
36527 * Copyright(c) 2006-2007, Ext JS, LLC.
36529 * Originally Released Under LGPL - original licence link has changed is not relivant.
36532 * <script type="text/javascript">
36535 * These classes are private internal classes
36537 Roo.bootstrap.layout.Center = function(config){
36538 config.region = "center";
36539 Roo.bootstrap.layout.Region.call(this, config);
36540 this.visible = true;
36541 this.minWidth = config.minWidth || 20;
36542 this.minHeight = config.minHeight || 20;
36545 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36547 // center panel can't be hidden
36551 // center panel can't be hidden
36554 getMinWidth: function(){
36555 return this.minWidth;
36558 getMinHeight: function(){
36559 return this.minHeight;
36572 Roo.bootstrap.layout.North = function(config)
36574 config.region = 'north';
36575 config.cursor = 'n-resize';
36577 Roo.bootstrap.layout.Split.call(this, config);
36581 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36582 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36583 this.split.el.addClass("roo-layout-split-v");
36585 var size = config.initialSize || config.height;
36586 if(typeof size != "undefined"){
36587 this.el.setHeight(size);
36590 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36592 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36596 getBox : function(){
36597 if(this.collapsed){
36598 return this.collapsedEl.getBox();
36600 var box = this.el.getBox();
36602 box.height += this.split.el.getHeight();
36607 updateBox : function(box){
36608 if(this.split && !this.collapsed){
36609 box.height -= this.split.el.getHeight();
36610 this.split.el.setLeft(box.x);
36611 this.split.el.setTop(box.y+box.height);
36612 this.split.el.setWidth(box.width);
36614 if(this.collapsed){
36615 this.updateBody(box.width, null);
36617 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36625 Roo.bootstrap.layout.South = function(config){
36626 config.region = 'south';
36627 config.cursor = 's-resize';
36628 Roo.bootstrap.layout.Split.call(this, config);
36630 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36631 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36632 this.split.el.addClass("roo-layout-split-v");
36634 var size = config.initialSize || config.height;
36635 if(typeof size != "undefined"){
36636 this.el.setHeight(size);
36640 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36641 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36642 getBox : function(){
36643 if(this.collapsed){
36644 return this.collapsedEl.getBox();
36646 var box = this.el.getBox();
36648 var sh = this.split.el.getHeight();
36655 updateBox : function(box){
36656 if(this.split && !this.collapsed){
36657 var sh = this.split.el.getHeight();
36660 this.split.el.setLeft(box.x);
36661 this.split.el.setTop(box.y-sh);
36662 this.split.el.setWidth(box.width);
36664 if(this.collapsed){
36665 this.updateBody(box.width, null);
36667 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36671 Roo.bootstrap.layout.East = function(config){
36672 config.region = "east";
36673 config.cursor = "e-resize";
36674 Roo.bootstrap.layout.Split.call(this, config);
36676 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36677 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36678 this.split.el.addClass("roo-layout-split-h");
36680 var size = config.initialSize || config.width;
36681 if(typeof size != "undefined"){
36682 this.el.setWidth(size);
36685 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36686 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36687 getBox : function(){
36688 if(this.collapsed){
36689 return this.collapsedEl.getBox();
36691 var box = this.el.getBox();
36693 var sw = this.split.el.getWidth();
36700 updateBox : function(box){
36701 if(this.split && !this.collapsed){
36702 var sw = this.split.el.getWidth();
36704 this.split.el.setLeft(box.x);
36705 this.split.el.setTop(box.y);
36706 this.split.el.setHeight(box.height);
36709 if(this.collapsed){
36710 this.updateBody(null, box.height);
36712 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36716 Roo.bootstrap.layout.West = function(config){
36717 config.region = "west";
36718 config.cursor = "w-resize";
36720 Roo.bootstrap.layout.Split.call(this, config);
36722 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36723 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36724 this.split.el.addClass("roo-layout-split-h");
36728 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36729 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36731 onRender: function(ctr, pos)
36733 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36734 var size = this.config.initialSize || this.config.width;
36735 if(typeof size != "undefined"){
36736 this.el.setWidth(size);
36740 getBox : function(){
36741 if(this.collapsed){
36742 return this.collapsedEl.getBox();
36744 var box = this.el.getBox();
36746 box.width += this.split.el.getWidth();
36751 updateBox : function(box){
36752 if(this.split && !this.collapsed){
36753 var sw = this.split.el.getWidth();
36755 this.split.el.setLeft(box.x+box.width);
36756 this.split.el.setTop(box.y);
36757 this.split.el.setHeight(box.height);
36759 if(this.collapsed){
36760 this.updateBody(null, box.height);
36762 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36765 Roo.namespace("Roo.bootstrap.panel");/*
36767 * Ext JS Library 1.1.1
36768 * Copyright(c) 2006-2007, Ext JS, LLC.
36770 * Originally Released Under LGPL - original licence link has changed is not relivant.
36773 * <script type="text/javascript">
36776 * @class Roo.ContentPanel
36777 * @extends Roo.util.Observable
36778 * A basic ContentPanel element.
36779 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36780 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36781 * @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
36782 * @cfg {Boolean} closable True if the panel can be closed/removed
36783 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36784 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36785 * @cfg {Toolbar} toolbar A toolbar for this panel
36786 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36787 * @cfg {String} title The title for this panel
36788 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36789 * @cfg {String} url Calls {@link #setUrl} with this value
36790 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36791 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36792 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36793 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36794 * @cfg {Boolean} badges render the badges
36797 * Create a new ContentPanel.
36798 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36799 * @param {String/Object} config A string to set only the title or a config object
36800 * @param {String} content (optional) Set the HTML content for this panel
36801 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36803 Roo.bootstrap.panel.Content = function( config){
36805 this.tpl = config.tpl || false;
36807 var el = config.el;
36808 var content = config.content;
36810 if(config.autoCreate){ // xtype is available if this is called from factory
36813 this.el = Roo.get(el);
36814 if(!this.el && config && config.autoCreate){
36815 if(typeof config.autoCreate == "object"){
36816 if(!config.autoCreate.id){
36817 config.autoCreate.id = config.id||el;
36819 this.el = Roo.DomHelper.append(document.body,
36820 config.autoCreate, true);
36822 var elcfg = { tag: "div",
36823 cls: "roo-layout-inactive-content",
36827 elcfg.html = config.html;
36831 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36834 this.closable = false;
36835 this.loaded = false;
36836 this.active = false;
36839 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36841 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36843 this.wrapEl = this.el; //this.el.wrap();
36845 if (config.toolbar.items) {
36846 ti = config.toolbar.items ;
36847 delete config.toolbar.items ;
36851 this.toolbar.render(this.wrapEl, 'before');
36852 for(var i =0;i < ti.length;i++) {
36853 // Roo.log(['add child', items[i]]);
36854 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36856 this.toolbar.items = nitems;
36857 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36858 delete config.toolbar;
36862 // xtype created footer. - not sure if will work as we normally have to render first..
36863 if (this.footer && !this.footer.el && this.footer.xtype) {
36864 if (!this.wrapEl) {
36865 this.wrapEl = this.el.wrap();
36868 this.footer.container = this.wrapEl.createChild();
36870 this.footer = Roo.factory(this.footer, Roo);
36875 if(typeof config == "string"){
36876 this.title = config;
36878 Roo.apply(this, config);
36882 this.resizeEl = Roo.get(this.resizeEl, true);
36884 this.resizeEl = this.el;
36886 // handle view.xtype
36894 * Fires when this panel is activated.
36895 * @param {Roo.ContentPanel} this
36899 * @event deactivate
36900 * Fires when this panel is activated.
36901 * @param {Roo.ContentPanel} this
36903 "deactivate" : true,
36907 * Fires when this panel is resized if fitToFrame is true.
36908 * @param {Roo.ContentPanel} this
36909 * @param {Number} width The width after any component adjustments
36910 * @param {Number} height The height after any component adjustments
36916 * Fires when this tab is created
36917 * @param {Roo.ContentPanel} this
36928 if(this.autoScroll){
36929 this.resizeEl.setStyle("overflow", "auto");
36931 // fix randome scrolling
36932 //this.el.on('scroll', function() {
36933 // Roo.log('fix random scolling');
36934 // this.scrollTo('top',0);
36937 content = content || this.content;
36939 this.setContent(content);
36941 if(config && config.url){
36942 this.setUrl(this.url, this.params, this.loadOnce);
36947 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36949 if (this.view && typeof(this.view.xtype) != 'undefined') {
36950 this.view.el = this.el.appendChild(document.createElement("div"));
36951 this.view = Roo.factory(this.view);
36952 this.view.render && this.view.render(false, '');
36956 this.fireEvent('render', this);
36959 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36963 setRegion : function(region){
36964 this.region = region;
36965 this.setActiveClass(region && !this.background);
36969 setActiveClass: function(state)
36972 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36973 this.el.setStyle('position','relative');
36975 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36976 this.el.setStyle('position', 'absolute');
36981 * Returns the toolbar for this Panel if one was configured.
36982 * @return {Roo.Toolbar}
36984 getToolbar : function(){
36985 return this.toolbar;
36988 setActiveState : function(active)
36990 this.active = active;
36991 this.setActiveClass(active);
36993 if(this.fireEvent("deactivate", this) === false){
36998 this.fireEvent("activate", this);
37002 * Updates this panel's element
37003 * @param {String} content The new content
37004 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37006 setContent : function(content, loadScripts){
37007 this.el.update(content, loadScripts);
37010 ignoreResize : function(w, h){
37011 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37014 this.lastSize = {width: w, height: h};
37019 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37020 * @return {Roo.UpdateManager} The UpdateManager
37022 getUpdateManager : function(){
37023 return this.el.getUpdateManager();
37026 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37027 * @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:
37030 url: "your-url.php",
37031 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37032 callback: yourFunction,
37033 scope: yourObject, //(optional scope)
37036 text: "Loading...",
37041 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37042 * 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.
37043 * @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}
37044 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37045 * @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.
37046 * @return {Roo.ContentPanel} this
37049 var um = this.el.getUpdateManager();
37050 um.update.apply(um, arguments);
37056 * 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.
37057 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37058 * @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)
37059 * @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)
37060 * @return {Roo.UpdateManager} The UpdateManager
37062 setUrl : function(url, params, loadOnce){
37063 if(this.refreshDelegate){
37064 this.removeListener("activate", this.refreshDelegate);
37066 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37067 this.on("activate", this.refreshDelegate);
37068 return this.el.getUpdateManager();
37071 _handleRefresh : function(url, params, loadOnce){
37072 if(!loadOnce || !this.loaded){
37073 var updater = this.el.getUpdateManager();
37074 updater.update(url, params, this._setLoaded.createDelegate(this));
37078 _setLoaded : function(){
37079 this.loaded = true;
37083 * Returns this panel's id
37086 getId : function(){
37091 * Returns this panel's element - used by regiosn to add.
37092 * @return {Roo.Element}
37094 getEl : function(){
37095 return this.wrapEl || this.el;
37100 adjustForComponents : function(width, height)
37102 //Roo.log('adjustForComponents ');
37103 if(this.resizeEl != this.el){
37104 width -= this.el.getFrameWidth('lr');
37105 height -= this.el.getFrameWidth('tb');
37108 var te = this.toolbar.getEl();
37109 te.setWidth(width);
37110 height -= te.getHeight();
37113 var te = this.footer.getEl();
37114 te.setWidth(width);
37115 height -= te.getHeight();
37119 if(this.adjustments){
37120 width += this.adjustments[0];
37121 height += this.adjustments[1];
37123 return {"width": width, "height": height};
37126 setSize : function(width, height){
37127 if(this.fitToFrame && !this.ignoreResize(width, height)){
37128 if(this.fitContainer && this.resizeEl != this.el){
37129 this.el.setSize(width, height);
37131 var size = this.adjustForComponents(width, height);
37132 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37133 this.fireEvent('resize', this, size.width, size.height);
37138 * Returns this panel's title
37141 getTitle : function(){
37143 if (typeof(this.title) != 'object') {
37148 for (var k in this.title) {
37149 if (!this.title.hasOwnProperty(k)) {
37153 if (k.indexOf('-') >= 0) {
37154 var s = k.split('-');
37155 for (var i = 0; i<s.length; i++) {
37156 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37159 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37166 * Set this panel's title
37167 * @param {String} title
37169 setTitle : function(title){
37170 this.title = title;
37172 this.region.updatePanelTitle(this, title);
37177 * Returns true is this panel was configured to be closable
37178 * @return {Boolean}
37180 isClosable : function(){
37181 return this.closable;
37184 beforeSlide : function(){
37186 this.resizeEl.clip();
37189 afterSlide : function(){
37191 this.resizeEl.unclip();
37195 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37196 * Will fail silently if the {@link #setUrl} method has not been called.
37197 * This does not activate the panel, just updates its content.
37199 refresh : function(){
37200 if(this.refreshDelegate){
37201 this.loaded = false;
37202 this.refreshDelegate();
37207 * Destroys this panel
37209 destroy : function(){
37210 this.el.removeAllListeners();
37211 var tempEl = document.createElement("span");
37212 tempEl.appendChild(this.el.dom);
37213 tempEl.innerHTML = "";
37219 * form - if the content panel contains a form - this is a reference to it.
37220 * @type {Roo.form.Form}
37224 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37225 * This contains a reference to it.
37231 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37241 * @param {Object} cfg Xtype definition of item to add.
37245 getChildContainer: function () {
37246 return this.getEl();
37251 var ret = new Roo.factory(cfg);
37256 if (cfg.xtype.match(/^Form$/)) {
37259 //if (this.footer) {
37260 // el = this.footer.container.insertSibling(false, 'before');
37262 el = this.el.createChild();
37265 this.form = new Roo.form.Form(cfg);
37268 if ( this.form.allItems.length) {
37269 this.form.render(el.dom);
37273 // should only have one of theses..
37274 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37275 // views.. should not be just added - used named prop 'view''
37277 cfg.el = this.el.appendChild(document.createElement("div"));
37280 var ret = new Roo.factory(cfg);
37282 ret.render && ret.render(false, ''); // render blank..
37292 * @class Roo.bootstrap.panel.Grid
37293 * @extends Roo.bootstrap.panel.Content
37295 * Create a new GridPanel.
37296 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37297 * @param {Object} config A the config object
37303 Roo.bootstrap.panel.Grid = function(config)
37307 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37308 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37310 config.el = this.wrapper;
37311 //this.el = this.wrapper;
37313 if (config.container) {
37314 // ctor'ed from a Border/panel.grid
37317 this.wrapper.setStyle("overflow", "hidden");
37318 this.wrapper.addClass('roo-grid-container');
37323 if(config.toolbar){
37324 var tool_el = this.wrapper.createChild();
37325 this.toolbar = Roo.factory(config.toolbar);
37327 if (config.toolbar.items) {
37328 ti = config.toolbar.items ;
37329 delete config.toolbar.items ;
37333 this.toolbar.render(tool_el);
37334 for(var i =0;i < ti.length;i++) {
37335 // Roo.log(['add child', items[i]]);
37336 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37338 this.toolbar.items = nitems;
37340 delete config.toolbar;
37343 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37344 config.grid.scrollBody = true;;
37345 config.grid.monitorWindowResize = false; // turn off autosizing
37346 config.grid.autoHeight = false;
37347 config.grid.autoWidth = false;
37349 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37351 if (config.background) {
37352 // render grid on panel activation (if panel background)
37353 this.on('activate', function(gp) {
37354 if (!gp.grid.rendered) {
37355 gp.grid.render(this.wrapper);
37356 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37361 this.grid.render(this.wrapper);
37362 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37365 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37366 // ??? needed ??? config.el = this.wrapper;
37371 // xtype created footer. - not sure if will work as we normally have to render first..
37372 if (this.footer && !this.footer.el && this.footer.xtype) {
37374 var ctr = this.grid.getView().getFooterPanel(true);
37375 this.footer.dataSource = this.grid.dataSource;
37376 this.footer = Roo.factory(this.footer, Roo);
37377 this.footer.render(ctr);
37387 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37388 getId : function(){
37389 return this.grid.id;
37393 * Returns the grid for this panel
37394 * @return {Roo.bootstrap.Table}
37396 getGrid : function(){
37400 setSize : function(width, height){
37401 if(!this.ignoreResize(width, height)){
37402 var grid = this.grid;
37403 var size = this.adjustForComponents(width, height);
37404 var gridel = grid.getGridEl();
37405 gridel.setSize(size.width, size.height);
37407 var thd = grid.getGridEl().select('thead',true).first();
37408 var tbd = grid.getGridEl().select('tbody', true).first();
37410 tbd.setSize(width, height - thd.getHeight());
37419 beforeSlide : function(){
37420 this.grid.getView().scroller.clip();
37423 afterSlide : function(){
37424 this.grid.getView().scroller.unclip();
37427 destroy : function(){
37428 this.grid.destroy();
37430 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37435 * @class Roo.bootstrap.panel.Nest
37436 * @extends Roo.bootstrap.panel.Content
37438 * Create a new Panel, that can contain a layout.Border.
37441 * @param {Roo.BorderLayout} layout The layout for this panel
37442 * @param {String/Object} config A string to set only the title or a config object
37444 Roo.bootstrap.panel.Nest = function(config)
37446 // construct with only one argument..
37447 /* FIXME - implement nicer consturctors
37448 if (layout.layout) {
37450 layout = config.layout;
37451 delete config.layout;
37453 if (layout.xtype && !layout.getEl) {
37454 // then layout needs constructing..
37455 layout = Roo.factory(layout, Roo);
37459 config.el = config.layout.getEl();
37461 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37463 config.layout.monitorWindowResize = false; // turn off autosizing
37464 this.layout = config.layout;
37465 this.layout.getEl().addClass("roo-layout-nested-layout");
37472 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37474 setSize : function(width, height){
37475 if(!this.ignoreResize(width, height)){
37476 var size = this.adjustForComponents(width, height);
37477 var el = this.layout.getEl();
37478 if (size.height < 1) {
37479 el.setWidth(size.width);
37481 el.setSize(size.width, size.height);
37483 var touch = el.dom.offsetWidth;
37484 this.layout.layout();
37485 // ie requires a double layout on the first pass
37486 if(Roo.isIE && !this.initialized){
37487 this.initialized = true;
37488 this.layout.layout();
37493 // activate all subpanels if not currently active..
37495 setActiveState : function(active){
37496 this.active = active;
37497 this.setActiveClass(active);
37500 this.fireEvent("deactivate", this);
37504 this.fireEvent("activate", this);
37505 // not sure if this should happen before or after..
37506 if (!this.layout) {
37507 return; // should not happen..
37510 for (var r in this.layout.regions) {
37511 reg = this.layout.getRegion(r);
37512 if (reg.getActivePanel()) {
37513 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37514 reg.setActivePanel(reg.getActivePanel());
37517 if (!reg.panels.length) {
37520 reg.showPanel(reg.getPanel(0));
37529 * Returns the nested BorderLayout for this panel
37530 * @return {Roo.BorderLayout}
37532 getLayout : function(){
37533 return this.layout;
37537 * Adds a xtype elements to the layout of the nested panel
37541 xtype : 'ContentPanel',
37548 xtype : 'NestedLayoutPanel',
37554 items : [ ... list of content panels or nested layout panels.. ]
37558 * @param {Object} cfg Xtype definition of item to add.
37560 addxtype : function(cfg) {
37561 return this.layout.addxtype(cfg);
37566 * Ext JS Library 1.1.1
37567 * Copyright(c) 2006-2007, Ext JS, LLC.
37569 * Originally Released Under LGPL - original licence link has changed is not relivant.
37572 * <script type="text/javascript">
37575 * @class Roo.TabPanel
37576 * @extends Roo.util.Observable
37577 * A lightweight tab container.
37581 // basic tabs 1, built from existing content
37582 var tabs = new Roo.TabPanel("tabs1");
37583 tabs.addTab("script", "View Script");
37584 tabs.addTab("markup", "View Markup");
37585 tabs.activate("script");
37587 // more advanced tabs, built from javascript
37588 var jtabs = new Roo.TabPanel("jtabs");
37589 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37591 // set up the UpdateManager
37592 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37593 var updater = tab2.getUpdateManager();
37594 updater.setDefaultUrl("ajax1.htm");
37595 tab2.on('activate', updater.refresh, updater, true);
37597 // Use setUrl for Ajax loading
37598 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37599 tab3.setUrl("ajax2.htm", null, true);
37602 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37605 jtabs.activate("jtabs-1");
37608 * Create a new TabPanel.
37609 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37610 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37612 Roo.bootstrap.panel.Tabs = function(config){
37614 * The container element for this TabPanel.
37615 * @type Roo.Element
37617 this.el = Roo.get(config.el);
37620 if(typeof config == "boolean"){
37621 this.tabPosition = config ? "bottom" : "top";
37623 Roo.apply(this, config);
37627 if(this.tabPosition == "bottom"){
37628 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37629 this.el.addClass("roo-tabs-bottom");
37631 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37632 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37633 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37635 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37637 if(this.tabPosition != "bottom"){
37638 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37639 * @type Roo.Element
37641 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37642 this.el.addClass("roo-tabs-top");
37646 this.bodyEl.setStyle("position", "relative");
37648 this.active = null;
37649 this.activateDelegate = this.activate.createDelegate(this);
37654 * Fires when the active tab changes
37655 * @param {Roo.TabPanel} this
37656 * @param {Roo.TabPanelItem} activePanel The new active tab
37660 * @event beforetabchange
37661 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37662 * @param {Roo.TabPanel} this
37663 * @param {Object} e Set cancel to true on this object to cancel the tab change
37664 * @param {Roo.TabPanelItem} tab The tab being changed to
37666 "beforetabchange" : true
37669 Roo.EventManager.onWindowResize(this.onResize, this);
37670 this.cpad = this.el.getPadding("lr");
37671 this.hiddenCount = 0;
37674 // toolbar on the tabbar support...
37675 if (this.toolbar) {
37676 alert("no toolbar support yet");
37677 this.toolbar = false;
37679 var tcfg = this.toolbar;
37680 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37681 this.toolbar = new Roo.Toolbar(tcfg);
37682 if (Roo.isSafari) {
37683 var tbl = tcfg.container.child('table', true);
37684 tbl.setAttribute('width', '100%');
37692 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37695 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37697 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37699 tabPosition : "top",
37701 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37703 currentTabWidth : 0,
37705 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37709 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37713 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37715 preferredTabWidth : 175,
37717 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37719 resizeTabs : false,
37721 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37723 monitorResize : true,
37725 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37730 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37731 * @param {String} id The id of the div to use <b>or create</b>
37732 * @param {String} text The text for the tab
37733 * @param {String} content (optional) Content to put in the TabPanelItem body
37734 * @param {Boolean} closable (optional) True to create a close icon on the tab
37735 * @return {Roo.TabPanelItem} The created TabPanelItem
37737 addTab : function(id, text, content, closable, tpl)
37739 var item = new Roo.bootstrap.panel.TabItem({
37743 closable : closable,
37746 this.addTabItem(item);
37748 item.setContent(content);
37754 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37755 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37756 * @return {Roo.TabPanelItem}
37758 getTab : function(id){
37759 return this.items[id];
37763 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37764 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37766 hideTab : function(id){
37767 var t = this.items[id];
37770 this.hiddenCount++;
37771 this.autoSizeTabs();
37776 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37777 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37779 unhideTab : function(id){
37780 var t = this.items[id];
37782 t.setHidden(false);
37783 this.hiddenCount--;
37784 this.autoSizeTabs();
37789 * Adds an existing {@link Roo.TabPanelItem}.
37790 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37792 addTabItem : function(item){
37793 this.items[item.id] = item;
37794 this.items.push(item);
37795 // if(this.resizeTabs){
37796 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37797 // this.autoSizeTabs();
37799 // item.autoSize();
37804 * Removes a {@link Roo.TabPanelItem}.
37805 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37807 removeTab : function(id){
37808 var items = this.items;
37809 var tab = items[id];
37810 if(!tab) { return; }
37811 var index = items.indexOf(tab);
37812 if(this.active == tab && items.length > 1){
37813 var newTab = this.getNextAvailable(index);
37818 this.stripEl.dom.removeChild(tab.pnode.dom);
37819 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37820 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37822 items.splice(index, 1);
37823 delete this.items[tab.id];
37824 tab.fireEvent("close", tab);
37825 tab.purgeListeners();
37826 this.autoSizeTabs();
37829 getNextAvailable : function(start){
37830 var items = this.items;
37832 // look for a next tab that will slide over to
37833 // replace the one being removed
37834 while(index < items.length){
37835 var item = items[++index];
37836 if(item && !item.isHidden()){
37840 // if one isn't found select the previous tab (on the left)
37843 var item = items[--index];
37844 if(item && !item.isHidden()){
37852 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37853 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37855 disableTab : function(id){
37856 var tab = this.items[id];
37857 if(tab && this.active != tab){
37863 * Enables a {@link Roo.TabPanelItem} that is disabled.
37864 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37866 enableTab : function(id){
37867 var tab = this.items[id];
37872 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37873 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37874 * @return {Roo.TabPanelItem} The TabPanelItem.
37876 activate : function(id){
37877 var tab = this.items[id];
37881 if(tab == this.active || tab.disabled){
37885 this.fireEvent("beforetabchange", this, e, tab);
37886 if(e.cancel !== true && !tab.disabled){
37888 this.active.hide();
37890 this.active = this.items[id];
37891 this.active.show();
37892 this.fireEvent("tabchange", this, this.active);
37898 * Gets the active {@link Roo.TabPanelItem}.
37899 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37901 getActiveTab : function(){
37902 return this.active;
37906 * Updates the tab body element to fit the height of the container element
37907 * for overflow scrolling
37908 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37910 syncHeight : function(targetHeight){
37911 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37912 var bm = this.bodyEl.getMargins();
37913 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37914 this.bodyEl.setHeight(newHeight);
37918 onResize : function(){
37919 if(this.monitorResize){
37920 this.autoSizeTabs();
37925 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37927 beginUpdate : function(){
37928 this.updating = true;
37932 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37934 endUpdate : function(){
37935 this.updating = false;
37936 this.autoSizeTabs();
37940 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37942 autoSizeTabs : function(){
37943 var count = this.items.length;
37944 var vcount = count - this.hiddenCount;
37945 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37948 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37949 var availWidth = Math.floor(w / vcount);
37950 var b = this.stripBody;
37951 if(b.getWidth() > w){
37952 var tabs = this.items;
37953 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37954 if(availWidth < this.minTabWidth){
37955 /*if(!this.sleft){ // incomplete scrolling code
37956 this.createScrollButtons();
37959 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37962 if(this.currentTabWidth < this.preferredTabWidth){
37963 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37969 * Returns the number of tabs in this TabPanel.
37972 getCount : function(){
37973 return this.items.length;
37977 * Resizes all the tabs to the passed width
37978 * @param {Number} The new width
37980 setTabWidth : function(width){
37981 this.currentTabWidth = width;
37982 for(var i = 0, len = this.items.length; i < len; i++) {
37983 if(!this.items[i].isHidden()) {
37984 this.items[i].setWidth(width);
37990 * Destroys this TabPanel
37991 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37993 destroy : function(removeEl){
37994 Roo.EventManager.removeResizeListener(this.onResize, this);
37995 for(var i = 0, len = this.items.length; i < len; i++){
37996 this.items[i].purgeListeners();
37998 if(removeEl === true){
37999 this.el.update("");
38004 createStrip : function(container)
38006 var strip = document.createElement("nav");
38007 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38008 container.appendChild(strip);
38012 createStripList : function(strip)
38014 // div wrapper for retard IE
38015 // returns the "tr" element.
38016 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38017 //'<div class="x-tabs-strip-wrap">'+
38018 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38019 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38020 return strip.firstChild; //.firstChild.firstChild.firstChild;
38022 createBody : function(container)
38024 var body = document.createElement("div");
38025 Roo.id(body, "tab-body");
38026 //Roo.fly(body).addClass("x-tabs-body");
38027 Roo.fly(body).addClass("tab-content");
38028 container.appendChild(body);
38031 createItemBody :function(bodyEl, id){
38032 var body = Roo.getDom(id);
38034 body = document.createElement("div");
38037 //Roo.fly(body).addClass("x-tabs-item-body");
38038 Roo.fly(body).addClass("tab-pane");
38039 bodyEl.insertBefore(body, bodyEl.firstChild);
38043 createStripElements : function(stripEl, text, closable, tpl)
38045 var td = document.createElement("li"); // was td..
38048 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38051 stripEl.appendChild(td);
38053 td.className = "x-tabs-closable";
38054 if(!this.closeTpl){
38055 this.closeTpl = new Roo.Template(
38056 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38057 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38058 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38061 var el = this.closeTpl.overwrite(td, {"text": text});
38062 var close = el.getElementsByTagName("div")[0];
38063 var inner = el.getElementsByTagName("em")[0];
38064 return {"el": el, "close": close, "inner": inner};
38067 // not sure what this is..
38068 // if(!this.tabTpl){
38069 //this.tabTpl = new Roo.Template(
38070 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38071 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38073 // this.tabTpl = new Roo.Template(
38074 // '<a href="#">' +
38075 // '<span unselectable="on"' +
38076 // (this.disableTooltips ? '' : ' title="{text}"') +
38077 // ' >{text}</span></a>'
38083 var template = tpl || this.tabTpl || false;
38087 template = new Roo.Template(
38089 '<span unselectable="on"' +
38090 (this.disableTooltips ? '' : ' title="{text}"') +
38091 ' >{text}</span></a>'
38095 switch (typeof(template)) {
38099 template = new Roo.Template(template);
38105 var el = template.overwrite(td, {"text": text});
38107 var inner = el.getElementsByTagName("span")[0];
38109 return {"el": el, "inner": inner};
38117 * @class Roo.TabPanelItem
38118 * @extends Roo.util.Observable
38119 * Represents an individual item (tab plus body) in a TabPanel.
38120 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38121 * @param {String} id The id of this TabPanelItem
38122 * @param {String} text The text for the tab of this TabPanelItem
38123 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38125 Roo.bootstrap.panel.TabItem = function(config){
38127 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38128 * @type Roo.TabPanel
38130 this.tabPanel = config.panel;
38132 * The id for this TabPanelItem
38135 this.id = config.id;
38137 this.disabled = false;
38139 this.text = config.text;
38141 this.loaded = false;
38142 this.closable = config.closable;
38145 * The body element for this TabPanelItem.
38146 * @type Roo.Element
38148 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38149 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38150 this.bodyEl.setStyle("display", "block");
38151 this.bodyEl.setStyle("zoom", "1");
38152 //this.hideAction();
38154 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38156 this.el = Roo.get(els.el);
38157 this.inner = Roo.get(els.inner, true);
38158 this.textEl = Roo.get(this.el.dom.firstChild, true);
38159 this.pnode = Roo.get(els.el.parentNode, true);
38160 // this.el.on("mousedown", this.onTabMouseDown, this);
38161 this.el.on("click", this.onTabClick, this);
38163 if(config.closable){
38164 var c = Roo.get(els.close, true);
38165 c.dom.title = this.closeText;
38166 c.addClassOnOver("close-over");
38167 c.on("click", this.closeClick, this);
38173 * Fires when this tab becomes the active tab.
38174 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38175 * @param {Roo.TabPanelItem} this
38179 * @event beforeclose
38180 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38181 * @param {Roo.TabPanelItem} this
38182 * @param {Object} e Set cancel to true on this object to cancel the close.
38184 "beforeclose": true,
38187 * Fires when this tab is closed.
38188 * @param {Roo.TabPanelItem} this
38192 * @event deactivate
38193 * Fires when this tab is no longer the active tab.
38194 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38195 * @param {Roo.TabPanelItem} this
38197 "deactivate" : true
38199 this.hidden = false;
38201 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38204 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38206 purgeListeners : function(){
38207 Roo.util.Observable.prototype.purgeListeners.call(this);
38208 this.el.removeAllListeners();
38211 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38214 this.pnode.addClass("active");
38217 this.tabPanel.stripWrap.repaint();
38219 this.fireEvent("activate", this.tabPanel, this);
38223 * Returns true if this tab is the active tab.
38224 * @return {Boolean}
38226 isActive : function(){
38227 return this.tabPanel.getActiveTab() == this;
38231 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38234 this.pnode.removeClass("active");
38236 this.fireEvent("deactivate", this.tabPanel, this);
38239 hideAction : function(){
38240 this.bodyEl.hide();
38241 this.bodyEl.setStyle("position", "absolute");
38242 this.bodyEl.setLeft("-20000px");
38243 this.bodyEl.setTop("-20000px");
38246 showAction : function(){
38247 this.bodyEl.setStyle("position", "relative");
38248 this.bodyEl.setTop("");
38249 this.bodyEl.setLeft("");
38250 this.bodyEl.show();
38254 * Set the tooltip for the tab.
38255 * @param {String} tooltip The tab's tooltip
38257 setTooltip : function(text){
38258 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38259 this.textEl.dom.qtip = text;
38260 this.textEl.dom.removeAttribute('title');
38262 this.textEl.dom.title = text;
38266 onTabClick : function(e){
38267 e.preventDefault();
38268 this.tabPanel.activate(this.id);
38271 onTabMouseDown : function(e){
38272 e.preventDefault();
38273 this.tabPanel.activate(this.id);
38276 getWidth : function(){
38277 return this.inner.getWidth();
38280 setWidth : function(width){
38281 var iwidth = width - this.pnode.getPadding("lr");
38282 this.inner.setWidth(iwidth);
38283 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38284 this.pnode.setWidth(width);
38288 * Show or hide the tab
38289 * @param {Boolean} hidden True to hide or false to show.
38291 setHidden : function(hidden){
38292 this.hidden = hidden;
38293 this.pnode.setStyle("display", hidden ? "none" : "");
38297 * Returns true if this tab is "hidden"
38298 * @return {Boolean}
38300 isHidden : function(){
38301 return this.hidden;
38305 * Returns the text for this tab
38308 getText : function(){
38312 autoSize : function(){
38313 //this.el.beginMeasure();
38314 this.textEl.setWidth(1);
38316 * #2804 [new] Tabs in Roojs
38317 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38319 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38320 //this.el.endMeasure();
38324 * Sets the text for the tab (Note: this also sets the tooltip text)
38325 * @param {String} text The tab's text and tooltip
38327 setText : function(text){
38329 this.textEl.update(text);
38330 this.setTooltip(text);
38331 //if(!this.tabPanel.resizeTabs){
38332 // this.autoSize();
38336 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38338 activate : function(){
38339 this.tabPanel.activate(this.id);
38343 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38345 disable : function(){
38346 if(this.tabPanel.active != this){
38347 this.disabled = true;
38348 this.pnode.addClass("disabled");
38353 * Enables this TabPanelItem if it was previously disabled.
38355 enable : function(){
38356 this.disabled = false;
38357 this.pnode.removeClass("disabled");
38361 * Sets the content for this TabPanelItem.
38362 * @param {String} content The content
38363 * @param {Boolean} loadScripts true to look for and load scripts
38365 setContent : function(content, loadScripts){
38366 this.bodyEl.update(content, loadScripts);
38370 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38371 * @return {Roo.UpdateManager} The UpdateManager
38373 getUpdateManager : function(){
38374 return this.bodyEl.getUpdateManager();
38378 * Set a URL to be used to load the content for this TabPanelItem.
38379 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38380 * @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)
38381 * @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)
38382 * @return {Roo.UpdateManager} The UpdateManager
38384 setUrl : function(url, params, loadOnce){
38385 if(this.refreshDelegate){
38386 this.un('activate', this.refreshDelegate);
38388 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38389 this.on("activate", this.refreshDelegate);
38390 return this.bodyEl.getUpdateManager();
38394 _handleRefresh : function(url, params, loadOnce){
38395 if(!loadOnce || !this.loaded){
38396 var updater = this.bodyEl.getUpdateManager();
38397 updater.update(url, params, this._setLoaded.createDelegate(this));
38402 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38403 * Will fail silently if the setUrl method has not been called.
38404 * This does not activate the panel, just updates its content.
38406 refresh : function(){
38407 if(this.refreshDelegate){
38408 this.loaded = false;
38409 this.refreshDelegate();
38414 _setLoaded : function(){
38415 this.loaded = true;
38419 closeClick : function(e){
38422 this.fireEvent("beforeclose", this, o);
38423 if(o.cancel !== true){
38424 this.tabPanel.removeTab(this.id);
38428 * The text displayed in the tooltip for the close icon.
38431 closeText : "Close this tab"
38434 * This script refer to:
38435 * Title: International Telephone Input
38436 * Author: Jack O'Connor
38437 * Code version: v12.1.12
38438 * Availability: https://github.com/jackocnr/intl-tel-input.git
38441 Roo.bootstrap.PhoneInputData = function() {
38444 "Afghanistan (افغانستان)",
38449 "Albania (Shqipëri)",
38454 "Algeria (الجزائر)",
38479 "Antigua and Barbuda",
38489 "Armenia (Հայաստան)",
38505 "Austria (Österreich)",
38510 "Azerbaijan (Azərbaycan)",
38520 "Bahrain (البحرين)",
38525 "Bangladesh (বাংলাদেশ)",
38535 "Belarus (Беларусь)",
38540 "Belgium (België)",
38570 "Bosnia and Herzegovina (Босна и Херцеговина)",
38585 "British Indian Ocean Territory",
38590 "British Virgin Islands",
38600 "Bulgaria (България)",
38610 "Burundi (Uburundi)",
38615 "Cambodia (កម្ពុជា)",
38620 "Cameroon (Cameroun)",
38629 ["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"]
38632 "Cape Verde (Kabu Verdi)",
38637 "Caribbean Netherlands",
38648 "Central African Republic (République centrafricaine)",
38668 "Christmas Island",
38674 "Cocos (Keeling) Islands",
38685 "Comoros (جزر القمر)",
38690 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38695 "Congo (Republic) (Congo-Brazzaville)",
38715 "Croatia (Hrvatska)",
38736 "Czech Republic (Česká republika)",
38741 "Denmark (Danmark)",
38756 "Dominican Republic (República Dominicana)",
38760 ["809", "829", "849"]
38778 "Equatorial Guinea (Guinea Ecuatorial)",
38798 "Falkland Islands (Islas Malvinas)",
38803 "Faroe Islands (Føroyar)",
38824 "French Guiana (Guyane française)",
38829 "French Polynesia (Polynésie française)",
38844 "Georgia (საქართველო)",
38849 "Germany (Deutschland)",
38869 "Greenland (Kalaallit Nunaat)",
38906 "Guinea-Bissau (Guiné Bissau)",
38931 "Hungary (Magyarország)",
38936 "Iceland (Ísland)",
38956 "Iraq (العراق)",
38972 "Israel (ישראל)",
38999 "Jordan (الأردن)",
39004 "Kazakhstan (Казахстан)",
39025 "Kuwait (الكويت)",
39030 "Kyrgyzstan (Кыргызстан)",
39040 "Latvia (Latvija)",
39045 "Lebanon (لبنان)",
39060 "Libya (ليبيا)",
39070 "Lithuania (Lietuva)",
39085 "Macedonia (FYROM) (Македонија)",
39090 "Madagascar (Madagasikara)",
39120 "Marshall Islands",
39130 "Mauritania (موريتانيا)",
39135 "Mauritius (Moris)",
39156 "Moldova (Republica Moldova)",
39166 "Mongolia (Монгол)",
39171 "Montenegro (Crna Gora)",
39181 "Morocco (المغرب)",
39187 "Mozambique (Moçambique)",
39192 "Myanmar (Burma) (မြန်မာ)",
39197 "Namibia (Namibië)",
39212 "Netherlands (Nederland)",
39217 "New Caledonia (Nouvelle-Calédonie)",
39252 "North Korea (조선 민주주의 인민 공화국)",
39257 "Northern Mariana Islands",
39273 "Pakistan (پاکستان)",
39283 "Palestine (فلسطين)",
39293 "Papua New Guinea",
39335 "Réunion (La Réunion)",
39341 "Romania (România)",
39357 "Saint Barthélemy",
39368 "Saint Kitts and Nevis",
39378 "Saint Martin (Saint-Martin (partie française))",
39384 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39389 "Saint Vincent and the Grenadines",
39404 "São Tomé and Príncipe (São Tomé e Príncipe)",
39409 "Saudi Arabia (المملكة العربية السعودية)",
39414 "Senegal (Sénégal)",
39444 "Slovakia (Slovensko)",
39449 "Slovenia (Slovenija)",
39459 "Somalia (Soomaaliya)",
39469 "South Korea (대한민국)",
39474 "South Sudan (جنوب السودان)",
39484 "Sri Lanka (ශ්රී ලංකාව)",
39489 "Sudan (السودان)",
39499 "Svalbard and Jan Mayen",
39510 "Sweden (Sverige)",
39515 "Switzerland (Schweiz)",
39520 "Syria (سوريا)",
39565 "Trinidad and Tobago",
39570 "Tunisia (تونس)",
39575 "Turkey (Türkiye)",
39585 "Turks and Caicos Islands",
39595 "U.S. Virgin Islands",
39605 "Ukraine (Україна)",
39610 "United Arab Emirates (الإمارات العربية المتحدة)",
39632 "Uzbekistan (Oʻzbekiston)",
39642 "Vatican City (Città del Vaticano)",
39653 "Vietnam (Việt Nam)",
39658 "Wallis and Futuna (Wallis-et-Futuna)",
39663 "Western Sahara (الصحراء الغربية)",
39669 "Yemen (اليمن)",
39693 * This script refer to:
39694 * Title: International Telephone Input
39695 * Author: Jack O'Connor
39696 * Code version: v12.1.12
39697 * Availability: https://github.com/jackocnr/intl-tel-input.git
39701 * @class Roo.bootstrap.PhoneInput
39702 * @extends Roo.bootstrap.TriggerField
39703 * An input with International dial-code selection
39705 * @cfg {String} defaultDialCode default '+852'
39706 * @cfg {Array} preferedCountries default []
39709 * Create a new PhoneInput.
39710 * @param {Object} config Configuration options
39713 Roo.bootstrap.PhoneInput = function(config) {
39714 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39717 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39719 listWidth: undefined,
39721 selectedClass: 'active',
39723 invalidClass : "has-warning",
39725 validClass: 'has-success',
39727 allowed: '0123456789',
39730 * @cfg {String} defaultDialCode The default dial code when initializing the input
39732 defaultDialCode: '+852',
39735 * @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
39737 preferedCountries: false,
39739 getAutoCreate : function()
39741 var data = Roo.bootstrap.PhoneInputData();
39742 var align = this.labelAlign || this.parentLabelAlign();
39745 this.allCountries = [];
39746 this.dialCodeMapping = [];
39748 for (var i = 0; i < data.length; i++) {
39750 this.allCountries[i] = {
39754 priority: c[3] || 0,
39755 areaCodes: c[4] || null
39757 this.dialCodeMapping[c[2]] = {
39760 priority: c[3] || 0,
39761 areaCodes: c[4] || null
39773 cls : 'form-control tel-input',
39774 autocomplete: 'new-password'
39777 var hiddenInput = {
39780 cls: 'hidden-tel-input'
39784 hiddenInput.name = this.name;
39787 if (this.disabled) {
39788 input.disabled = true;
39791 var flag_container = {
39808 cls: this.hasFeedback ? 'has-feedback' : '',
39814 cls: 'dial-code-holder',
39821 cls: 'roo-select2-container input-group',
39828 if (this.fieldLabel.length) {
39831 tooltip: 'This field is required'
39837 cls: 'control-label',
39843 html: this.fieldLabel
39846 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39852 if(this.indicatorpos == 'right') {
39853 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39860 if(align == 'left') {
39868 if(this.labelWidth > 12){
39869 label.style = "width: " + this.labelWidth + 'px';
39871 if(this.labelWidth < 13 && this.labelmd == 0){
39872 this.labelmd = this.labelWidth;
39874 if(this.labellg > 0){
39875 label.cls += ' col-lg-' + this.labellg;
39876 input.cls += ' col-lg-' + (12 - this.labellg);
39878 if(this.labelmd > 0){
39879 label.cls += ' col-md-' + this.labelmd;
39880 container.cls += ' col-md-' + (12 - this.labelmd);
39882 if(this.labelsm > 0){
39883 label.cls += ' col-sm-' + this.labelsm;
39884 container.cls += ' col-sm-' + (12 - this.labelsm);
39886 if(this.labelxs > 0){
39887 label.cls += ' col-xs-' + this.labelxs;
39888 container.cls += ' col-xs-' + (12 - this.labelxs);
39898 var settings = this;
39900 ['xs','sm','md','lg'].map(function(size){
39901 if (settings[size]) {
39902 cfg.cls += ' col-' + size + '-' + settings[size];
39906 this.store = new Roo.data.Store({
39907 proxy : new Roo.data.MemoryProxy({}),
39908 reader : new Roo.data.JsonReader({
39919 'name' : 'dialCode',
39923 'name' : 'priority',
39927 'name' : 'areaCodes',
39934 if(!this.preferedCountries) {
39935 this.preferedCountries = [
39942 var p = this.preferedCountries.reverse();
39945 for (var i = 0; i < p.length; i++) {
39946 for (var j = 0; j < this.allCountries.length; j++) {
39947 if(this.allCountries[j].iso2 == p[i]) {
39948 var t = this.allCountries[j];
39949 this.allCountries.splice(j,1);
39950 this.allCountries.unshift(t);
39956 this.store.proxy.data = {
39958 data: this.allCountries
39964 initEvents : function()
39967 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39969 this.indicator = this.indicatorEl();
39970 this.flag = this.flagEl();
39971 this.dialCodeHolder = this.dialCodeHolderEl();
39973 this.trigger = this.el.select('div.flag-box',true).first();
39974 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39979 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39980 _this.list.setWidth(lw);
39983 this.list.on('mouseover', this.onViewOver, this);
39984 this.list.on('mousemove', this.onViewMove, this);
39985 this.inputEl().on("keyup", this.onKeyUp, this);
39987 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39989 this.view = new Roo.View(this.list, this.tpl, {
39990 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39993 this.view.on('click', this.onViewClick, this);
39994 this.setValue(this.defaultDialCode);
39997 onTriggerClick : function(e)
39999 Roo.log('trigger click');
40004 if(this.isExpanded()){
40006 this.hasFocus = false;
40008 this.store.load({});
40009 this.hasFocus = true;
40014 isExpanded : function()
40016 return this.list.isVisible();
40019 collapse : function()
40021 if(!this.isExpanded()){
40025 Roo.get(document).un('mousedown', this.collapseIf, this);
40026 Roo.get(document).un('mousewheel', this.collapseIf, this);
40027 this.fireEvent('collapse', this);
40031 expand : function()
40035 if(this.isExpanded() || !this.hasFocus){
40039 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40040 this.list.setWidth(lw);
40043 this.restrictHeight();
40045 Roo.get(document).on('mousedown', this.collapseIf, this);
40046 Roo.get(document).on('mousewheel', this.collapseIf, this);
40048 this.fireEvent('expand', this);
40051 restrictHeight : function()
40053 this.list.alignTo(this.inputEl(), this.listAlign);
40054 this.list.alignTo(this.inputEl(), this.listAlign);
40057 onViewOver : function(e, t)
40059 if(this.inKeyMode){
40062 var item = this.view.findItemFromChild(t);
40065 var index = this.view.indexOf(item);
40066 this.select(index, false);
40071 onViewClick : function(view, doFocus, el, e)
40073 var index = this.view.getSelectedIndexes()[0];
40075 var r = this.store.getAt(index);
40078 this.onSelect(r, index);
40080 if(doFocus !== false && !this.blockFocus){
40081 this.inputEl().focus();
40085 onViewMove : function(e, t)
40087 this.inKeyMode = false;
40090 select : function(index, scrollIntoView)
40092 this.selectedIndex = index;
40093 this.view.select(index);
40094 if(scrollIntoView !== false){
40095 var el = this.view.getNode(index);
40097 this.list.scrollChildIntoView(el, false);
40102 createList : function()
40104 this.list = Roo.get(document.body).createChild({
40106 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40107 style: 'display:none'
40110 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40113 collapseIf : function(e)
40115 var in_combo = e.within(this.el);
40116 var in_list = e.within(this.list);
40117 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40119 if (in_combo || in_list || is_list) {
40125 onSelect : function(record, index)
40127 if(this.fireEvent('beforeselect', this, record, index) !== false){
40129 this.setFlagClass(record.data.iso2);
40130 this.setDialCode(record.data.dialCode);
40131 this.hasFocus = false;
40133 this.fireEvent('select', this, record, index);
40137 flagEl : function()
40139 var flag = this.el.select('div.flag',true).first();
40146 dialCodeHolderEl : function()
40148 var d = this.el.select('input.dial-code-holder',true).first();
40155 setDialCode : function(v)
40157 this.dialCodeHolder.dom.value = '+'+v;
40160 setFlagClass : function(n)
40162 this.flag.dom.className = 'flag '+n;
40165 getValue : function()
40167 var v = this.inputEl().getValue();
40168 if(this.dialCodeHolder) {
40169 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40174 setValue : function(v)
40176 var d = this.getDialCode(v);
40178 //invalid dial code
40179 if(v.length == 0 || !d || d.length == 0) {
40181 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40182 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40188 this.setFlagClass(this.dialCodeMapping[d].iso2);
40189 this.setDialCode(d);
40190 this.inputEl().dom.value = v.replace('+'+d,'');
40191 this.hiddenEl().dom.value = this.getValue();
40196 getDialCode : function(v)
40200 if (v.length == 0) {
40201 return this.dialCodeHolder.dom.value;
40205 if (v.charAt(0) != "+") {
40208 var numericChars = "";
40209 for (var i = 1; i < v.length; i++) {
40210 var c = v.charAt(i);
40213 if (this.dialCodeMapping[numericChars]) {
40214 dialCode = v.substr(1, i);
40216 if (numericChars.length == 4) {
40226 this.setValue(this.defaultDialCode);
40230 hiddenEl : function()
40232 return this.el.select('input.hidden-tel-input',true).first();
40235 onKeyUp : function(e){
40237 var k = e.getKey();
40238 var c = e.getCharCode();
40241 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40242 this.allowed.indexOf(String.fromCharCode(c)) === -1
40247 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40250 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40254 this.setValue(this.getValue());
40259 * @class Roo.bootstrap.MoneyField
40260 * @extends Roo.bootstrap.ComboBox
40261 * Bootstrap MoneyField class
40264 * Create a new MoneyField.
40265 * @param {Object} config Configuration options
40268 Roo.bootstrap.MoneyField = function(config) {
40270 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40274 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40277 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40279 allowDecimals : true,
40281 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40283 decimalSeparator : ".",
40285 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40287 decimalPrecision : 0,
40289 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40291 allowNegative : true,
40293 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40297 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40299 minValue : Number.NEGATIVE_INFINITY,
40301 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40303 maxValue : Number.MAX_VALUE,
40305 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40307 minText : "The minimum value for this field is {0}",
40309 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40311 maxText : "The maximum value for this field is {0}",
40313 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40314 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40316 nanText : "{0} is not a valid number",
40318 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40322 * @cfg {String} defaults currency of the MoneyField
40323 * value should be in lkey
40325 defaultCurrency : false,
40327 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40329 thousandsDelimiter : false,
40339 getAutoCreate : function()
40341 var align = this.labelAlign || this.parentLabelAlign();
40353 cls : 'form-control roo-money-amount-input',
40354 autocomplete: 'new-password'
40357 var hiddenInput = {
40361 cls: 'hidden-number-input'
40365 hiddenInput.name = this.name;
40368 if (this.disabled) {
40369 input.disabled = true;
40372 var clg = 12 - this.inputlg;
40373 var cmd = 12 - this.inputmd;
40374 var csm = 12 - this.inputsm;
40375 var cxs = 12 - this.inputxs;
40379 cls : 'row roo-money-field',
40383 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40387 cls: 'roo-select2-container input-group',
40391 cls : 'form-control roo-money-currency-input',
40392 autocomplete: 'new-password',
40394 name : this.currencyName
40398 cls : 'input-group-addon',
40412 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40416 cls: this.hasFeedback ? 'has-feedback' : '',
40427 if (this.fieldLabel.length) {
40430 tooltip: 'This field is required'
40436 cls: 'control-label',
40442 html: this.fieldLabel
40445 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40451 if(this.indicatorpos == 'right') {
40452 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40459 if(align == 'left') {
40467 if(this.labelWidth > 12){
40468 label.style = "width: " + this.labelWidth + 'px';
40470 if(this.labelWidth < 13 && this.labelmd == 0){
40471 this.labelmd = this.labelWidth;
40473 if(this.labellg > 0){
40474 label.cls += ' col-lg-' + this.labellg;
40475 input.cls += ' col-lg-' + (12 - this.labellg);
40477 if(this.labelmd > 0){
40478 label.cls += ' col-md-' + this.labelmd;
40479 container.cls += ' col-md-' + (12 - this.labelmd);
40481 if(this.labelsm > 0){
40482 label.cls += ' col-sm-' + this.labelsm;
40483 container.cls += ' col-sm-' + (12 - this.labelsm);
40485 if(this.labelxs > 0){
40486 label.cls += ' col-xs-' + this.labelxs;
40487 container.cls += ' col-xs-' + (12 - this.labelxs);
40498 var settings = this;
40500 ['xs','sm','md','lg'].map(function(size){
40501 if (settings[size]) {
40502 cfg.cls += ' col-' + size + '-' + settings[size];
40509 initEvents : function()
40511 this.indicator = this.indicatorEl();
40513 this.initCurrencyEvent();
40515 this.initNumberEvent();
40518 initCurrencyEvent : function()
40521 throw "can not find store for combo";
40524 this.store = Roo.factory(this.store, Roo.data);
40525 this.store.parent = this;
40529 this.triggerEl = this.el.select('.input-group-addon', true).first();
40531 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40536 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40537 _this.list.setWidth(lw);
40540 this.list.on('mouseover', this.onViewOver, this);
40541 this.list.on('mousemove', this.onViewMove, this);
40542 this.list.on('scroll', this.onViewScroll, this);
40545 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40548 this.view = new Roo.View(this.list, this.tpl, {
40549 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40552 this.view.on('click', this.onViewClick, this);
40554 this.store.on('beforeload', this.onBeforeLoad, this);
40555 this.store.on('load', this.onLoad, this);
40556 this.store.on('loadexception', this.onLoadException, this);
40558 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40559 "up" : function(e){
40560 this.inKeyMode = true;
40564 "down" : function(e){
40565 if(!this.isExpanded()){
40566 this.onTriggerClick();
40568 this.inKeyMode = true;
40573 "enter" : function(e){
40576 if(this.fireEvent("specialkey", this, e)){
40577 this.onViewClick(false);
40583 "esc" : function(e){
40587 "tab" : function(e){
40590 if(this.fireEvent("specialkey", this, e)){
40591 this.onViewClick(false);
40599 doRelay : function(foo, bar, hname){
40600 if(hname == 'down' || this.scope.isExpanded()){
40601 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40609 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40613 initNumberEvent : function(e)
40615 this.inputEl().on("keydown" , this.fireKey, this);
40616 this.inputEl().on("focus", this.onFocus, this);
40617 this.inputEl().on("blur", this.onBlur, this);
40619 this.inputEl().relayEvent('keyup', this);
40621 if(this.indicator){
40622 this.indicator.addClass('invisible');
40625 this.originalValue = this.getValue();
40627 if(this.validationEvent == 'keyup'){
40628 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40629 this.inputEl().on('keyup', this.filterValidation, this);
40631 else if(this.validationEvent !== false){
40632 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40635 if(this.selectOnFocus){
40636 this.on("focus", this.preFocus, this);
40639 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40640 this.inputEl().on("keypress", this.filterKeys, this);
40642 this.inputEl().relayEvent('keypress', this);
40645 var allowed = "0123456789";
40647 if(this.allowDecimals){
40648 allowed += this.decimalSeparator;
40651 if(this.allowNegative){
40655 if(this.thousandsDelimiter) {
40659 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40661 var keyPress = function(e){
40663 var k = e.getKey();
40665 var c = e.getCharCode();
40668 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40669 allowed.indexOf(String.fromCharCode(c)) === -1
40675 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40679 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40684 this.inputEl().on("keypress", keyPress, this);
40688 onTriggerClick : function(e)
40695 this.loadNext = false;
40697 if(this.isExpanded()){
40702 this.hasFocus = true;
40704 if(this.triggerAction == 'all') {
40705 this.doQuery(this.allQuery, true);
40709 this.doQuery(this.getRawValue());
40712 getCurrency : function()
40714 var v = this.currencyEl().getValue();
40719 restrictHeight : function()
40721 this.list.alignTo(this.currencyEl(), this.listAlign);
40722 this.list.alignTo(this.currencyEl(), this.listAlign);
40725 onViewClick : function(view, doFocus, el, e)
40727 var index = this.view.getSelectedIndexes()[0];
40729 var r = this.store.getAt(index);
40732 this.onSelect(r, index);
40736 onSelect : function(record, index){
40738 if(this.fireEvent('beforeselect', this, record, index) !== false){
40740 this.setFromCurrencyData(index > -1 ? record.data : false);
40744 this.fireEvent('select', this, record, index);
40748 setFromCurrencyData : function(o)
40752 this.lastCurrency = o;
40754 if (this.currencyField) {
40755 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40757 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40760 this.lastSelectionText = currency;
40762 //setting default currency
40763 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40764 this.setCurrency(this.defaultCurrency);
40768 this.setCurrency(currency);
40771 setFromData : function(o)
40775 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40777 this.setFromCurrencyData(c);
40782 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40784 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40787 this.setValue(value);
40791 setCurrency : function(v)
40793 this.currencyValue = v;
40796 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40801 setValue : function(v)
40803 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40809 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40811 this.inputEl().dom.value = (v == '') ? '' :
40812 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40814 if(!this.allowZero && v === '0') {
40815 this.hiddenEl().dom.value = '';
40816 this.inputEl().dom.value = '';
40823 getRawValue : function()
40825 var v = this.inputEl().getValue();
40830 getValue : function()
40832 return this.fixPrecision(this.parseValue(this.getRawValue()));
40835 parseValue : function(value)
40837 if(this.thousandsDelimiter) {
40839 r = new RegExp(",", "g");
40840 value = value.replace(r, "");
40843 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40844 return isNaN(value) ? '' : value;
40848 fixPrecision : function(value)
40850 if(this.thousandsDelimiter) {
40852 r = new RegExp(",", "g");
40853 value = value.replace(r, "");
40856 var nan = isNaN(value);
40858 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40859 return nan ? '' : value;
40861 return parseFloat(value).toFixed(this.decimalPrecision);
40864 decimalPrecisionFcn : function(v)
40866 return Math.floor(v);
40869 validateValue : function(value)
40871 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40875 var num = this.parseValue(value);
40878 this.markInvalid(String.format(this.nanText, value));
40882 if(num < this.minValue){
40883 this.markInvalid(String.format(this.minText, this.minValue));
40887 if(num > this.maxValue){
40888 this.markInvalid(String.format(this.maxText, this.maxValue));
40895 validate : function()
40897 if(this.disabled || this.allowBlank){
40902 var currency = this.getCurrency();
40904 if(this.validateValue(this.getRawValue()) && currency.length){
40909 this.markInvalid();
40913 getName: function()
40918 beforeBlur : function()
40924 var v = this.parseValue(this.getRawValue());
40931 onBlur : function()
40935 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40936 //this.el.removeClass(this.focusClass);
40939 this.hasFocus = false;
40941 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40945 var v = this.getValue();
40947 if(String(v) !== String(this.startValue)){
40948 this.fireEvent('change', this, v, this.startValue);
40951 this.fireEvent("blur", this);
40954 inputEl : function()
40956 return this.el.select('.roo-money-amount-input', true).first();
40959 currencyEl : function()
40961 return this.el.select('.roo-money-currency-input', true).first();
40964 hiddenEl : function()
40966 return this.el.select('input.hidden-number-input',true).first();