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',
2651 onRender : function(ct, position)
2653 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2656 var cfg = Roo.apply({}, this.getAutoCreate());
2659 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2661 //if (!cfg.name.length) {
2665 cfg.cls += ' ' + this.cls;
2668 cfg.style = this.style;
2670 this.el = Roo.get(document.body).createChild(cfg, position);
2672 //var type = this.el.dom.type;
2675 if(this.tabIndex !== undefined){
2676 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2679 this.dialogEl = this.el.select('.modal-dialog',true).first();
2680 this.bodyEl = this.el.select('.modal-body',true).first();
2681 this.closeEl = this.el.select('.modal-header .close', true).first();
2682 this.headerEl = this.el.select('.modal-header',true).first();
2683 this.titleEl = this.el.select('.modal-title',true).first();
2684 this.footerEl = this.el.select('.modal-footer',true).first();
2686 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2688 //this.el.addClass("x-dlg-modal");
2690 if (this.buttons.length) {
2691 Roo.each(this.buttons, function(bb) {
2692 var b = Roo.apply({}, bb);
2693 b.xns = b.xns || Roo.bootstrap;
2694 b.xtype = b.xtype || 'Button';
2695 if (typeof(b.listeners) == 'undefined') {
2696 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2699 var btn = Roo.factory(b);
2701 btn.render(this.el.select('.modal-footer div').first());
2705 // render the children.
2708 if(typeof(this.items) != 'undefined'){
2709 var items = this.items;
2712 for(var i =0;i < items.length;i++) {
2713 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2717 this.items = nitems;
2719 // where are these used - they used to be body/close/footer
2723 //this.el.addClass([this.fieldClass, this.cls]);
2727 getAutoCreate : function()
2731 html : this.html || ''
2736 cls : 'modal-title',
2740 if(this.specificTitle){
2746 if (this.allow_close) {
2758 if(this.size.length){
2759 size = 'modal-' + this.size;
2766 cls: "modal-dialog " + size,
2769 cls : "modal-content",
2772 cls : 'modal-header',
2777 cls : 'modal-footer',
2781 cls: 'btn-' + this.buttonPosition
2798 modal.cls += ' fade';
2804 getChildContainer : function() {
2809 getButtonContainer : function() {
2810 return this.el.select('.modal-footer div',true).first();
2813 initEvents : function()
2815 if (this.allow_close) {
2816 this.closeEl.on('click', this.hide, this);
2818 Roo.EventManager.onWindowResize(this.resize, this, true);
2825 this.maskEl.setSize(
2826 Roo.lib.Dom.getViewWidth(true),
2827 Roo.lib.Dom.getViewHeight(true)
2830 if (this.fitwindow) {
2832 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2833 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2838 if(this.max_width !== 0) {
2840 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2844 this.height <= Roo.lib.Dom.getViewportHeight(true) - 60
2846 this.setSize(w, this.height);
2852 this.height > Roo.lib.Dom.getViewportHeight(true) - 60
2854 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2858 if(!this.fit_content) {
2859 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2863 var body_childs = this.bodyEl.dom.childNodes;
2864 // does not seem to give enough space...
2865 var full_height = 60 + this.headerEl.getHeight() + this.footerEl.getHeight();
2866 for(var i = 0; i < body_childs.length; i++) {
2867 full_height += body_childs[i].offsetHeight;
2870 this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2875 setSize : function(w,h)
2885 if (!this.rendered) {
2889 //this.el.setStyle('display', 'block');
2890 this.el.removeClass('hideing');
2891 this.el.addClass('show');
2893 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2896 this.el.addClass('in');
2899 this.el.addClass('in');
2902 // not sure how we can show data in here..
2904 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2907 Roo.get(document.body).addClass("x-body-masked");
2909 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2910 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2911 this.maskEl.addClass('show');
2915 this.fireEvent('show', this);
2917 // set zindex here - otherwise it appears to be ignored...
2918 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2921 this.items.forEach( function(e) {
2922 e.layout ? e.layout() : false;
2930 if(this.fireEvent("beforehide", this) !== false){
2931 this.maskEl.removeClass('show');
2932 Roo.get(document.body).removeClass("x-body-masked");
2933 this.el.removeClass('in');
2934 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2936 if(this.animate){ // why
2937 this.el.addClass('hideing');
2939 if (!this.el.hasClass('hideing')) {
2940 return; // it's been shown again...
2942 this.el.removeClass('show');
2943 this.el.removeClass('hideing');
2947 this.el.removeClass('show');
2949 this.fireEvent('hide', this);
2952 isVisible : function()
2955 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2959 addButton : function(str, cb)
2963 var b = Roo.apply({}, { html : str } );
2964 b.xns = b.xns || Roo.bootstrap;
2965 b.xtype = b.xtype || 'Button';
2966 if (typeof(b.listeners) == 'undefined') {
2967 b.listeners = { click : cb.createDelegate(this) };
2970 var btn = Roo.factory(b);
2972 btn.render(this.el.select('.modal-footer div').first());
2978 setDefaultButton : function(btn)
2980 //this.el.select('.modal-footer').()
2984 resizeTo: function(w,h)
2988 this.dialogEl.setWidth(w);
2989 if (this.diff === false) {
2990 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2993 this.bodyEl.setHeight(h-this.diff);
2995 this.fireEvent('resize', this);
2998 setContentSize : function(w, h)
3002 onButtonClick: function(btn,e)
3005 this.fireEvent('btnclick', btn.name, e);
3008 * Set the title of the Dialog
3009 * @param {String} str new Title
3011 setTitle: function(str) {
3012 this.titleEl.dom.innerHTML = str;
3015 * Set the body of the Dialog
3016 * @param {String} str new Title
3018 setBody: function(str) {
3019 this.bodyEl.dom.innerHTML = str;
3022 * Set the body of the Dialog using the template
3023 * @param {Obj} data - apply this data to the template and replace the body contents.
3025 applyBody: function(obj)
3028 Roo.log("Error - using apply Body without a template");
3031 this.tmpl.overwrite(this.bodyEl, obj);
3037 Roo.apply(Roo.bootstrap.Modal, {
3039 * Button config that displays a single OK button
3048 * Button config that displays Yes and No buttons
3064 * Button config that displays OK and Cancel buttons
3079 * Button config that displays Yes, No and Cancel buttons
3103 * messagebox - can be used as a replace
3107 * @class Roo.MessageBox
3108 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3112 Roo.Msg.alert('Status', 'Changes saved successfully.');
3114 // Prompt for user data:
3115 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3117 // process text value...
3121 // Show a dialog using config options:
3123 title:'Save Changes?',
3124 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3125 buttons: Roo.Msg.YESNOCANCEL,
3132 Roo.bootstrap.MessageBox = function(){
3133 var dlg, opt, mask, waitTimer;
3134 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3135 var buttons, activeTextEl, bwidth;
3139 var handleButton = function(button){
3141 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3145 var handleHide = function(){
3147 dlg.el.removeClass(opt.cls);
3150 // Roo.TaskMgr.stop(waitTimer);
3151 // waitTimer = null;
3156 var updateButtons = function(b){
3159 buttons["ok"].hide();
3160 buttons["cancel"].hide();
3161 buttons["yes"].hide();
3162 buttons["no"].hide();
3163 //dlg.footer.dom.style.display = 'none';
3166 dlg.footerEl.dom.style.display = '';
3167 for(var k in buttons){
3168 if(typeof buttons[k] != "function"){
3171 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3172 width += buttons[k].el.getWidth()+15;
3182 var handleEsc = function(d, k, e){
3183 if(opt && opt.closable !== false){
3193 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3194 * @return {Roo.BasicDialog} The BasicDialog element
3196 getDialog : function(){
3198 dlg = new Roo.bootstrap.Modal( {
3201 //constraintoviewport:false,
3203 //collapsible : false,
3208 //buttonAlign:"center",
3209 closeClick : function(){
3210 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3213 handleButton("cancel");
3218 dlg.on("hide", handleHide);
3220 //dlg.addKeyListener(27, handleEsc);
3222 this.buttons = buttons;
3223 var bt = this.buttonText;
3224 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3225 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3226 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3227 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3229 bodyEl = dlg.bodyEl.createChild({
3231 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3232 '<textarea class="roo-mb-textarea"></textarea>' +
3233 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3235 msgEl = bodyEl.dom.firstChild;
3236 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3237 textboxEl.enableDisplayMode();
3238 textboxEl.addKeyListener([10,13], function(){
3239 if(dlg.isVisible() && opt && opt.buttons){
3242 }else if(opt.buttons.yes){
3243 handleButton("yes");
3247 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3248 textareaEl.enableDisplayMode();
3249 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3250 progressEl.enableDisplayMode();
3252 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3253 var pf = progressEl.dom.firstChild;
3255 pp = Roo.get(pf.firstChild);
3256 pp.setHeight(pf.offsetHeight);
3264 * Updates the message box body text
3265 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3266 * the XHTML-compliant non-breaking space character '&#160;')
3267 * @return {Roo.MessageBox} This message box
3269 updateText : function(text)
3271 if(!dlg.isVisible() && !opt.width){
3272 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3273 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3275 msgEl.innerHTML = text || ' ';
3277 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3278 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3280 Math.min(opt.width || cw , this.maxWidth),
3281 Math.max(opt.minWidth || this.minWidth, bwidth)
3284 activeTextEl.setWidth(w);
3286 if(dlg.isVisible()){
3287 dlg.fixedcenter = false;
3289 // to big, make it scroll. = But as usual stupid IE does not support
3292 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3293 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3294 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3296 bodyEl.dom.style.height = '';
3297 bodyEl.dom.style.overflowY = '';
3300 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3302 bodyEl.dom.style.overflowX = '';
3305 dlg.setContentSize(w, bodyEl.getHeight());
3306 if(dlg.isVisible()){
3307 dlg.fixedcenter = true;
3313 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3314 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3315 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3316 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3317 * @return {Roo.MessageBox} This message box
3319 updateProgress : function(value, text){
3321 this.updateText(text);
3324 if (pp) { // weird bug on my firefox - for some reason this is not defined
3325 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3326 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3332 * Returns true if the message box is currently displayed
3333 * @return {Boolean} True if the message box is visible, else false
3335 isVisible : function(){
3336 return dlg && dlg.isVisible();
3340 * Hides the message box if it is displayed
3343 if(this.isVisible()){
3349 * Displays a new message box, or reinitializes an existing message box, based on the config options
3350 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3351 * The following config object properties are supported:
3353 Property Type Description
3354 ---------- --------------- ------------------------------------------------------------------------------------
3355 animEl String/Element An id or Element from which the message box should animate as it opens and
3356 closes (defaults to undefined)
3357 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3358 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3359 closable Boolean False to hide the top-right close button (defaults to true). Note that
3360 progress and wait dialogs will ignore this property and always hide the
3361 close button as they can only be closed programmatically.
3362 cls String A custom CSS class to apply to the message box element
3363 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3364 displayed (defaults to 75)
3365 fn Function A callback function to execute after closing the dialog. The arguments to the
3366 function will be btn (the name of the button that was clicked, if applicable,
3367 e.g. "ok"), and text (the value of the active text field, if applicable).
3368 Progress and wait dialogs will ignore this option since they do not respond to
3369 user actions and can only be closed programmatically, so any required function
3370 should be called by the same code after it closes the dialog.
3371 icon String A CSS class that provides a background image to be used as an icon for
3372 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3373 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3374 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3375 modal Boolean False to allow user interaction with the page while the message box is
3376 displayed (defaults to true)
3377 msg String A string that will replace the existing message box body text (defaults
3378 to the XHTML-compliant non-breaking space character ' ')
3379 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3380 progress Boolean True to display a progress bar (defaults to false)
3381 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3382 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3383 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3384 title String The title text
3385 value String The string value to set into the active textbox element if displayed
3386 wait Boolean True to display a progress bar (defaults to false)
3387 width Number The width of the dialog in pixels
3394 msg: 'Please enter your address:',
3396 buttons: Roo.MessageBox.OKCANCEL,
3399 animEl: 'addAddressBtn'
3402 * @param {Object} config Configuration options
3403 * @return {Roo.MessageBox} This message box
3405 show : function(options)
3408 // this causes nightmares if you show one dialog after another
3409 // especially on callbacks..
3411 if(this.isVisible()){
3414 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3415 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3416 Roo.log("New Dialog Message:" + options.msg )
3417 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3418 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3421 var d = this.getDialog();
3423 d.setTitle(opt.title || " ");
3424 d.closeEl.setDisplayed(opt.closable !== false);
3425 activeTextEl = textboxEl;
3426 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3431 textareaEl.setHeight(typeof opt.multiline == "number" ?
3432 opt.multiline : this.defaultTextHeight);
3433 activeTextEl = textareaEl;
3442 progressEl.setDisplayed(opt.progress === true);
3443 this.updateProgress(0);
3444 activeTextEl.dom.value = opt.value || "";
3446 dlg.setDefaultButton(activeTextEl);
3448 var bs = opt.buttons;
3452 }else if(bs && bs.yes){
3453 db = buttons["yes"];
3455 dlg.setDefaultButton(db);
3457 bwidth = updateButtons(opt.buttons);
3458 this.updateText(opt.msg);
3460 d.el.addClass(opt.cls);
3462 d.proxyDrag = opt.proxyDrag === true;
3463 d.modal = opt.modal !== false;
3464 d.mask = opt.modal !== false ? mask : false;
3466 // force it to the end of the z-index stack so it gets a cursor in FF
3467 document.body.appendChild(dlg.el.dom);
3468 d.animateTarget = null;
3469 d.show(options.animEl);
3475 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3476 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3477 * and closing the message box when the process is complete.
3478 * @param {String} title The title bar text
3479 * @param {String} msg The message box body text
3480 * @return {Roo.MessageBox} This message box
3482 progress : function(title, msg){
3489 minWidth: this.minProgressWidth,
3496 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3497 * If a callback function is passed it will be called after the user clicks the button, and the
3498 * id of the button that was clicked will be passed as the only parameter to the callback
3499 * (could also be the top-right close button).
3500 * @param {String} title The title bar text
3501 * @param {String} msg The message box body text
3502 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3503 * @param {Object} scope (optional) The scope of the callback function
3504 * @return {Roo.MessageBox} This message box
3506 alert : function(title, msg, fn, scope)
3521 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3522 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3523 * You are responsible for closing the message box when the process is complete.
3524 * @param {String} msg The message box body text
3525 * @param {String} title (optional) The title bar text
3526 * @return {Roo.MessageBox} This message box
3528 wait : function(msg, title){
3539 waitTimer = Roo.TaskMgr.start({
3541 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3549 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3550 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3551 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3552 * @param {String} title The title bar text
3553 * @param {String} msg The message box body text
3554 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555 * @param {Object} scope (optional) The scope of the callback function
3556 * @return {Roo.MessageBox} This message box
3558 confirm : function(title, msg, fn, scope){
3562 buttons: this.YESNO,
3571 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3572 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3573 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3574 * (could also be the top-right close button) and the text that was entered will be passed as the two
3575 * parameters to the callback.
3576 * @param {String} title The title bar text
3577 * @param {String} msg The message box body text
3578 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3579 * @param {Object} scope (optional) The scope of the callback function
3580 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3581 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3582 * @return {Roo.MessageBox} This message box
3584 prompt : function(title, msg, fn, scope, multiline){
3588 buttons: this.OKCANCEL,
3593 multiline: multiline,
3600 * Button config that displays a single OK button
3605 * Button config that displays Yes and No buttons
3608 YESNO : {yes:true, no:true},
3610 * Button config that displays OK and Cancel buttons
3613 OKCANCEL : {ok:true, cancel:true},
3615 * Button config that displays Yes, No and Cancel buttons
3618 YESNOCANCEL : {yes:true, no:true, cancel:true},
3621 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3624 defaultTextHeight : 75,
3626 * The maximum width in pixels of the message box (defaults to 600)
3631 * The minimum width in pixels of the message box (defaults to 100)
3636 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3637 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3640 minProgressWidth : 250,
3642 * An object containing the default button text strings that can be overriden for localized language support.
3643 * Supported properties are: ok, cancel, yes and no.
3644 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3657 * Shorthand for {@link Roo.MessageBox}
3659 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3660 Roo.Msg = Roo.Msg || Roo.MessageBox;
3669 * @class Roo.bootstrap.Navbar
3670 * @extends Roo.bootstrap.Component
3671 * Bootstrap Navbar class
3674 * Create a new Navbar
3675 * @param {Object} config The config object
3679 Roo.bootstrap.Navbar = function(config){
3680 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3684 * @event beforetoggle
3685 * Fire before toggle the menu
3686 * @param {Roo.EventObject} e
3688 "beforetoggle" : true
3692 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3701 getAutoCreate : function(){
3704 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3708 initEvents :function ()
3710 //Roo.log(this.el.select('.navbar-toggle',true));
3711 this.el.select('.navbar-toggle',true).on('click', function() {
3712 if(this.fireEvent('beforetoggle', this) !== false){
3713 this.el.select('.navbar-collapse',true).toggleClass('in');
3723 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3725 var size = this.el.getSize();
3726 this.maskEl.setSize(size.width, size.height);
3727 this.maskEl.enableDisplayMode("block");
3736 getChildContainer : function()
3738 if (this.el.select('.collapse').getCount()) {
3739 return this.el.select('.collapse',true).first();
3772 * @class Roo.bootstrap.NavSimplebar
3773 * @extends Roo.bootstrap.Navbar
3774 * Bootstrap Sidebar class
3776 * @cfg {Boolean} inverse is inverted color
3778 * @cfg {String} type (nav | pills | tabs)
3779 * @cfg {Boolean} arrangement stacked | justified
3780 * @cfg {String} align (left | right) alignment
3782 * @cfg {Boolean} main (true|false) main nav bar? default false
3783 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3785 * @cfg {String} tag (header|footer|nav|div) default is nav
3791 * Create a new Sidebar
3792 * @param {Object} config The config object
3796 Roo.bootstrap.NavSimplebar = function(config){
3797 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3800 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3816 getAutoCreate : function(){
3820 tag : this.tag || 'div',
3833 this.type = this.type || 'nav';
3834 if (['tabs','pills'].indexOf(this.type)!==-1) {
3835 cfg.cn[0].cls += ' nav-' + this.type
3839 if (this.type!=='nav') {
3840 Roo.log('nav type must be nav/tabs/pills')
3842 cfg.cn[0].cls += ' navbar-nav'
3848 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3849 cfg.cn[0].cls += ' nav-' + this.arrangement;
3853 if (this.align === 'right') {
3854 cfg.cn[0].cls += ' navbar-right';
3858 cfg.cls += ' navbar-inverse';
3885 * @class Roo.bootstrap.NavHeaderbar
3886 * @extends Roo.bootstrap.NavSimplebar
3887 * Bootstrap Sidebar class
3889 * @cfg {String} brand what is brand
3890 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3891 * @cfg {String} brand_href href of the brand
3892 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3893 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3894 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3895 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3898 * Create a new Sidebar
3899 * @param {Object} config The config object
3903 Roo.bootstrap.NavHeaderbar = function(config){
3904 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3908 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3915 desktopCenter : false,
3918 getAutoCreate : function(){
3921 tag: this.nav || 'nav',
3928 if (this.desktopCenter) {
3929 cn.push({cls : 'container', cn : []});
3936 cls: 'navbar-header',
3941 cls: 'navbar-toggle',
3942 'data-toggle': 'collapse',
3947 html: 'Toggle navigation'
3969 cls: 'collapse navbar-collapse',
3973 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3975 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3976 cfg.cls += ' navbar-' + this.position;
3978 // tag can override this..
3980 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3983 if (this.brand !== '') {
3986 href: this.brand_href ? this.brand_href : '#',
3987 cls: 'navbar-brand',
3995 cfg.cls += ' main-nav';
4003 getHeaderChildContainer : function()
4005 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4006 return this.el.select('.navbar-header',true).first();
4009 return this.getChildContainer();
4013 initEvents : function()
4015 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4017 if (this.autohide) {
4022 Roo.get(document).on('scroll',function(e) {
4023 var ns = Roo.get(document).getScroll().top;
4024 var os = prevScroll;
4028 ft.removeClass('slideDown');
4029 ft.addClass('slideUp');
4032 ft.removeClass('slideUp');
4033 ft.addClass('slideDown');
4054 * @class Roo.bootstrap.NavSidebar
4055 * @extends Roo.bootstrap.Navbar
4056 * Bootstrap Sidebar class
4059 * Create a new Sidebar
4060 * @param {Object} config The config object
4064 Roo.bootstrap.NavSidebar = function(config){
4065 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4068 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4070 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4072 getAutoCreate : function(){
4077 cls: 'sidebar sidebar-nav'
4099 * @class Roo.bootstrap.NavGroup
4100 * @extends Roo.bootstrap.Component
4101 * Bootstrap NavGroup class
4102 * @cfg {String} align (left|right)
4103 * @cfg {Boolean} inverse
4104 * @cfg {String} type (nav|pills|tab) default nav
4105 * @cfg {String} navId - reference Id for navbar.
4109 * Create a new nav group
4110 * @param {Object} config The config object
4113 Roo.bootstrap.NavGroup = function(config){
4114 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4117 Roo.bootstrap.NavGroup.register(this);
4121 * Fires when the active item changes
4122 * @param {Roo.bootstrap.NavGroup} this
4123 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4124 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4131 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4142 getAutoCreate : function()
4144 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4151 if (['tabs','pills'].indexOf(this.type)!==-1) {
4152 cfg.cls += ' nav-' + this.type
4154 if (this.type!=='nav') {
4155 Roo.log('nav type must be nav/tabs/pills')
4157 cfg.cls += ' navbar-nav'
4160 if (this.parent() && this.parent().sidebar) {
4163 cls: 'dashboard-menu sidebar-menu'
4169 if (this.form === true) {
4175 if (this.align === 'right') {
4176 cfg.cls += ' navbar-right';
4178 cfg.cls += ' navbar-left';
4182 if (this.align === 'right') {
4183 cfg.cls += ' navbar-right';
4187 cfg.cls += ' navbar-inverse';
4195 * sets the active Navigation item
4196 * @param {Roo.bootstrap.NavItem} the new current navitem
4198 setActiveItem : function(item)
4201 Roo.each(this.navItems, function(v){
4206 v.setActive(false, true);
4213 item.setActive(true, true);
4214 this.fireEvent('changed', this, item, prev);
4219 * gets the active Navigation item
4220 * @return {Roo.bootstrap.NavItem} the current navitem
4222 getActive : function()
4226 Roo.each(this.navItems, function(v){
4237 indexOfNav : function()
4241 Roo.each(this.navItems, function(v,i){
4252 * adds a Navigation item
4253 * @param {Roo.bootstrap.NavItem} the navitem to add
4255 addItem : function(cfg)
4257 var cn = new Roo.bootstrap.NavItem(cfg);
4259 cn.parentId = this.id;
4260 cn.onRender(this.el, null);
4264 * register a Navigation item
4265 * @param {Roo.bootstrap.NavItem} the navitem to add
4267 register : function(item)
4269 this.navItems.push( item);
4270 item.navId = this.navId;
4275 * clear all the Navigation item
4278 clearAll : function()
4281 this.el.dom.innerHTML = '';
4284 getNavItem: function(tabId)
4287 Roo.each(this.navItems, function(e) {
4288 if (e.tabId == tabId) {
4298 setActiveNext : function()
4300 var i = this.indexOfNav(this.getActive());
4301 if (i > this.navItems.length) {
4304 this.setActiveItem(this.navItems[i+1]);
4306 setActivePrev : function()
4308 var i = this.indexOfNav(this.getActive());
4312 this.setActiveItem(this.navItems[i-1]);
4314 clearWasActive : function(except) {
4315 Roo.each(this.navItems, function(e) {
4316 if (e.tabId != except.tabId && e.was_active) {
4317 e.was_active = false;
4324 getWasActive : function ()
4327 Roo.each(this.navItems, function(e) {
4342 Roo.apply(Roo.bootstrap.NavGroup, {
4346 * register a Navigation Group
4347 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4349 register : function(navgrp)
4351 this.groups[navgrp.navId] = navgrp;
4355 * fetch a Navigation Group based on the navigation ID
4356 * @param {string} the navgroup to add
4357 * @returns {Roo.bootstrap.NavGroup} the navgroup
4359 get: function(navId) {
4360 if (typeof(this.groups[navId]) == 'undefined') {
4362 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4364 return this.groups[navId] ;
4379 * @class Roo.bootstrap.NavItem
4380 * @extends Roo.bootstrap.Component
4381 * Bootstrap Navbar.NavItem class
4382 * @cfg {String} href link to
4383 * @cfg {String} html content of button
4384 * @cfg {String} badge text inside badge
4385 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4386 * @cfg {String} glyphicon name of glyphicon
4387 * @cfg {String} icon name of font awesome icon
4388 * @cfg {Boolean} active Is item active
4389 * @cfg {Boolean} disabled Is item disabled
4391 * @cfg {Boolean} preventDefault (true | false) default false
4392 * @cfg {String} tabId the tab that this item activates.
4393 * @cfg {String} tagtype (a|span) render as a href or span?
4394 * @cfg {Boolean} animateRef (true|false) link to element default false
4397 * Create a new Navbar Item
4398 * @param {Object} config The config object
4400 Roo.bootstrap.NavItem = function(config){
4401 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4406 * The raw click event for the entire grid.
4407 * @param {Roo.EventObject} e
4412 * Fires when the active item active state changes
4413 * @param {Roo.bootstrap.NavItem} this
4414 * @param {boolean} state the new state
4420 * Fires when scroll to element
4421 * @param {Roo.bootstrap.NavItem} this
4422 * @param {Object} options
4423 * @param {Roo.EventObject} e
4431 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4439 preventDefault : false,
4446 getAutoCreate : function(){
4455 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4457 if (this.disabled) {
4458 cfg.cls += ' disabled';
4461 if (this.href || this.html || this.glyphicon || this.icon) {
4465 href : this.href || "#",
4466 html: this.html || ''
4471 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4474 if(this.glyphicon) {
4475 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4480 cfg.cn[0].html += " <span class='caret'></span>";
4484 if (this.badge !== '') {
4486 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4494 initEvents: function()
4496 if (typeof (this.menu) != 'undefined') {
4497 this.menu.parentType = this.xtype;
4498 this.menu.triggerEl = this.el;
4499 this.menu = this.addxtype(Roo.apply({}, this.menu));
4502 this.el.select('a',true).on('click', this.onClick, this);
4504 if(this.tagtype == 'span'){
4505 this.el.select('span',true).on('click', this.onClick, this);
4508 // at this point parent should be available..
4509 this.parent().register(this);
4512 onClick : function(e)
4514 if (e.getTarget('.dropdown-menu-item')) {
4515 // did you click on a menu itemm.... - then don't trigger onclick..
4520 this.preventDefault ||
4523 Roo.log("NavItem - prevent Default?");
4527 if (this.disabled) {
4531 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4532 if (tg && tg.transition) {
4533 Roo.log("waiting for the transitionend");
4539 //Roo.log("fire event clicked");
4540 if(this.fireEvent('click', this, e) === false){
4544 if(this.tagtype == 'span'){
4548 //Roo.log(this.href);
4549 var ael = this.el.select('a',true).first();
4552 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4553 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4554 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4555 return; // ignore... - it's a 'hash' to another page.
4557 Roo.log("NavItem - prevent Default?");
4559 this.scrollToElement(e);
4563 var p = this.parent();
4565 if (['tabs','pills'].indexOf(p.type)!==-1) {
4566 if (typeof(p.setActiveItem) !== 'undefined') {
4567 p.setActiveItem(this);
4571 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4572 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4573 // remove the collapsed menu expand...
4574 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4578 isActive: function () {
4581 setActive : function(state, fire, is_was_active)
4583 if (this.active && !state && this.navId) {
4584 this.was_active = true;
4585 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4587 nv.clearWasActive(this);
4591 this.active = state;
4594 this.el.removeClass('active');
4595 } else if (!this.el.hasClass('active')) {
4596 this.el.addClass('active');
4599 this.fireEvent('changed', this, state);
4602 // show a panel if it's registered and related..
4604 if (!this.navId || !this.tabId || !state || is_was_active) {
4608 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4612 var pan = tg.getPanelByName(this.tabId);
4616 // if we can not flip to new panel - go back to old nav highlight..
4617 if (false == tg.showPanel(pan)) {
4618 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4620 var onav = nv.getWasActive();
4622 onav.setActive(true, false, true);
4631 // this should not be here...
4632 setDisabled : function(state)
4634 this.disabled = state;
4636 this.el.removeClass('disabled');
4637 } else if (!this.el.hasClass('disabled')) {
4638 this.el.addClass('disabled');
4644 * Fetch the element to display the tooltip on.
4645 * @return {Roo.Element} defaults to this.el
4647 tooltipEl : function()
4649 return this.el.select('' + this.tagtype + '', true).first();
4652 scrollToElement : function(e)
4654 var c = document.body;
4657 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4659 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4660 c = document.documentElement;
4663 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4669 var o = target.calcOffsetsTo(c);
4676 this.fireEvent('scrollto', this, options, e);
4678 Roo.get(c).scrollTo('top', options.value, true);
4691 * <span> icon </span>
4692 * <span> text </span>
4693 * <span>badge </span>
4697 * @class Roo.bootstrap.NavSidebarItem
4698 * @extends Roo.bootstrap.NavItem
4699 * Bootstrap Navbar.NavSidebarItem class
4700 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4701 * {Boolean} open is the menu open
4702 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4703 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4704 * {String} buttonSize (sm|md|lg)the extra classes for the button
4705 * {Boolean} showArrow show arrow next to the text (default true)
4707 * Create a new Navbar Button
4708 * @param {Object} config The config object
4710 Roo.bootstrap.NavSidebarItem = function(config){
4711 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4716 * The raw click event for the entire grid.
4717 * @param {Roo.EventObject} e
4722 * Fires when the active item active state changes
4723 * @param {Roo.bootstrap.NavSidebarItem} this
4724 * @param {boolean} state the new state
4732 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4734 badgeWeight : 'default',
4740 buttonWeight : 'default',
4746 getAutoCreate : function(){
4751 href : this.href || '#',
4757 if(this.buttonView){
4760 href : this.href || '#',
4761 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4774 cfg.cls += ' active';
4777 if (this.disabled) {
4778 cfg.cls += ' disabled';
4781 cfg.cls += ' open x-open';
4784 if (this.glyphicon || this.icon) {
4785 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4786 a.cn.push({ tag : 'i', cls : c }) ;
4789 if(!this.buttonView){
4792 html : this.html || ''
4799 if (this.badge !== '') {
4800 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4806 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4809 a.cls += ' dropdown-toggle treeview' ;
4815 initEvents : function()
4817 if (typeof (this.menu) != 'undefined') {
4818 this.menu.parentType = this.xtype;
4819 this.menu.triggerEl = this.el;
4820 this.menu = this.addxtype(Roo.apply({}, this.menu));
4823 this.el.on('click', this.onClick, this);
4825 if(this.badge !== ''){
4826 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4831 onClick : function(e)
4838 if(this.preventDefault){
4842 this.fireEvent('click', this);
4845 disable : function()
4847 this.setDisabled(true);
4852 this.setDisabled(false);
4855 setDisabled : function(state)
4857 if(this.disabled == state){
4861 this.disabled = state;
4864 this.el.addClass('disabled');
4868 this.el.removeClass('disabled');
4873 setActive : function(state)
4875 if(this.active == state){
4879 this.active = state;
4882 this.el.addClass('active');
4886 this.el.removeClass('active');
4891 isActive: function ()
4896 setBadge : function(str)
4902 this.badgeEl.dom.innerHTML = str;
4919 * @class Roo.bootstrap.Row
4920 * @extends Roo.bootstrap.Component
4921 * Bootstrap Row class (contains columns...)
4925 * @param {Object} config The config object
4928 Roo.bootstrap.Row = function(config){
4929 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4932 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4934 getAutoCreate : function(){
4953 * @class Roo.bootstrap.Element
4954 * @extends Roo.bootstrap.Component
4955 * Bootstrap Element class
4956 * @cfg {String} html contents of the element
4957 * @cfg {String} tag tag of the element
4958 * @cfg {String} cls class of the element
4959 * @cfg {Boolean} preventDefault (true|false) default false
4960 * @cfg {Boolean} clickable (true|false) default false
4963 * Create a new Element
4964 * @param {Object} config The config object
4967 Roo.bootstrap.Element = function(config){
4968 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4974 * When a element is chick
4975 * @param {Roo.bootstrap.Element} this
4976 * @param {Roo.EventObject} e
4982 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4987 preventDefault: false,
4990 getAutoCreate : function(){
4994 // cls: this.cls, double assign in parent class Component.js :: onRender
5001 initEvents: function()
5003 Roo.bootstrap.Element.superclass.initEvents.call(this);
5006 this.el.on('click', this.onClick, this);
5011 onClick : function(e)
5013 if(this.preventDefault){
5017 this.fireEvent('click', this, e);
5020 getValue : function()
5022 return this.el.dom.innerHTML;
5025 setValue : function(value)
5027 this.el.dom.innerHTML = value;
5042 * @class Roo.bootstrap.Pagination
5043 * @extends Roo.bootstrap.Component
5044 * Bootstrap Pagination class
5045 * @cfg {String} size xs | sm | md | lg
5046 * @cfg {Boolean} inverse false | true
5049 * Create a new Pagination
5050 * @param {Object} config The config object
5053 Roo.bootstrap.Pagination = function(config){
5054 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5057 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5063 getAutoCreate : function(){
5069 cfg.cls += ' inverse';
5075 cfg.cls += " " + this.cls;
5093 * @class Roo.bootstrap.PaginationItem
5094 * @extends Roo.bootstrap.Component
5095 * Bootstrap PaginationItem class
5096 * @cfg {String} html text
5097 * @cfg {String} href the link
5098 * @cfg {Boolean} preventDefault (true | false) default true
5099 * @cfg {Boolean} active (true | false) default false
5100 * @cfg {Boolean} disabled default false
5104 * Create a new PaginationItem
5105 * @param {Object} config The config object
5109 Roo.bootstrap.PaginationItem = function(config){
5110 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5115 * The raw click event for the entire grid.
5116 * @param {Roo.EventObject} e
5122 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5126 preventDefault: true,
5131 getAutoCreate : function(){
5137 href : this.href ? this.href : '#',
5138 html : this.html ? this.html : ''
5148 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5152 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5158 initEvents: function() {
5160 this.el.on('click', this.onClick, this);
5163 onClick : function(e)
5165 Roo.log('PaginationItem on click ');
5166 if(this.preventDefault){
5174 this.fireEvent('click', this, e);
5190 * @class Roo.bootstrap.Slider
5191 * @extends Roo.bootstrap.Component
5192 * Bootstrap Slider class
5195 * Create a new Slider
5196 * @param {Object} config The config object
5199 Roo.bootstrap.Slider = function(config){
5200 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5203 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5205 getAutoCreate : function(){
5209 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5213 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5225 * Ext JS Library 1.1.1
5226 * Copyright(c) 2006-2007, Ext JS, LLC.
5228 * Originally Released Under LGPL - original licence link has changed is not relivant.
5231 * <script type="text/javascript">
5236 * @class Roo.grid.ColumnModel
5237 * @extends Roo.util.Observable
5238 * This is the default implementation of a ColumnModel used by the Grid. It defines
5239 * the columns in the grid.
5242 var colModel = new Roo.grid.ColumnModel([
5243 {header: "Ticker", width: 60, sortable: true, locked: true},
5244 {header: "Company Name", width: 150, sortable: true},
5245 {header: "Market Cap.", width: 100, sortable: true},
5246 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5247 {header: "Employees", width: 100, sortable: true, resizable: false}
5252 * The config options listed for this class are options which may appear in each
5253 * individual column definition.
5254 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5256 * @param {Object} config An Array of column config objects. See this class's
5257 * config objects for details.
5259 Roo.grid.ColumnModel = function(config){
5261 * The config passed into the constructor
5263 this.config = config;
5266 // if no id, create one
5267 // if the column does not have a dataIndex mapping,
5268 // map it to the order it is in the config
5269 for(var i = 0, len = config.length; i < len; i++){
5271 if(typeof c.dataIndex == "undefined"){
5274 if(typeof c.renderer == "string"){
5275 c.renderer = Roo.util.Format[c.renderer];
5277 if(typeof c.id == "undefined"){
5280 if(c.editor && c.editor.xtype){
5281 c.editor = Roo.factory(c.editor, Roo.grid);
5283 if(c.editor && c.editor.isFormField){
5284 c.editor = new Roo.grid.GridEditor(c.editor);
5286 this.lookup[c.id] = c;
5290 * The width of columns which have no width specified (defaults to 100)
5293 this.defaultWidth = 100;
5296 * Default sortable of columns which have no sortable specified (defaults to false)
5299 this.defaultSortable = false;
5303 * @event widthchange
5304 * Fires when the width of a column changes.
5305 * @param {ColumnModel} this
5306 * @param {Number} columnIndex The column index
5307 * @param {Number} newWidth The new width
5309 "widthchange": true,
5311 * @event headerchange
5312 * Fires when the text of a header changes.
5313 * @param {ColumnModel} this
5314 * @param {Number} columnIndex The column index
5315 * @param {Number} newText The new header text
5317 "headerchange": true,
5319 * @event hiddenchange
5320 * Fires when a column is hidden or "unhidden".
5321 * @param {ColumnModel} this
5322 * @param {Number} columnIndex The column index
5323 * @param {Boolean} hidden true if hidden, false otherwise
5325 "hiddenchange": true,
5327 * @event columnmoved
5328 * Fires when a column is moved.
5329 * @param {ColumnModel} this
5330 * @param {Number} oldIndex
5331 * @param {Number} newIndex
5333 "columnmoved" : true,
5335 * @event columlockchange
5336 * Fires when a column's locked state is changed
5337 * @param {ColumnModel} this
5338 * @param {Number} colIndex
5339 * @param {Boolean} locked true if locked
5341 "columnlockchange" : true
5343 Roo.grid.ColumnModel.superclass.constructor.call(this);
5345 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5347 * @cfg {String} header The header text to display in the Grid view.
5350 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5351 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5352 * specified, the column's index is used as an index into the Record's data Array.
5355 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5356 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5359 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5360 * Defaults to the value of the {@link #defaultSortable} property.
5361 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5364 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5367 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5370 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5373 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5376 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5377 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5378 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5379 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5382 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5385 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5388 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5391 * @cfg {String} cursor (Optional)
5394 * @cfg {String} tooltip (Optional)
5397 * @cfg {Number} xs (Optional)
5400 * @cfg {Number} sm (Optional)
5403 * @cfg {Number} md (Optional)
5406 * @cfg {Number} lg (Optional)
5409 * Returns the id of the column at the specified index.
5410 * @param {Number} index The column index
5411 * @return {String} the id
5413 getColumnId : function(index){
5414 return this.config[index].id;
5418 * Returns the column for a specified id.
5419 * @param {String} id The column id
5420 * @return {Object} the column
5422 getColumnById : function(id){
5423 return this.lookup[id];
5428 * Returns the column for a specified dataIndex.
5429 * @param {String} dataIndex The column dataIndex
5430 * @return {Object|Boolean} the column or false if not found
5432 getColumnByDataIndex: function(dataIndex){
5433 var index = this.findColumnIndex(dataIndex);
5434 return index > -1 ? this.config[index] : false;
5438 * Returns the index for a specified column id.
5439 * @param {String} id The column id
5440 * @return {Number} the index, or -1 if not found
5442 getIndexById : function(id){
5443 for(var i = 0, len = this.config.length; i < len; i++){
5444 if(this.config[i].id == id){
5452 * Returns the index for a specified column dataIndex.
5453 * @param {String} dataIndex The column dataIndex
5454 * @return {Number} the index, or -1 if not found
5457 findColumnIndex : function(dataIndex){
5458 for(var i = 0, len = this.config.length; i < len; i++){
5459 if(this.config[i].dataIndex == dataIndex){
5467 moveColumn : function(oldIndex, newIndex){
5468 var c = this.config[oldIndex];
5469 this.config.splice(oldIndex, 1);
5470 this.config.splice(newIndex, 0, c);
5471 this.dataMap = null;
5472 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5475 isLocked : function(colIndex){
5476 return this.config[colIndex].locked === true;
5479 setLocked : function(colIndex, value, suppressEvent){
5480 if(this.isLocked(colIndex) == value){
5483 this.config[colIndex].locked = value;
5485 this.fireEvent("columnlockchange", this, colIndex, value);
5489 getTotalLockedWidth : function(){
5491 for(var i = 0; i < this.config.length; i++){
5492 if(this.isLocked(i) && !this.isHidden(i)){
5493 this.totalWidth += this.getColumnWidth(i);
5499 getLockedCount : function(){
5500 for(var i = 0, len = this.config.length; i < len; i++){
5501 if(!this.isLocked(i)){
5506 return this.config.length;
5510 * Returns the number of columns.
5513 getColumnCount : function(visibleOnly){
5514 if(visibleOnly === true){
5516 for(var i = 0, len = this.config.length; i < len; i++){
5517 if(!this.isHidden(i)){
5523 return this.config.length;
5527 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5528 * @param {Function} fn
5529 * @param {Object} scope (optional)
5530 * @return {Array} result
5532 getColumnsBy : function(fn, scope){
5534 for(var i = 0, len = this.config.length; i < len; i++){
5535 var c = this.config[i];
5536 if(fn.call(scope||this, c, i) === true){
5544 * Returns true if the specified column is sortable.
5545 * @param {Number} col The column index
5548 isSortable : function(col){
5549 if(typeof this.config[col].sortable == "undefined"){
5550 return this.defaultSortable;
5552 return this.config[col].sortable;
5556 * Returns the rendering (formatting) function defined for the column.
5557 * @param {Number} col The column index.
5558 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5560 getRenderer : function(col){
5561 if(!this.config[col].renderer){
5562 return Roo.grid.ColumnModel.defaultRenderer;
5564 return this.config[col].renderer;
5568 * Sets the rendering (formatting) function for a column.
5569 * @param {Number} col The column index
5570 * @param {Function} fn The function to use to process the cell's raw data
5571 * to return HTML markup for the grid view. The render function is called with
5572 * the following parameters:<ul>
5573 * <li>Data value.</li>
5574 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5575 * <li>css A CSS style string to apply to the table cell.</li>
5576 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5577 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5578 * <li>Row index</li>
5579 * <li>Column index</li>
5580 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5582 setRenderer : function(col, fn){
5583 this.config[col].renderer = fn;
5587 * Returns the width for the specified column.
5588 * @param {Number} col The column index
5591 getColumnWidth : function(col){
5592 return this.config[col].width * 1 || this.defaultWidth;
5596 * Sets the width for a column.
5597 * @param {Number} col The column index
5598 * @param {Number} width The new width
5600 setColumnWidth : function(col, width, suppressEvent){
5601 this.config[col].width = width;
5602 this.totalWidth = null;
5604 this.fireEvent("widthchange", this, col, width);
5609 * Returns the total width of all columns.
5610 * @param {Boolean} includeHidden True to include hidden column widths
5613 getTotalWidth : function(includeHidden){
5614 if(!this.totalWidth){
5615 this.totalWidth = 0;
5616 for(var i = 0, len = this.config.length; i < len; i++){
5617 if(includeHidden || !this.isHidden(i)){
5618 this.totalWidth += this.getColumnWidth(i);
5622 return this.totalWidth;
5626 * Returns the header for the specified column.
5627 * @param {Number} col The column index
5630 getColumnHeader : function(col){
5631 return this.config[col].header;
5635 * Sets the header for a column.
5636 * @param {Number} col The column index
5637 * @param {String} header The new header
5639 setColumnHeader : function(col, header){
5640 this.config[col].header = header;
5641 this.fireEvent("headerchange", this, col, header);
5645 * Returns the tooltip for the specified column.
5646 * @param {Number} col The column index
5649 getColumnTooltip : function(col){
5650 return this.config[col].tooltip;
5653 * Sets the tooltip for a column.
5654 * @param {Number} col The column index
5655 * @param {String} tooltip The new tooltip
5657 setColumnTooltip : function(col, tooltip){
5658 this.config[col].tooltip = tooltip;
5662 * Returns the dataIndex for the specified column.
5663 * @param {Number} col The column index
5666 getDataIndex : function(col){
5667 return this.config[col].dataIndex;
5671 * Sets the dataIndex for a column.
5672 * @param {Number} col The column index
5673 * @param {Number} dataIndex The new dataIndex
5675 setDataIndex : function(col, dataIndex){
5676 this.config[col].dataIndex = dataIndex;
5682 * Returns true if the cell is editable.
5683 * @param {Number} colIndex The column index
5684 * @param {Number} rowIndex The row index - this is nto actually used..?
5687 isCellEditable : function(colIndex, rowIndex){
5688 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5692 * Returns the editor defined for the cell/column.
5693 * return false or null to disable editing.
5694 * @param {Number} colIndex The column index
5695 * @param {Number} rowIndex The row index
5698 getCellEditor : function(colIndex, rowIndex){
5699 return this.config[colIndex].editor;
5703 * Sets if a column is editable.
5704 * @param {Number} col The column index
5705 * @param {Boolean} editable True if the column is editable
5707 setEditable : function(col, editable){
5708 this.config[col].editable = editable;
5713 * Returns true if the column is hidden.
5714 * @param {Number} colIndex The column index
5717 isHidden : function(colIndex){
5718 return this.config[colIndex].hidden;
5723 * Returns true if the column width cannot be changed
5725 isFixed : function(colIndex){
5726 return this.config[colIndex].fixed;
5730 * Returns true if the column can be resized
5733 isResizable : function(colIndex){
5734 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5737 * Sets if a column is hidden.
5738 * @param {Number} colIndex The column index
5739 * @param {Boolean} hidden True if the column is hidden
5741 setHidden : function(colIndex, hidden){
5742 this.config[colIndex].hidden = hidden;
5743 this.totalWidth = null;
5744 this.fireEvent("hiddenchange", this, colIndex, hidden);
5748 * Sets the editor for a column.
5749 * @param {Number} col The column index
5750 * @param {Object} editor The editor object
5752 setEditor : function(col, editor){
5753 this.config[col].editor = editor;
5757 Roo.grid.ColumnModel.defaultRenderer = function(value)
5759 if(typeof value == "object") {
5762 if(typeof value == "string" && value.length < 1){
5766 return String.format("{0}", value);
5769 // Alias for backwards compatibility
5770 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5773 * Ext JS Library 1.1.1
5774 * Copyright(c) 2006-2007, Ext JS, LLC.
5776 * Originally Released Under LGPL - original licence link has changed is not relivant.
5779 * <script type="text/javascript">
5783 * @class Roo.LoadMask
5784 * A simple utility class for generically masking elements while loading data. If the element being masked has
5785 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5786 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5787 * element's UpdateManager load indicator and will be destroyed after the initial load.
5789 * Create a new LoadMask
5790 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5791 * @param {Object} config The config object
5793 Roo.LoadMask = function(el, config){
5794 this.el = Roo.get(el);
5795 Roo.apply(this, config);
5797 this.store.on('beforeload', this.onBeforeLoad, this);
5798 this.store.on('load', this.onLoad, this);
5799 this.store.on('loadexception', this.onLoadException, this);
5800 this.removeMask = false;
5802 var um = this.el.getUpdateManager();
5803 um.showLoadIndicator = false; // disable the default indicator
5804 um.on('beforeupdate', this.onBeforeLoad, this);
5805 um.on('update', this.onLoad, this);
5806 um.on('failure', this.onLoad, this);
5807 this.removeMask = true;
5811 Roo.LoadMask.prototype = {
5813 * @cfg {Boolean} removeMask
5814 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5815 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5819 * The text to display in a centered loading message box (defaults to 'Loading...')
5823 * @cfg {String} msgCls
5824 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5826 msgCls : 'x-mask-loading',
5829 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5835 * Disables the mask to prevent it from being displayed
5837 disable : function(){
5838 this.disabled = true;
5842 * Enables the mask so that it can be displayed
5844 enable : function(){
5845 this.disabled = false;
5848 onLoadException : function()
5852 if (typeof(arguments[3]) != 'undefined') {
5853 Roo.MessageBox.alert("Error loading",arguments[3]);
5857 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5858 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5865 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5870 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5874 onBeforeLoad : function(){
5876 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5881 destroy : function(){
5883 this.store.un('beforeload', this.onBeforeLoad, this);
5884 this.store.un('load', this.onLoad, this);
5885 this.store.un('loadexception', this.onLoadException, this);
5887 var um = this.el.getUpdateManager();
5888 um.un('beforeupdate', this.onBeforeLoad, this);
5889 um.un('update', this.onLoad, this);
5890 um.un('failure', this.onLoad, this);
5901 * @class Roo.bootstrap.Table
5902 * @extends Roo.bootstrap.Component
5903 * Bootstrap Table class
5904 * @cfg {String} cls table class
5905 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5906 * @cfg {String} bgcolor Specifies the background color for a table
5907 * @cfg {Number} border Specifies whether the table cells should have borders or not
5908 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5909 * @cfg {Number} cellspacing Specifies the space between cells
5910 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5911 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5912 * @cfg {String} sortable Specifies that the table should be sortable
5913 * @cfg {String} summary Specifies a summary of the content of a table
5914 * @cfg {Number} width Specifies the width of a table
5915 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5917 * @cfg {boolean} striped Should the rows be alternative striped
5918 * @cfg {boolean} bordered Add borders to the table
5919 * @cfg {boolean} hover Add hover highlighting
5920 * @cfg {boolean} condensed Format condensed
5921 * @cfg {boolean} responsive Format condensed
5922 * @cfg {Boolean} loadMask (true|false) default false
5923 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5924 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5925 * @cfg {Boolean} rowSelection (true|false) default false
5926 * @cfg {Boolean} cellSelection (true|false) default false
5927 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5928 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5929 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5930 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5934 * Create a new Table
5935 * @param {Object} config The config object
5938 Roo.bootstrap.Table = function(config){
5939 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5944 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5945 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5946 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5947 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5949 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5951 this.sm.grid = this;
5952 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5953 this.sm = this.selModel;
5954 this.sm.xmodule = this.xmodule || false;
5957 if (this.cm && typeof(this.cm.config) == 'undefined') {
5958 this.colModel = new Roo.grid.ColumnModel(this.cm);
5959 this.cm = this.colModel;
5960 this.cm.xmodule = this.xmodule || false;
5963 this.store= Roo.factory(this.store, Roo.data);
5964 this.ds = this.store;
5965 this.ds.xmodule = this.xmodule || false;
5968 if (this.footer && this.store) {
5969 this.footer.dataSource = this.ds;
5970 this.footer = Roo.factory(this.footer);
5977 * Fires when a cell is clicked
5978 * @param {Roo.bootstrap.Table} this
5979 * @param {Roo.Element} el
5980 * @param {Number} rowIndex
5981 * @param {Number} columnIndex
5982 * @param {Roo.EventObject} e
5986 * @event celldblclick
5987 * Fires when a cell is double clicked
5988 * @param {Roo.bootstrap.Table} this
5989 * @param {Roo.Element} el
5990 * @param {Number} rowIndex
5991 * @param {Number} columnIndex
5992 * @param {Roo.EventObject} e
5994 "celldblclick" : true,
5997 * Fires when a row is clicked
5998 * @param {Roo.bootstrap.Table} this
5999 * @param {Roo.Element} el
6000 * @param {Number} rowIndex
6001 * @param {Roo.EventObject} e
6005 * @event rowdblclick
6006 * Fires when a row is double clicked
6007 * @param {Roo.bootstrap.Table} this
6008 * @param {Roo.Element} el
6009 * @param {Number} rowIndex
6010 * @param {Roo.EventObject} e
6012 "rowdblclick" : true,
6015 * Fires when a mouseover occur
6016 * @param {Roo.bootstrap.Table} this
6017 * @param {Roo.Element} el
6018 * @param {Number} rowIndex
6019 * @param {Number} columnIndex
6020 * @param {Roo.EventObject} e
6025 * Fires when a mouseout occur
6026 * @param {Roo.bootstrap.Table} this
6027 * @param {Roo.Element} el
6028 * @param {Number} rowIndex
6029 * @param {Number} columnIndex
6030 * @param {Roo.EventObject} e
6035 * Fires when a row is rendered, so you can change add a style to it.
6036 * @param {Roo.bootstrap.Table} this
6037 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6041 * @event rowsrendered
6042 * Fires when all the rows have been rendered
6043 * @param {Roo.bootstrap.Table} this
6045 'rowsrendered' : true,
6047 * @event contextmenu
6048 * The raw contextmenu event for the entire grid.
6049 * @param {Roo.EventObject} e
6051 "contextmenu" : true,
6053 * @event rowcontextmenu
6054 * Fires when a row is right clicked
6055 * @param {Roo.bootstrap.Table} this
6056 * @param {Number} rowIndex
6057 * @param {Roo.EventObject} e
6059 "rowcontextmenu" : true,
6061 * @event cellcontextmenu
6062 * Fires when a cell is right clicked
6063 * @param {Roo.bootstrap.Table} this
6064 * @param {Number} rowIndex
6065 * @param {Number} cellIndex
6066 * @param {Roo.EventObject} e
6068 "cellcontextmenu" : true,
6070 * @event headercontextmenu
6071 * Fires when a header is right clicked
6072 * @param {Roo.bootstrap.Table} this
6073 * @param {Number} columnIndex
6074 * @param {Roo.EventObject} e
6076 "headercontextmenu" : true
6080 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6106 rowSelection : false,
6107 cellSelection : false,
6110 // Roo.Element - the tbody
6112 // Roo.Element - thead element
6115 container: false, // used by gridpanel...
6121 auto_hide_footer : false,
6123 getAutoCreate : function()
6125 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6132 if (this.scrollBody) {
6133 cfg.cls += ' table-body-fixed';
6136 cfg.cls += ' table-striped';
6140 cfg.cls += ' table-hover';
6142 if (this.bordered) {
6143 cfg.cls += ' table-bordered';
6145 if (this.condensed) {
6146 cfg.cls += ' table-condensed';
6148 if (this.responsive) {
6149 cfg.cls += ' table-responsive';
6153 cfg.cls+= ' ' +this.cls;
6156 // this lot should be simplifed...
6169 ].forEach(function(k) {
6177 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6180 if(this.store || this.cm){
6181 if(this.headerShow){
6182 cfg.cn.push(this.renderHeader());
6185 cfg.cn.push(this.renderBody());
6187 if(this.footerShow){
6188 cfg.cn.push(this.renderFooter());
6190 // where does this come from?
6191 //cfg.cls+= ' TableGrid';
6194 return { cn : [ cfg ] };
6197 initEvents : function()
6199 if(!this.store || !this.cm){
6202 if (this.selModel) {
6203 this.selModel.initEvents();
6207 //Roo.log('initEvents with ds!!!!');
6209 this.mainBody = this.el.select('tbody', true).first();
6210 this.mainHead = this.el.select('thead', true).first();
6211 this.mainFoot = this.el.select('tfoot', true).first();
6217 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6218 e.on('click', _this.sort, _this);
6221 this.mainBody.on("click", this.onClick, this);
6222 this.mainBody.on("dblclick", this.onDblClick, this);
6224 // why is this done????? = it breaks dialogs??
6225 //this.parent().el.setStyle('position', 'relative');
6229 this.footer.parentId = this.id;
6230 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6233 this.el.select('tfoot tr td').first().addClass('hide');
6238 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6241 this.store.on('load', this.onLoad, this);
6242 this.store.on('beforeload', this.onBeforeLoad, this);
6243 this.store.on('update', this.onUpdate, this);
6244 this.store.on('add', this.onAdd, this);
6245 this.store.on("clear", this.clear, this);
6247 this.el.on("contextmenu", this.onContextMenu, this);
6249 this.mainBody.on('scroll', this.onBodyScroll, this);
6251 this.cm.on("headerchange", this.onHeaderChange, this);
6253 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6257 onContextMenu : function(e, t)
6259 this.processEvent("contextmenu", e);
6262 processEvent : function(name, e)
6264 if (name != 'touchstart' ) {
6265 this.fireEvent(name, e);
6268 var t = e.getTarget();
6270 var cell = Roo.get(t);
6276 if(cell.findParent('tfoot', false, true)){
6280 if(cell.findParent('thead', false, true)){
6282 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6283 cell = Roo.get(t).findParent('th', false, true);
6285 Roo.log("failed to find th in thead?");
6286 Roo.log(e.getTarget());
6291 var cellIndex = cell.dom.cellIndex;
6293 var ename = name == 'touchstart' ? 'click' : name;
6294 this.fireEvent("header" + ename, this, cellIndex, e);
6299 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6300 cell = Roo.get(t).findParent('td', false, true);
6302 Roo.log("failed to find th in tbody?");
6303 Roo.log(e.getTarget());
6308 var row = cell.findParent('tr', false, true);
6309 var cellIndex = cell.dom.cellIndex;
6310 var rowIndex = row.dom.rowIndex - 1;
6314 this.fireEvent("row" + name, this, rowIndex, e);
6318 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6324 onMouseover : function(e, el)
6326 var cell = Roo.get(el);
6332 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6333 cell = cell.findParent('td', false, true);
6336 var row = cell.findParent('tr', false, true);
6337 var cellIndex = cell.dom.cellIndex;
6338 var rowIndex = row.dom.rowIndex - 1; // start from 0
6340 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6344 onMouseout : function(e, el)
6346 var cell = Roo.get(el);
6352 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6353 cell = cell.findParent('td', false, true);
6356 var row = cell.findParent('tr', false, true);
6357 var cellIndex = cell.dom.cellIndex;
6358 var rowIndex = row.dom.rowIndex - 1; // start from 0
6360 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6364 onClick : function(e, el)
6366 var cell = Roo.get(el);
6368 if(!cell || (!this.cellSelection && !this.rowSelection)){
6372 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6373 cell = cell.findParent('td', false, true);
6376 if(!cell || typeof(cell) == 'undefined'){
6380 var row = cell.findParent('tr', false, true);
6382 if(!row || typeof(row) == 'undefined'){
6386 var cellIndex = cell.dom.cellIndex;
6387 var rowIndex = this.getRowIndex(row);
6389 // why??? - should these not be based on SelectionModel?
6390 if(this.cellSelection){
6391 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6394 if(this.rowSelection){
6395 this.fireEvent('rowclick', this, row, rowIndex, e);
6401 onDblClick : function(e,el)
6403 var cell = Roo.get(el);
6405 if(!cell || (!this.cellSelection && !this.rowSelection)){
6409 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6410 cell = cell.findParent('td', false, true);
6413 if(!cell || typeof(cell) == 'undefined'){
6417 var row = cell.findParent('tr', false, true);
6419 if(!row || typeof(row) == 'undefined'){
6423 var cellIndex = cell.dom.cellIndex;
6424 var rowIndex = this.getRowIndex(row);
6426 if(this.cellSelection){
6427 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6430 if(this.rowSelection){
6431 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6435 sort : function(e,el)
6437 var col = Roo.get(el);
6439 if(!col.hasClass('sortable')){
6443 var sort = col.attr('sort');
6446 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6450 this.store.sortInfo = {field : sort, direction : dir};
6453 Roo.log("calling footer first");
6454 this.footer.onClick('first');
6457 this.store.load({ params : { start : 0 } });
6461 renderHeader : function()
6469 this.totalWidth = 0;
6471 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6473 var config = cm.config[i];
6477 cls : 'x-hcol-' + i,
6479 html: cm.getColumnHeader(i)
6484 if(typeof(config.sortable) != 'undefined' && config.sortable){
6486 c.html = '<i class="glyphicon"></i>' + c.html;
6489 if(typeof(config.lgHeader) != 'undefined'){
6490 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6493 if(typeof(config.mdHeader) != 'undefined'){
6494 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6497 if(typeof(config.smHeader) != 'undefined'){
6498 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6501 if(typeof(config.xsHeader) != 'undefined'){
6502 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6509 if(typeof(config.tooltip) != 'undefined'){
6510 c.tooltip = config.tooltip;
6513 if(typeof(config.colspan) != 'undefined'){
6514 c.colspan = config.colspan;
6517 if(typeof(config.hidden) != 'undefined' && config.hidden){
6518 c.style += ' display:none;';
6521 if(typeof(config.dataIndex) != 'undefined'){
6522 c.sort = config.dataIndex;
6527 if(typeof(config.align) != 'undefined' && config.align.length){
6528 c.style += ' text-align:' + config.align + ';';
6531 if(typeof(config.width) != 'undefined'){
6532 c.style += ' width:' + config.width + 'px;';
6533 this.totalWidth += config.width;
6535 this.totalWidth += 100; // assume minimum of 100 per column?
6538 if(typeof(config.cls) != 'undefined'){
6539 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6542 ['xs','sm','md','lg'].map(function(size){
6544 if(typeof(config[size]) == 'undefined'){
6548 if (!config[size]) { // 0 = hidden
6549 c.cls += ' hidden-' + size;
6553 c.cls += ' col-' + size + '-' + config[size];
6563 renderBody : function()
6573 colspan : this.cm.getColumnCount()
6583 renderFooter : function()
6593 colspan : this.cm.getColumnCount()
6607 // Roo.log('ds onload');
6612 var ds = this.store;
6614 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6615 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6616 if (_this.store.sortInfo) {
6618 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6619 e.select('i', true).addClass(['glyphicon-arrow-up']);
6622 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6623 e.select('i', true).addClass(['glyphicon-arrow-down']);
6628 var tbody = this.mainBody;
6630 if(ds.getCount() > 0){
6631 ds.data.each(function(d,rowIndex){
6632 var row = this.renderRow(cm, ds, rowIndex);
6634 tbody.createChild(row);
6638 if(row.cellObjects.length){
6639 Roo.each(row.cellObjects, function(r){
6640 _this.renderCellObject(r);
6647 var tfoot = this.el.select('tfoot', true).first();
6649 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6651 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6653 var total = this.ds.getTotalCount();
6655 if(this.footer.pageSize < total){
6656 this.mainFoot.show();
6660 Roo.each(this.el.select('tbody td', true).elements, function(e){
6661 e.on('mouseover', _this.onMouseover, _this);
6664 Roo.each(this.el.select('tbody td', true).elements, function(e){
6665 e.on('mouseout', _this.onMouseout, _this);
6667 this.fireEvent('rowsrendered', this);
6673 onUpdate : function(ds,record)
6675 this.refreshRow(record);
6679 onRemove : function(ds, record, index, isUpdate){
6680 if(isUpdate !== true){
6681 this.fireEvent("beforerowremoved", this, index, record);
6683 var bt = this.mainBody.dom;
6685 var rows = this.el.select('tbody > tr', true).elements;
6687 if(typeof(rows[index]) != 'undefined'){
6688 bt.removeChild(rows[index].dom);
6691 // if(bt.rows[index]){
6692 // bt.removeChild(bt.rows[index]);
6695 if(isUpdate !== true){
6696 //this.stripeRows(index);
6697 //this.syncRowHeights(index, index);
6699 this.fireEvent("rowremoved", this, index, record);
6703 onAdd : function(ds, records, rowIndex)
6705 //Roo.log('on Add called');
6706 // - note this does not handle multiple adding very well..
6707 var bt = this.mainBody.dom;
6708 for (var i =0 ; i < records.length;i++) {
6709 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6710 //Roo.log(records[i]);
6711 //Roo.log(this.store.getAt(rowIndex+i));
6712 this.insertRow(this.store, rowIndex + i, false);
6719 refreshRow : function(record){
6720 var ds = this.store, index;
6721 if(typeof record == 'number'){
6723 record = ds.getAt(index);
6725 index = ds.indexOf(record);
6727 this.insertRow(ds, index, true);
6729 this.onRemove(ds, record, index+1, true);
6731 //this.syncRowHeights(index, index);
6733 this.fireEvent("rowupdated", this, index, record);
6736 insertRow : function(dm, rowIndex, isUpdate){
6739 this.fireEvent("beforerowsinserted", this, rowIndex);
6741 //var s = this.getScrollState();
6742 var row = this.renderRow(this.cm, this.store, rowIndex);
6743 // insert before rowIndex..
6744 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6748 if(row.cellObjects.length){
6749 Roo.each(row.cellObjects, function(r){
6750 _this.renderCellObject(r);
6755 this.fireEvent("rowsinserted", this, rowIndex);
6756 //this.syncRowHeights(firstRow, lastRow);
6757 //this.stripeRows(firstRow);
6764 getRowDom : function(rowIndex)
6766 var rows = this.el.select('tbody > tr', true).elements;
6768 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6771 // returns the object tree for a tr..
6774 renderRow : function(cm, ds, rowIndex)
6776 var d = ds.getAt(rowIndex);
6780 cls : 'x-row-' + rowIndex,
6784 var cellObjects = [];
6786 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6787 var config = cm.config[i];
6789 var renderer = cm.getRenderer(i);
6793 if(typeof(renderer) !== 'undefined'){
6794 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6796 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6797 // and are rendered into the cells after the row is rendered - using the id for the element.
6799 if(typeof(value) === 'object'){
6809 rowIndex : rowIndex,
6814 this.fireEvent('rowclass', this, rowcfg);
6818 cls : rowcfg.rowClass + ' x-col-' + i,
6820 html: (typeof(value) === 'object') ? '' : value
6827 if(typeof(config.colspan) != 'undefined'){
6828 td.colspan = config.colspan;
6831 if(typeof(config.hidden) != 'undefined' && config.hidden){
6832 td.style += ' display:none;';
6835 if(typeof(config.align) != 'undefined' && config.align.length){
6836 td.style += ' text-align:' + config.align + ';';
6838 if(typeof(config.valign) != 'undefined' && config.valign.length){
6839 td.style += ' vertical-align:' + config.valign + ';';
6842 if(typeof(config.width) != 'undefined'){
6843 td.style += ' width:' + config.width + 'px;';
6846 if(typeof(config.cursor) != 'undefined'){
6847 td.style += ' cursor:' + config.cursor + ';';
6850 if(typeof(config.cls) != 'undefined'){
6851 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6854 ['xs','sm','md','lg'].map(function(size){
6856 if(typeof(config[size]) == 'undefined'){
6860 if (!config[size]) { // 0 = hidden
6861 td.cls += ' hidden-' + size;
6865 td.cls += ' col-' + size + '-' + config[size];
6873 row.cellObjects = cellObjects;
6881 onBeforeLoad : function()
6890 this.el.select('tbody', true).first().dom.innerHTML = '';
6893 * Show or hide a row.
6894 * @param {Number} rowIndex to show or hide
6895 * @param {Boolean} state hide
6897 setRowVisibility : function(rowIndex, state)
6899 var bt = this.mainBody.dom;
6901 var rows = this.el.select('tbody > tr', true).elements;
6903 if(typeof(rows[rowIndex]) == 'undefined'){
6906 rows[rowIndex].dom.style.display = state ? '' : 'none';
6910 getSelectionModel : function(){
6912 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6914 return this.selModel;
6917 * Render the Roo.bootstrap object from renderder
6919 renderCellObject : function(r)
6923 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6925 var t = r.cfg.render(r.container);
6928 Roo.each(r.cfg.cn, function(c){
6930 container: t.getChildContainer(),
6933 _this.renderCellObject(child);
6938 getRowIndex : function(row)
6942 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6953 * Returns the grid's underlying element = used by panel.Grid
6954 * @return {Element} The element
6956 getGridEl : function(){
6960 * Forces a resize - used by panel.Grid
6961 * @return {Element} The element
6963 autoSize : function()
6965 //var ctr = Roo.get(this.container.dom.parentElement);
6966 var ctr = Roo.get(this.el.dom);
6968 var thd = this.getGridEl().select('thead',true).first();
6969 var tbd = this.getGridEl().select('tbody', true).first();
6970 var tfd = this.getGridEl().select('tfoot', true).first();
6972 var cw = ctr.getWidth();
6976 tbd.setSize(ctr.getWidth(),
6977 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6979 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6982 cw = Math.max(cw, this.totalWidth);
6983 this.getGridEl().select('tr',true).setWidth(cw);
6984 // resize 'expandable coloumn?
6986 return; // we doe not have a view in this design..
6989 onBodyScroll: function()
6991 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6993 this.mainHead.setStyle({
6994 'position' : 'relative',
6995 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7001 var scrollHeight = this.mainBody.dom.scrollHeight;
7003 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7005 var height = this.mainBody.getHeight();
7007 if(scrollHeight - height == scrollTop) {
7009 var total = this.ds.getTotalCount();
7011 if(this.footer.cursor + this.footer.pageSize < total){
7013 this.footer.ds.load({
7015 start : this.footer.cursor + this.footer.pageSize,
7016 limit : this.footer.pageSize
7026 onHeaderChange : function()
7028 var header = this.renderHeader();
7029 var table = this.el.select('table', true).first();
7031 this.mainHead.remove();
7032 this.mainHead = table.createChild(header, this.mainBody, false);
7035 onHiddenChange : function(colModel, colIndex, hidden)
7037 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7038 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7040 this.CSS.updateRule(thSelector, "display", "");
7041 this.CSS.updateRule(tdSelector, "display", "");
7044 this.CSS.updateRule(thSelector, "display", "none");
7045 this.CSS.updateRule(tdSelector, "display", "none");
7048 this.onHeaderChange();
7065 * @class Roo.bootstrap.TableCell
7066 * @extends Roo.bootstrap.Component
7067 * Bootstrap TableCell class
7068 * @cfg {String} html cell contain text
7069 * @cfg {String} cls cell class
7070 * @cfg {String} tag cell tag (td|th) default td
7071 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7072 * @cfg {String} align Aligns the content in a cell
7073 * @cfg {String} axis Categorizes cells
7074 * @cfg {String} bgcolor Specifies the background color of a cell
7075 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7076 * @cfg {Number} colspan Specifies the number of columns a cell should span
7077 * @cfg {String} headers Specifies one or more header cells a cell is related to
7078 * @cfg {Number} height Sets the height of a cell
7079 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7080 * @cfg {Number} rowspan Sets the number of rows a cell should span
7081 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7082 * @cfg {String} valign Vertical aligns the content in a cell
7083 * @cfg {Number} width Specifies the width of a cell
7086 * Create a new TableCell
7087 * @param {Object} config The config object
7090 Roo.bootstrap.TableCell = function(config){
7091 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7094 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7114 getAutoCreate : function(){
7115 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7135 cfg.align=this.align
7141 cfg.bgcolor=this.bgcolor
7144 cfg.charoff=this.charoff
7147 cfg.colspan=this.colspan
7150 cfg.headers=this.headers
7153 cfg.height=this.height
7156 cfg.nowrap=this.nowrap
7159 cfg.rowspan=this.rowspan
7162 cfg.scope=this.scope
7165 cfg.valign=this.valign
7168 cfg.width=this.width
7187 * @class Roo.bootstrap.TableRow
7188 * @extends Roo.bootstrap.Component
7189 * Bootstrap TableRow class
7190 * @cfg {String} cls row class
7191 * @cfg {String} align Aligns the content in a table row
7192 * @cfg {String} bgcolor Specifies a background color for a table row
7193 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7194 * @cfg {String} valign Vertical aligns the content in a table row
7197 * Create a new TableRow
7198 * @param {Object} config The config object
7201 Roo.bootstrap.TableRow = function(config){
7202 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7205 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7213 getAutoCreate : function(){
7214 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7224 cfg.align = this.align;
7227 cfg.bgcolor = this.bgcolor;
7230 cfg.charoff = this.charoff;
7233 cfg.valign = this.valign;
7251 * @class Roo.bootstrap.TableBody
7252 * @extends Roo.bootstrap.Component
7253 * Bootstrap TableBody class
7254 * @cfg {String} cls element class
7255 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7256 * @cfg {String} align Aligns the content inside the element
7257 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7258 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7261 * Create a new TableBody
7262 * @param {Object} config The config object
7265 Roo.bootstrap.TableBody = function(config){
7266 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7269 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7277 getAutoCreate : function(){
7278 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7292 cfg.align = this.align;
7295 cfg.charoff = this.charoff;
7298 cfg.valign = this.valign;
7305 // initEvents : function()
7312 // this.store = Roo.factory(this.store, Roo.data);
7313 // this.store.on('load', this.onLoad, this);
7315 // this.store.load();
7319 // onLoad: function ()
7321 // this.fireEvent('load', this);
7331 * Ext JS Library 1.1.1
7332 * Copyright(c) 2006-2007, Ext JS, LLC.
7334 * Originally Released Under LGPL - original licence link has changed is not relivant.
7337 * <script type="text/javascript">
7340 // as we use this in bootstrap.
7341 Roo.namespace('Roo.form');
7343 * @class Roo.form.Action
7344 * Internal Class used to handle form actions
7346 * @param {Roo.form.BasicForm} el The form element or its id
7347 * @param {Object} config Configuration options
7352 // define the action interface
7353 Roo.form.Action = function(form, options){
7355 this.options = options || {};
7358 * Client Validation Failed
7361 Roo.form.Action.CLIENT_INVALID = 'client';
7363 * Server Validation Failed
7366 Roo.form.Action.SERVER_INVALID = 'server';
7368 * Connect to Server Failed
7371 Roo.form.Action.CONNECT_FAILURE = 'connect';
7373 * Reading Data from Server Failed
7376 Roo.form.Action.LOAD_FAILURE = 'load';
7378 Roo.form.Action.prototype = {
7380 failureType : undefined,
7381 response : undefined,
7385 run : function(options){
7390 success : function(response){
7395 handleResponse : function(response){
7399 // default connection failure
7400 failure : function(response){
7402 this.response = response;
7403 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7404 this.form.afterAction(this, false);
7407 processResponse : function(response){
7408 this.response = response;
7409 if(!response.responseText){
7412 this.result = this.handleResponse(response);
7416 // utility functions used internally
7417 getUrl : function(appendParams){
7418 var url = this.options.url || this.form.url || this.form.el.dom.action;
7420 var p = this.getParams();
7422 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7428 getMethod : function(){
7429 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7432 getParams : function(){
7433 var bp = this.form.baseParams;
7434 var p = this.options.params;
7436 if(typeof p == "object"){
7437 p = Roo.urlEncode(Roo.applyIf(p, bp));
7438 }else if(typeof p == 'string' && bp){
7439 p += '&' + Roo.urlEncode(bp);
7442 p = Roo.urlEncode(bp);
7447 createCallback : function(){
7449 success: this.success,
7450 failure: this.failure,
7452 timeout: (this.form.timeout*1000),
7453 upload: this.form.fileUpload ? this.success : undefined
7458 Roo.form.Action.Submit = function(form, options){
7459 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7462 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7465 haveProgress : false,
7466 uploadComplete : false,
7468 // uploadProgress indicator.
7469 uploadProgress : function()
7471 if (!this.form.progressUrl) {
7475 if (!this.haveProgress) {
7476 Roo.MessageBox.progress("Uploading", "Uploading");
7478 if (this.uploadComplete) {
7479 Roo.MessageBox.hide();
7483 this.haveProgress = true;
7485 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7487 var c = new Roo.data.Connection();
7489 url : this.form.progressUrl,
7494 success : function(req){
7495 //console.log(data);
7499 rdata = Roo.decode(req.responseText)
7501 Roo.log("Invalid data from server..");
7505 if (!rdata || !rdata.success) {
7507 Roo.MessageBox.alert(Roo.encode(rdata));
7510 var data = rdata.data;
7512 if (this.uploadComplete) {
7513 Roo.MessageBox.hide();
7518 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7519 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7522 this.uploadProgress.defer(2000,this);
7525 failure: function(data) {
7526 Roo.log('progress url failed ');
7537 // run get Values on the form, so it syncs any secondary forms.
7538 this.form.getValues();
7540 var o = this.options;
7541 var method = this.getMethod();
7542 var isPost = method == 'POST';
7543 if(o.clientValidation === false || this.form.isValid()){
7545 if (this.form.progressUrl) {
7546 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7547 (new Date() * 1) + '' + Math.random());
7552 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7553 form:this.form.el.dom,
7554 url:this.getUrl(!isPost),
7556 params:isPost ? this.getParams() : null,
7557 isUpload: this.form.fileUpload
7560 this.uploadProgress();
7562 }else if (o.clientValidation !== false){ // client validation failed
7563 this.failureType = Roo.form.Action.CLIENT_INVALID;
7564 this.form.afterAction(this, false);
7568 success : function(response)
7570 this.uploadComplete= true;
7571 if (this.haveProgress) {
7572 Roo.MessageBox.hide();
7576 var result = this.processResponse(response);
7577 if(result === true || result.success){
7578 this.form.afterAction(this, true);
7582 this.form.markInvalid(result.errors);
7583 this.failureType = Roo.form.Action.SERVER_INVALID;
7585 this.form.afterAction(this, false);
7587 failure : function(response)
7589 this.uploadComplete= true;
7590 if (this.haveProgress) {
7591 Roo.MessageBox.hide();
7594 this.response = response;
7595 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7596 this.form.afterAction(this, false);
7599 handleResponse : function(response){
7600 if(this.form.errorReader){
7601 var rs = this.form.errorReader.read(response);
7604 for(var i = 0, len = rs.records.length; i < len; i++) {
7605 var r = rs.records[i];
7609 if(errors.length < 1){
7613 success : rs.success,
7619 ret = Roo.decode(response.responseText);
7623 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7633 Roo.form.Action.Load = function(form, options){
7634 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7635 this.reader = this.form.reader;
7638 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7643 Roo.Ajax.request(Roo.apply(
7644 this.createCallback(), {
7645 method:this.getMethod(),
7646 url:this.getUrl(false),
7647 params:this.getParams()
7651 success : function(response){
7653 var result = this.processResponse(response);
7654 if(result === true || !result.success || !result.data){
7655 this.failureType = Roo.form.Action.LOAD_FAILURE;
7656 this.form.afterAction(this, false);
7659 this.form.clearInvalid();
7660 this.form.setValues(result.data);
7661 this.form.afterAction(this, true);
7664 handleResponse : function(response){
7665 if(this.form.reader){
7666 var rs = this.form.reader.read(response);
7667 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7669 success : rs.success,
7673 return Roo.decode(response.responseText);
7677 Roo.form.Action.ACTION_TYPES = {
7678 'load' : Roo.form.Action.Load,
7679 'submit' : Roo.form.Action.Submit
7688 * @class Roo.bootstrap.Form
7689 * @extends Roo.bootstrap.Component
7690 * Bootstrap Form class
7691 * @cfg {String} method GET | POST (default POST)
7692 * @cfg {String} labelAlign top | left (default top)
7693 * @cfg {String} align left | right - for navbars
7694 * @cfg {Boolean} loadMask load mask when submit (default true)
7699 * @param {Object} config The config object
7703 Roo.bootstrap.Form = function(config){
7705 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7707 Roo.bootstrap.Form.popover.apply();
7711 * @event clientvalidation
7712 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7713 * @param {Form} this
7714 * @param {Boolean} valid true if the form has passed client-side validation
7716 clientvalidation: true,
7718 * @event beforeaction
7719 * Fires before any action is performed. Return false to cancel the action.
7720 * @param {Form} this
7721 * @param {Action} action The action to be performed
7725 * @event actionfailed
7726 * Fires when an action fails.
7727 * @param {Form} this
7728 * @param {Action} action The action that failed
7730 actionfailed : true,
7732 * @event actioncomplete
7733 * Fires when an action is completed.
7734 * @param {Form} this
7735 * @param {Action} action The action that completed
7737 actioncomplete : true
7741 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7744 * @cfg {String} method
7745 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7750 * The URL to use for form actions if one isn't supplied in the action options.
7753 * @cfg {Boolean} fileUpload
7754 * Set to true if this form is a file upload.
7758 * @cfg {Object} baseParams
7759 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7763 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7767 * @cfg {Sting} align (left|right) for navbar forms
7772 activeAction : null,
7775 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7776 * element by passing it or its id or mask the form itself by passing in true.
7779 waitMsgTarget : false,
7784 * @cfg {Boolean} errorMask (true|false) default false
7789 * @cfg {Number} maskOffset Default 100
7794 * @cfg {Boolean} maskBody
7798 getAutoCreate : function(){
7802 method : this.method || 'POST',
7803 id : this.id || Roo.id(),
7806 if (this.parent().xtype.match(/^Nav/)) {
7807 cfg.cls = 'navbar-form navbar-' + this.align;
7811 if (this.labelAlign == 'left' ) {
7812 cfg.cls += ' form-horizontal';
7818 initEvents : function()
7820 this.el.on('submit', this.onSubmit, this);
7821 // this was added as random key presses on the form where triggering form submit.
7822 this.el.on('keypress', function(e) {
7823 if (e.getCharCode() != 13) {
7826 // we might need to allow it for textareas.. and some other items.
7827 // check e.getTarget().
7829 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7833 Roo.log("keypress blocked");
7841 onSubmit : function(e){
7846 * Returns true if client-side validation on the form is successful.
7849 isValid : function(){
7850 var items = this.getItems();
7854 items.each(function(f){
7860 Roo.log('invalid field: ' + f.name);
7864 if(!target && f.el.isVisible(true)){
7870 if(this.errorMask && !valid){
7871 Roo.bootstrap.Form.popover.mask(this, target);
7878 * Returns true if any fields in this form have changed since their original load.
7881 isDirty : function(){
7883 var items = this.getItems();
7884 items.each(function(f){
7894 * Performs a predefined action (submit or load) or custom actions you define on this form.
7895 * @param {String} actionName The name of the action type
7896 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7897 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7898 * accept other config options):
7900 Property Type Description
7901 ---------------- --------------- ----------------------------------------------------------------------------------
7902 url String The url for the action (defaults to the form's url)
7903 method String The form method to use (defaults to the form's method, or POST if not defined)
7904 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7905 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7906 validate the form on the client (defaults to false)
7908 * @return {BasicForm} this
7910 doAction : function(action, options){
7911 if(typeof action == 'string'){
7912 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7914 if(this.fireEvent('beforeaction', this, action) !== false){
7915 this.beforeAction(action);
7916 action.run.defer(100, action);
7922 beforeAction : function(action){
7923 var o = action.options;
7928 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7930 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7933 // not really supported yet.. ??
7935 //if(this.waitMsgTarget === true){
7936 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7937 //}else if(this.waitMsgTarget){
7938 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7939 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7941 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7947 afterAction : function(action, success){
7948 this.activeAction = null;
7949 var o = action.options;
7954 Roo.get(document.body).unmask();
7960 //if(this.waitMsgTarget === true){
7961 // this.el.unmask();
7962 //}else if(this.waitMsgTarget){
7963 // this.waitMsgTarget.unmask();
7965 // Roo.MessageBox.updateProgress(1);
7966 // Roo.MessageBox.hide();
7973 Roo.callback(o.success, o.scope, [this, action]);
7974 this.fireEvent('actioncomplete', this, action);
7978 // failure condition..
7979 // we have a scenario where updates need confirming.
7980 // eg. if a locking scenario exists..
7981 // we look for { errors : { needs_confirm : true }} in the response.
7983 (typeof(action.result) != 'undefined') &&
7984 (typeof(action.result.errors) != 'undefined') &&
7985 (typeof(action.result.errors.needs_confirm) != 'undefined')
7988 Roo.log("not supported yet");
7991 Roo.MessageBox.confirm(
7992 "Change requires confirmation",
7993 action.result.errorMsg,
7998 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8008 Roo.callback(o.failure, o.scope, [this, action]);
8009 // show an error message if no failed handler is set..
8010 if (!this.hasListener('actionfailed')) {
8011 Roo.log("need to add dialog support");
8013 Roo.MessageBox.alert("Error",
8014 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8015 action.result.errorMsg :
8016 "Saving Failed, please check your entries or try again"
8021 this.fireEvent('actionfailed', this, action);
8026 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8027 * @param {String} id The value to search for
8030 findField : function(id){
8031 var items = this.getItems();
8032 var field = items.get(id);
8034 items.each(function(f){
8035 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8042 return field || null;
8045 * Mark fields in this form invalid in bulk.
8046 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8047 * @return {BasicForm} this
8049 markInvalid : function(errors){
8050 if(errors instanceof Array){
8051 for(var i = 0, len = errors.length; i < len; i++){
8052 var fieldError = errors[i];
8053 var f = this.findField(fieldError.id);
8055 f.markInvalid(fieldError.msg);
8061 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8062 field.markInvalid(errors[id]);
8066 //Roo.each(this.childForms || [], function (f) {
8067 // f.markInvalid(errors);
8074 * Set values for fields in this form in bulk.
8075 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8076 * @return {BasicForm} this
8078 setValues : function(values){
8079 if(values instanceof Array){ // array of objects
8080 for(var i = 0, len = values.length; i < len; i++){
8082 var f = this.findField(v.id);
8084 f.setValue(v.value);
8085 if(this.trackResetOnLoad){
8086 f.originalValue = f.getValue();
8090 }else{ // object hash
8093 if(typeof values[id] != 'function' && (field = this.findField(id))){
8095 if (field.setFromData &&
8097 field.displayField &&
8098 // combos' with local stores can
8099 // be queried via setValue()
8100 // to set their value..
8101 (field.store && !field.store.isLocal)
8105 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8106 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8107 field.setFromData(sd);
8109 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8111 field.setFromData(values);
8114 field.setValue(values[id]);
8118 if(this.trackResetOnLoad){
8119 field.originalValue = field.getValue();
8125 //Roo.each(this.childForms || [], function (f) {
8126 // f.setValues(values);
8133 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8134 * they are returned as an array.
8135 * @param {Boolean} asString
8138 getValues : function(asString){
8139 //if (this.childForms) {
8140 // copy values from the child forms
8141 // Roo.each(this.childForms, function (f) {
8142 // this.setValues(f.getValues());
8148 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8149 if(asString === true){
8152 return Roo.urlDecode(fs);
8156 * Returns the fields in this form as an object with key/value pairs.
8157 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8160 getFieldValues : function(with_hidden)
8162 var items = this.getItems();
8164 items.each(function(f){
8170 var v = f.getValue();
8172 if (f.inputType =='radio') {
8173 if (typeof(ret[f.getName()]) == 'undefined') {
8174 ret[f.getName()] = ''; // empty..
8177 if (!f.el.dom.checked) {
8185 if(f.xtype == 'MoneyField'){
8186 ret[f.currencyName] = f.getCurrency();
8189 // not sure if this supported any more..
8190 if ((typeof(v) == 'object') && f.getRawValue) {
8191 v = f.getRawValue() ; // dates..
8193 // combo boxes where name != hiddenName...
8194 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8195 ret[f.name] = f.getRawValue();
8197 ret[f.getName()] = v;
8204 * Clears all invalid messages in this form.
8205 * @return {BasicForm} this
8207 clearInvalid : function(){
8208 var items = this.getItems();
8210 items.each(function(f){
8219 * @return {BasicForm} this
8222 var items = this.getItems();
8223 items.each(function(f){
8227 Roo.each(this.childForms || [], function (f) {
8235 getItems : function()
8237 var r=new Roo.util.MixedCollection(false, function(o){
8238 return o.id || (o.id = Roo.id());
8240 var iter = function(el) {
8247 Roo.each(el.items,function(e) {
8256 hideFields : function(items)
8258 Roo.each(items, function(i){
8260 var f = this.findField(i);
8266 if(f.xtype == 'DateField'){
8267 f.setVisible(false);
8276 showFields : function(items)
8278 Roo.each(items, function(i){
8280 var f = this.findField(i);
8286 if(f.xtype == 'DateField'){
8298 Roo.apply(Roo.bootstrap.Form, {
8325 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8326 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8327 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8328 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8331 this.maskEl.top.enableDisplayMode("block");
8332 this.maskEl.left.enableDisplayMode("block");
8333 this.maskEl.bottom.enableDisplayMode("block");
8334 this.maskEl.right.enableDisplayMode("block");
8336 this.toolTip = new Roo.bootstrap.Tooltip({
8337 cls : 'roo-form-error-popover',
8339 'left' : ['r-l', [-2,0], 'right'],
8340 'right' : ['l-r', [2,0], 'left'],
8341 'bottom' : ['tl-bl', [0,2], 'top'],
8342 'top' : [ 'bl-tl', [0,-2], 'bottom']
8346 this.toolTip.render(Roo.get(document.body));
8348 this.toolTip.el.enableDisplayMode("block");
8350 Roo.get(document.body).on('click', function(){
8354 Roo.get(document.body).on('touchstart', function(){
8358 this.isApplied = true
8361 mask : function(form, target)
8365 this.target = target;
8367 if(!this.form.errorMask || !target.el){
8371 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8373 Roo.log(scrollable);
8375 var ot = this.target.el.calcOffsetsTo(scrollable);
8377 var scrollTo = ot[1] - this.form.maskOffset;
8379 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8381 scrollable.scrollTo('top', scrollTo);
8383 var box = this.target.el.getBox();
8385 var zIndex = Roo.bootstrap.Modal.zIndex++;
8388 this.maskEl.top.setStyle('position', 'absolute');
8389 this.maskEl.top.setStyle('z-index', zIndex);
8390 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8391 this.maskEl.top.setLeft(0);
8392 this.maskEl.top.setTop(0);
8393 this.maskEl.top.show();
8395 this.maskEl.left.setStyle('position', 'absolute');
8396 this.maskEl.left.setStyle('z-index', zIndex);
8397 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8398 this.maskEl.left.setLeft(0);
8399 this.maskEl.left.setTop(box.y - this.padding);
8400 this.maskEl.left.show();
8402 this.maskEl.bottom.setStyle('position', 'absolute');
8403 this.maskEl.bottom.setStyle('z-index', zIndex);
8404 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8405 this.maskEl.bottom.setLeft(0);
8406 this.maskEl.bottom.setTop(box.bottom + this.padding);
8407 this.maskEl.bottom.show();
8409 this.maskEl.right.setStyle('position', 'absolute');
8410 this.maskEl.right.setStyle('z-index', zIndex);
8411 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8412 this.maskEl.right.setLeft(box.right + this.padding);
8413 this.maskEl.right.setTop(box.y - this.padding);
8414 this.maskEl.right.show();
8416 this.toolTip.bindEl = this.target.el;
8418 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8420 var tip = this.target.blankText;
8422 if(this.target.getValue() !== '' ) {
8424 if (this.target.invalidText.length) {
8425 tip = this.target.invalidText;
8426 } else if (this.target.regexText.length){
8427 tip = this.target.regexText;
8431 this.toolTip.show(tip);
8433 this.intervalID = window.setInterval(function() {
8434 Roo.bootstrap.Form.popover.unmask();
8437 window.onwheel = function(){ return false;};
8439 (function(){ this.isMasked = true; }).defer(500, this);
8445 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8449 this.maskEl.top.setStyle('position', 'absolute');
8450 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8451 this.maskEl.top.hide();
8453 this.maskEl.left.setStyle('position', 'absolute');
8454 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8455 this.maskEl.left.hide();
8457 this.maskEl.bottom.setStyle('position', 'absolute');
8458 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8459 this.maskEl.bottom.hide();
8461 this.maskEl.right.setStyle('position', 'absolute');
8462 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8463 this.maskEl.right.hide();
8465 this.toolTip.hide();
8467 this.toolTip.el.hide();
8469 window.onwheel = function(){ return true;};
8471 if(this.intervalID){
8472 window.clearInterval(this.intervalID);
8473 this.intervalID = false;
8476 this.isMasked = false;
8486 * Ext JS Library 1.1.1
8487 * Copyright(c) 2006-2007, Ext JS, LLC.
8489 * Originally Released Under LGPL - original licence link has changed is not relivant.
8492 * <script type="text/javascript">
8495 * @class Roo.form.VTypes
8496 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8499 Roo.form.VTypes = function(){
8500 // closure these in so they are only created once.
8501 var alpha = /^[a-zA-Z_]+$/;
8502 var alphanum = /^[a-zA-Z0-9_]+$/;
8503 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8504 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8506 // All these messages and functions are configurable
8509 * The function used to validate email addresses
8510 * @param {String} value The email address
8512 'email' : function(v){
8513 return email.test(v);
8516 * The error text to display when the email validation function returns false
8519 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8521 * The keystroke filter mask to be applied on email input
8524 'emailMask' : /[a-z0-9_\.\-@]/i,
8527 * The function used to validate URLs
8528 * @param {String} value The URL
8530 'url' : function(v){
8534 * The error text to display when the url validation function returns false
8537 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8540 * The function used to validate alpha values
8541 * @param {String} value The value
8543 'alpha' : function(v){
8544 return alpha.test(v);
8547 * The error text to display when the alpha validation function returns false
8550 'alphaText' : 'This field should only contain letters and _',
8552 * The keystroke filter mask to be applied on alpha input
8555 'alphaMask' : /[a-z_]/i,
8558 * The function used to validate alphanumeric values
8559 * @param {String} value The value
8561 'alphanum' : function(v){
8562 return alphanum.test(v);
8565 * The error text to display when the alphanumeric validation function returns false
8568 'alphanumText' : 'This field should only contain letters, numbers and _',
8570 * The keystroke filter mask to be applied on alphanumeric input
8573 'alphanumMask' : /[a-z0-9_]/i
8583 * @class Roo.bootstrap.Input
8584 * @extends Roo.bootstrap.Component
8585 * Bootstrap Input class
8586 * @cfg {Boolean} disabled is it disabled
8587 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8588 * @cfg {String} name name of the input
8589 * @cfg {string} fieldLabel - the label associated
8590 * @cfg {string} placeholder - placeholder to put in text.
8591 * @cfg {string} before - input group add on before
8592 * @cfg {string} after - input group add on after
8593 * @cfg {string} size - (lg|sm) or leave empty..
8594 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8595 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8596 * @cfg {Number} md colspan out of 12 for computer-sized screens
8597 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8598 * @cfg {string} value default value of the input
8599 * @cfg {Number} labelWidth set the width of label
8600 * @cfg {Number} labellg set the width of label (1-12)
8601 * @cfg {Number} labelmd set the width of label (1-12)
8602 * @cfg {Number} labelsm set the width of label (1-12)
8603 * @cfg {Number} labelxs set the width of label (1-12)
8604 * @cfg {String} labelAlign (top|left)
8605 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8606 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8607 * @cfg {String} indicatorpos (left|right) default left
8608 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8609 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8611 * @cfg {String} align (left|center|right) Default left
8612 * @cfg {Boolean} forceFeedback (true|false) Default false
8615 * Create a new Input
8616 * @param {Object} config The config object
8619 Roo.bootstrap.Input = function(config){
8621 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8626 * Fires when this field receives input focus.
8627 * @param {Roo.form.Field} this
8632 * Fires when this field loses input focus.
8633 * @param {Roo.form.Field} this
8638 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8639 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8640 * @param {Roo.form.Field} this
8641 * @param {Roo.EventObject} e The event object
8646 * Fires just before the field blurs if the field value has changed.
8647 * @param {Roo.form.Field} this
8648 * @param {Mixed} newValue The new value
8649 * @param {Mixed} oldValue The original value
8654 * Fires after the field has been marked as invalid.
8655 * @param {Roo.form.Field} this
8656 * @param {String} msg The validation message
8661 * Fires after the field has been validated with no errors.
8662 * @param {Roo.form.Field} this
8667 * Fires after the key up
8668 * @param {Roo.form.Field} this
8669 * @param {Roo.EventObject} e The event Object
8675 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8677 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8678 automatic validation (defaults to "keyup").
8680 validationEvent : "keyup",
8682 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8684 validateOnBlur : true,
8686 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8688 validationDelay : 250,
8690 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8692 focusClass : "x-form-focus", // not needed???
8696 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8698 invalidClass : "has-warning",
8701 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8703 validClass : "has-success",
8706 * @cfg {Boolean} hasFeedback (true|false) default true
8711 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8713 invalidFeedbackClass : "glyphicon-warning-sign",
8716 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8718 validFeedbackClass : "glyphicon-ok",
8721 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8723 selectOnFocus : false,
8726 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8730 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8735 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8737 disableKeyFilter : false,
8740 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8744 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8748 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8750 blankText : "Please complete this mandatory field",
8753 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8757 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8759 maxLength : Number.MAX_VALUE,
8761 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8763 minLengthText : "The minimum length for this field is {0}",
8765 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8767 maxLengthText : "The maximum length for this field is {0}",
8771 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8772 * If available, this function will be called only after the basic validators all return true, and will be passed the
8773 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8777 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8778 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8779 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8783 * @cfg {String} regexText -- Depricated - use Invalid Text
8788 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8794 autocomplete: false,
8813 formatedValue : false,
8814 forceFeedback : false,
8816 indicatorpos : 'left',
8826 parentLabelAlign : function()
8829 while (parent.parent()) {
8830 parent = parent.parent();
8831 if (typeof(parent.labelAlign) !='undefined') {
8832 return parent.labelAlign;
8839 getAutoCreate : function()
8841 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8847 if(this.inputType != 'hidden'){
8848 cfg.cls = 'form-group' //input-group
8854 type : this.inputType,
8856 cls : 'form-control',
8857 placeholder : this.placeholder || '',
8858 autocomplete : this.autocomplete || 'new-password'
8861 if(this.capture.length){
8862 input.capture = this.capture;
8865 if(this.accept.length){
8866 input.accept = this.accept + "/*";
8870 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8873 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8874 input.maxLength = this.maxLength;
8877 if (this.disabled) {
8878 input.disabled=true;
8881 if (this.readOnly) {
8882 input.readonly=true;
8886 input.name = this.name;
8890 input.cls += ' input-' + this.size;
8894 ['xs','sm','md','lg'].map(function(size){
8895 if (settings[size]) {
8896 cfg.cls += ' col-' + size + '-' + settings[size];
8900 var inputblock = input;
8904 cls: 'glyphicon form-control-feedback'
8907 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8910 cls : 'has-feedback',
8918 if (this.before || this.after) {
8921 cls : 'input-group',
8925 if (this.before && typeof(this.before) == 'string') {
8927 inputblock.cn.push({
8929 cls : 'roo-input-before input-group-addon',
8933 if (this.before && typeof(this.before) == 'object') {
8934 this.before = Roo.factory(this.before);
8936 inputblock.cn.push({
8938 cls : 'roo-input-before input-group-' +
8939 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8943 inputblock.cn.push(input);
8945 if (this.after && typeof(this.after) == 'string') {
8946 inputblock.cn.push({
8948 cls : 'roo-input-after input-group-addon',
8952 if (this.after && typeof(this.after) == 'object') {
8953 this.after = Roo.factory(this.after);
8955 inputblock.cn.push({
8957 cls : 'roo-input-after input-group-' +
8958 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8962 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8963 inputblock.cls += ' has-feedback';
8964 inputblock.cn.push(feedback);
8968 if (align ==='left' && this.fieldLabel.length) {
8970 cfg.cls += ' roo-form-group-label-left';
8975 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8976 tooltip : 'This field is required'
8981 cls : 'control-label',
8982 html : this.fieldLabel
8993 var labelCfg = cfg.cn[1];
8994 var contentCfg = cfg.cn[2];
8996 if(this.indicatorpos == 'right'){
9001 cls : 'control-label',
9005 html : this.fieldLabel
9009 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9010 tooltip : 'This field is required'
9023 labelCfg = cfg.cn[0];
9024 contentCfg = cfg.cn[1];
9028 if(this.labelWidth > 12){
9029 labelCfg.style = "width: " + this.labelWidth + 'px';
9032 if(this.labelWidth < 13 && this.labelmd == 0){
9033 this.labelmd = this.labelWidth;
9036 if(this.labellg > 0){
9037 labelCfg.cls += ' col-lg-' + this.labellg;
9038 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9041 if(this.labelmd > 0){
9042 labelCfg.cls += ' col-md-' + this.labelmd;
9043 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9046 if(this.labelsm > 0){
9047 labelCfg.cls += ' col-sm-' + this.labelsm;
9048 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9051 if(this.labelxs > 0){
9052 labelCfg.cls += ' col-xs-' + this.labelxs;
9053 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9057 } else if ( this.fieldLabel.length) {
9062 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9063 tooltip : 'This field is required'
9067 //cls : 'input-group-addon',
9068 html : this.fieldLabel
9076 if(this.indicatorpos == 'right'){
9081 //cls : 'input-group-addon',
9082 html : this.fieldLabel
9087 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9088 tooltip : 'This field is required'
9108 if (this.parentType === 'Navbar' && this.parent().bar) {
9109 cfg.cls += ' navbar-form';
9112 if (this.parentType === 'NavGroup') {
9113 cfg.cls += ' navbar-form';
9121 * return the real input element.
9123 inputEl: function ()
9125 return this.el.select('input.form-control',true).first();
9128 tooltipEl : function()
9130 return this.inputEl();
9133 indicatorEl : function()
9135 var indicator = this.el.select('i.roo-required-indicator',true).first();
9145 setDisabled : function(v)
9147 var i = this.inputEl().dom;
9149 i.removeAttribute('disabled');
9153 i.setAttribute('disabled','true');
9155 initEvents : function()
9158 this.inputEl().on("keydown" , this.fireKey, this);
9159 this.inputEl().on("focus", this.onFocus, this);
9160 this.inputEl().on("blur", this.onBlur, this);
9162 this.inputEl().relayEvent('keyup', this);
9164 this.indicator = this.indicatorEl();
9167 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9170 // reference to original value for reset
9171 this.originalValue = this.getValue();
9172 //Roo.form.TextField.superclass.initEvents.call(this);
9173 if(this.validationEvent == 'keyup'){
9174 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9175 this.inputEl().on('keyup', this.filterValidation, this);
9177 else if(this.validationEvent !== false){
9178 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9181 if(this.selectOnFocus){
9182 this.on("focus", this.preFocus, this);
9185 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9186 this.inputEl().on("keypress", this.filterKeys, this);
9188 this.inputEl().relayEvent('keypress', this);
9191 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9192 this.el.on("click", this.autoSize, this);
9195 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9196 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9199 if (typeof(this.before) == 'object') {
9200 this.before.render(this.el.select('.roo-input-before',true).first());
9202 if (typeof(this.after) == 'object') {
9203 this.after.render(this.el.select('.roo-input-after',true).first());
9206 this.inputEl().on('change', this.onChange, this);
9209 filterValidation : function(e){
9210 if(!e.isNavKeyPress()){
9211 this.validationTask.delay(this.validationDelay);
9215 * Validates the field value
9216 * @return {Boolean} True if the value is valid, else false
9218 validate : function(){
9219 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9220 if(this.disabled || this.validateValue(this.getRawValue())){
9231 * Validates a value according to the field's validation rules and marks the field as invalid
9232 * if the validation fails
9233 * @param {Mixed} value The value to validate
9234 * @return {Boolean} True if the value is valid, else false
9236 validateValue : function(value)
9238 if(this.getVisibilityEl().hasClass('hidden')){
9242 if(value.length < 1) { // if it's blank
9243 if(this.allowBlank){
9249 if(value.length < this.minLength){
9252 if(value.length > this.maxLength){
9256 var vt = Roo.form.VTypes;
9257 if(!vt[this.vtype](value, this)){
9261 if(typeof this.validator == "function"){
9262 var msg = this.validator(value);
9266 if (typeof(msg) == 'string') {
9267 this.invalidText = msg;
9271 if(this.regex && !this.regex.test(value)){
9279 fireKey : function(e){
9280 //Roo.log('field ' + e.getKey());
9281 if(e.isNavKeyPress()){
9282 this.fireEvent("specialkey", this, e);
9285 focus : function (selectText){
9287 this.inputEl().focus();
9288 if(selectText === true){
9289 this.inputEl().dom.select();
9295 onFocus : function(){
9296 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9297 // this.el.addClass(this.focusClass);
9300 this.hasFocus = true;
9301 this.startValue = this.getValue();
9302 this.fireEvent("focus", this);
9306 beforeBlur : Roo.emptyFn,
9310 onBlur : function(){
9312 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9313 //this.el.removeClass(this.focusClass);
9315 this.hasFocus = false;
9316 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9319 var v = this.getValue();
9320 if(String(v) !== String(this.startValue)){
9321 this.fireEvent('change', this, v, this.startValue);
9323 this.fireEvent("blur", this);
9326 onChange : function(e)
9328 var v = this.getValue();
9329 if(String(v) !== String(this.startValue)){
9330 this.fireEvent('change', this, v, this.startValue);
9336 * Resets the current field value to the originally loaded value and clears any validation messages
9339 this.setValue(this.originalValue);
9343 * Returns the name of the field
9344 * @return {Mixed} name The name field
9346 getName: function(){
9350 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9351 * @return {Mixed} value The field value
9353 getValue : function(){
9355 var v = this.inputEl().getValue();
9360 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9361 * @return {Mixed} value The field value
9363 getRawValue : function(){
9364 var v = this.inputEl().getValue();
9370 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9371 * @param {Mixed} value The value to set
9373 setRawValue : function(v){
9374 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9377 selectText : function(start, end){
9378 var v = this.getRawValue();
9380 start = start === undefined ? 0 : start;
9381 end = end === undefined ? v.length : end;
9382 var d = this.inputEl().dom;
9383 if(d.setSelectionRange){
9384 d.setSelectionRange(start, end);
9385 }else if(d.createTextRange){
9386 var range = d.createTextRange();
9387 range.moveStart("character", start);
9388 range.moveEnd("character", v.length-end);
9395 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9396 * @param {Mixed} value The value to set
9398 setValue : function(v){
9401 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9407 processValue : function(value){
9408 if(this.stripCharsRe){
9409 var newValue = value.replace(this.stripCharsRe, '');
9410 if(newValue !== value){
9411 this.setRawValue(newValue);
9418 preFocus : function(){
9420 if(this.selectOnFocus){
9421 this.inputEl().dom.select();
9424 filterKeys : function(e){
9426 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9429 var c = e.getCharCode(), cc = String.fromCharCode(c);
9430 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9433 if(!this.maskRe.test(cc)){
9438 * Clear any invalid styles/messages for this field
9440 clearInvalid : function(){
9442 if(!this.el || this.preventMark){ // not rendered
9447 this.el.removeClass(this.invalidClass);
9449 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9451 var feedback = this.el.select('.form-control-feedback', true).first();
9454 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9460 this.indicator.removeClass('visible');
9461 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9464 this.fireEvent('valid', this);
9468 * Mark this field as valid
9470 markValid : function()
9472 if(!this.el || this.preventMark){ // not rendered...
9476 this.el.removeClass([this.invalidClass, this.validClass]);
9478 var feedback = this.el.select('.form-control-feedback', true).first();
9481 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9485 this.indicator.removeClass('visible');
9486 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9493 if(this.allowBlank && !this.getRawValue().length){
9497 this.el.addClass(this.validClass);
9499 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9501 var feedback = this.el.select('.form-control-feedback', true).first();
9504 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9505 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9510 this.fireEvent('valid', this);
9514 * Mark this field as invalid
9515 * @param {String} msg The validation message
9517 markInvalid : function(msg)
9519 if(!this.el || this.preventMark){ // not rendered
9523 this.el.removeClass([this.invalidClass, this.validClass]);
9525 var feedback = this.el.select('.form-control-feedback', true).first();
9528 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9535 if(this.allowBlank && !this.getRawValue().length){
9540 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9541 this.indicator.addClass('visible');
9544 this.el.addClass(this.invalidClass);
9546 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9548 var feedback = this.el.select('.form-control-feedback', true).first();
9551 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9553 if(this.getValue().length || this.forceFeedback){
9554 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9561 this.fireEvent('invalid', this, msg);
9564 SafariOnKeyDown : function(event)
9566 // this is a workaround for a password hang bug on chrome/ webkit.
9567 if (this.inputEl().dom.type != 'password') {
9571 var isSelectAll = false;
9573 if(this.inputEl().dom.selectionEnd > 0){
9574 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9576 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9577 event.preventDefault();
9582 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9584 event.preventDefault();
9585 // this is very hacky as keydown always get's upper case.
9587 var cc = String.fromCharCode(event.getCharCode());
9588 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9592 adjustWidth : function(tag, w){
9593 tag = tag.toLowerCase();
9594 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9595 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9599 if(tag == 'textarea'){
9602 }else if(Roo.isOpera){
9606 if(tag == 'textarea'){
9614 setFieldLabel : function(v)
9621 var ar = this.el.select('label > span',true);
9623 if (ar.elements.length) {
9624 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9625 this.fieldLabel = v;
9629 var br = this.el.select('label',true);
9631 if(br.elements.length) {
9632 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9633 this.fieldLabel = v;
9637 Roo.log('Cannot Found any of label > span || label in input');
9641 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9642 this.fieldLabel = v;
9657 * @class Roo.bootstrap.TextArea
9658 * @extends Roo.bootstrap.Input
9659 * Bootstrap TextArea class
9660 * @cfg {Number} cols Specifies the visible width of a text area
9661 * @cfg {Number} rows Specifies the visible number of lines in a text area
9662 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9663 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9664 * @cfg {string} html text
9667 * Create a new TextArea
9668 * @param {Object} config The config object
9671 Roo.bootstrap.TextArea = function(config){
9672 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9676 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9686 getAutoCreate : function(){
9688 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9694 if(this.inputType != 'hidden'){
9695 cfg.cls = 'form-group' //input-group
9703 value : this.value || '',
9704 html: this.html || '',
9705 cls : 'form-control',
9706 placeholder : this.placeholder || ''
9710 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9711 input.maxLength = this.maxLength;
9715 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9719 input.cols = this.cols;
9722 if (this.readOnly) {
9723 input.readonly = true;
9727 input.name = this.name;
9731 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9735 ['xs','sm','md','lg'].map(function(size){
9736 if (settings[size]) {
9737 cfg.cls += ' col-' + size + '-' + settings[size];
9741 var inputblock = input;
9743 if(this.hasFeedback && !this.allowBlank){
9747 cls: 'glyphicon form-control-feedback'
9751 cls : 'has-feedback',
9760 if (this.before || this.after) {
9763 cls : 'input-group',
9767 inputblock.cn.push({
9769 cls : 'input-group-addon',
9774 inputblock.cn.push(input);
9776 if(this.hasFeedback && !this.allowBlank){
9777 inputblock.cls += ' has-feedback';
9778 inputblock.cn.push(feedback);
9782 inputblock.cn.push({
9784 cls : 'input-group-addon',
9791 if (align ==='left' && this.fieldLabel.length) {
9796 cls : 'control-label',
9797 html : this.fieldLabel
9808 if(this.labelWidth > 12){
9809 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9812 if(this.labelWidth < 13 && this.labelmd == 0){
9813 this.labelmd = this.labelWidth;
9816 if(this.labellg > 0){
9817 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9818 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9821 if(this.labelmd > 0){
9822 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9823 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9826 if(this.labelsm > 0){
9827 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9828 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9831 if(this.labelxs > 0){
9832 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9833 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9836 } else if ( this.fieldLabel.length) {
9841 //cls : 'input-group-addon',
9842 html : this.fieldLabel
9860 if (this.disabled) {
9861 input.disabled=true;
9868 * return the real textarea element.
9870 inputEl: function ()
9872 return this.el.select('textarea.form-control',true).first();
9876 * Clear any invalid styles/messages for this field
9878 clearInvalid : function()
9881 if(!this.el || this.preventMark){ // not rendered
9885 var label = this.el.select('label', true).first();
9886 var icon = this.el.select('i.fa-star', true).first();
9892 this.el.removeClass(this.invalidClass);
9894 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9896 var feedback = this.el.select('.form-control-feedback', true).first();
9899 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9904 this.fireEvent('valid', this);
9908 * Mark this field as valid
9910 markValid : function()
9912 if(!this.el || this.preventMark){ // not rendered
9916 this.el.removeClass([this.invalidClass, this.validClass]);
9918 var feedback = this.el.select('.form-control-feedback', true).first();
9921 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9924 if(this.disabled || this.allowBlank){
9928 var label = this.el.select('label', true).first();
9929 var icon = this.el.select('i.fa-star', true).first();
9935 this.el.addClass(this.validClass);
9937 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9939 var feedback = this.el.select('.form-control-feedback', true).first();
9942 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9943 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9948 this.fireEvent('valid', this);
9952 * Mark this field as invalid
9953 * @param {String} msg The validation message
9955 markInvalid : function(msg)
9957 if(!this.el || this.preventMark){ // not rendered
9961 this.el.removeClass([this.invalidClass, this.validClass]);
9963 var feedback = this.el.select('.form-control-feedback', true).first();
9966 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9969 if(this.disabled || this.allowBlank){
9973 var label = this.el.select('label', true).first();
9974 var icon = this.el.select('i.fa-star', true).first();
9976 if(!this.getValue().length && label && !icon){
9977 this.el.createChild({
9979 cls : 'text-danger fa fa-lg fa-star',
9980 tooltip : 'This field is required',
9981 style : 'margin-right:5px;'
9985 this.el.addClass(this.invalidClass);
9987 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9989 var feedback = this.el.select('.form-control-feedback', true).first();
9992 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9994 if(this.getValue().length || this.forceFeedback){
9995 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10002 this.fireEvent('invalid', this, msg);
10010 * trigger field - base class for combo..
10015 * @class Roo.bootstrap.TriggerField
10016 * @extends Roo.bootstrap.Input
10017 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10018 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10019 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10020 * for which you can provide a custom implementation. For example:
10022 var trigger = new Roo.bootstrap.TriggerField();
10023 trigger.onTriggerClick = myTriggerFn;
10024 trigger.applyTo('my-field');
10027 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10028 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10029 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10030 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10031 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10034 * Create a new TriggerField.
10035 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10036 * to the base TextField)
10038 Roo.bootstrap.TriggerField = function(config){
10039 this.mimicing = false;
10040 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10043 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10045 * @cfg {String} triggerClass A CSS class to apply to the trigger
10048 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10053 * @cfg {Boolean} removable (true|false) special filter default false
10057 /** @cfg {Boolean} grow @hide */
10058 /** @cfg {Number} growMin @hide */
10059 /** @cfg {Number} growMax @hide */
10065 autoSize: Roo.emptyFn,
10069 deferHeight : true,
10072 actionMode : 'wrap',
10077 getAutoCreate : function(){
10079 var align = this.labelAlign || this.parentLabelAlign();
10084 cls: 'form-group' //input-group
10091 type : this.inputType,
10092 cls : 'form-control',
10093 autocomplete: 'new-password',
10094 placeholder : this.placeholder || ''
10098 input.name = this.name;
10101 input.cls += ' input-' + this.size;
10104 if (this.disabled) {
10105 input.disabled=true;
10108 var inputblock = input;
10110 if(this.hasFeedback && !this.allowBlank){
10114 cls: 'glyphicon form-control-feedback'
10117 if(this.removable && !this.editable && !this.tickable){
10119 cls : 'has-feedback',
10125 cls : 'roo-combo-removable-btn close'
10132 cls : 'has-feedback',
10141 if(this.removable && !this.editable && !this.tickable){
10143 cls : 'roo-removable',
10149 cls : 'roo-combo-removable-btn close'
10156 if (this.before || this.after) {
10159 cls : 'input-group',
10163 inputblock.cn.push({
10165 cls : 'input-group-addon',
10170 inputblock.cn.push(input);
10172 if(this.hasFeedback && !this.allowBlank){
10173 inputblock.cls += ' has-feedback';
10174 inputblock.cn.push(feedback);
10178 inputblock.cn.push({
10180 cls : 'input-group-addon',
10193 cls: 'form-hidden-field'
10207 cls: 'form-hidden-field'
10211 cls: 'roo-select2-choices',
10215 cls: 'roo-select2-search-field',
10228 cls: 'roo-select2-container input-group',
10233 // cls: 'typeahead typeahead-long dropdown-menu',
10234 // style: 'display:none'
10239 if(!this.multiple && this.showToggleBtn){
10245 if (this.caret != false) {
10248 cls: 'fa fa-' + this.caret
10255 cls : 'input-group-addon btn dropdown-toggle',
10260 cls: 'combobox-clear',
10274 combobox.cls += ' roo-select2-container-multi';
10277 if (align ==='left' && this.fieldLabel.length) {
10279 cfg.cls += ' roo-form-group-label-left';
10284 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10285 tooltip : 'This field is required'
10290 cls : 'control-label',
10291 html : this.fieldLabel
10303 var labelCfg = cfg.cn[1];
10304 var contentCfg = cfg.cn[2];
10306 if(this.indicatorpos == 'right'){
10311 cls : 'control-label',
10315 html : this.fieldLabel
10319 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10320 tooltip : 'This field is required'
10333 labelCfg = cfg.cn[0];
10334 contentCfg = cfg.cn[1];
10337 if(this.labelWidth > 12){
10338 labelCfg.style = "width: " + this.labelWidth + 'px';
10341 if(this.labelWidth < 13 && this.labelmd == 0){
10342 this.labelmd = this.labelWidth;
10345 if(this.labellg > 0){
10346 labelCfg.cls += ' col-lg-' + this.labellg;
10347 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10350 if(this.labelmd > 0){
10351 labelCfg.cls += ' col-md-' + this.labelmd;
10352 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10355 if(this.labelsm > 0){
10356 labelCfg.cls += ' col-sm-' + this.labelsm;
10357 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10360 if(this.labelxs > 0){
10361 labelCfg.cls += ' col-xs-' + this.labelxs;
10362 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10365 } else if ( this.fieldLabel.length) {
10366 // Roo.log(" label");
10370 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10371 tooltip : 'This field is required'
10375 //cls : 'input-group-addon',
10376 html : this.fieldLabel
10384 if(this.indicatorpos == 'right'){
10392 html : this.fieldLabel
10396 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10397 tooltip : 'This field is required'
10410 // Roo.log(" no label && no align");
10417 ['xs','sm','md','lg'].map(function(size){
10418 if (settings[size]) {
10419 cfg.cls += ' col-' + size + '-' + settings[size];
10430 onResize : function(w, h){
10431 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10432 // if(typeof w == 'number'){
10433 // var x = w - this.trigger.getWidth();
10434 // this.inputEl().setWidth(this.adjustWidth('input', x));
10435 // this.trigger.setStyle('left', x+'px');
10440 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10443 getResizeEl : function(){
10444 return this.inputEl();
10448 getPositionEl : function(){
10449 return this.inputEl();
10453 alignErrorIcon : function(){
10454 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10458 initEvents : function(){
10462 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10463 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10464 if(!this.multiple && this.showToggleBtn){
10465 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10466 if(this.hideTrigger){
10467 this.trigger.setDisplayed(false);
10469 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10473 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10476 if(this.removable && !this.editable && !this.tickable){
10477 var close = this.closeTriggerEl();
10480 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10481 close.on('click', this.removeBtnClick, this, close);
10485 //this.trigger.addClassOnOver('x-form-trigger-over');
10486 //this.trigger.addClassOnClick('x-form-trigger-click');
10489 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10493 closeTriggerEl : function()
10495 var close = this.el.select('.roo-combo-removable-btn', true).first();
10496 return close ? close : false;
10499 removeBtnClick : function(e, h, el)
10501 e.preventDefault();
10503 if(this.fireEvent("remove", this) !== false){
10505 this.fireEvent("afterremove", this)
10509 createList : function()
10511 this.list = Roo.get(document.body).createChild({
10513 cls: 'typeahead typeahead-long dropdown-menu',
10514 style: 'display:none'
10517 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10522 initTrigger : function(){
10527 onDestroy : function(){
10529 this.trigger.removeAllListeners();
10530 // this.trigger.remove();
10533 // this.wrap.remove();
10535 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10539 onFocus : function(){
10540 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10542 if(!this.mimicing){
10543 this.wrap.addClass('x-trigger-wrap-focus');
10544 this.mimicing = true;
10545 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10546 if(this.monitorTab){
10547 this.el.on("keydown", this.checkTab, this);
10554 checkTab : function(e){
10555 if(e.getKey() == e.TAB){
10556 this.triggerBlur();
10561 onBlur : function(){
10566 mimicBlur : function(e, t){
10568 if(!this.wrap.contains(t) && this.validateBlur()){
10569 this.triggerBlur();
10575 triggerBlur : function(){
10576 this.mimicing = false;
10577 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10578 if(this.monitorTab){
10579 this.el.un("keydown", this.checkTab, this);
10581 //this.wrap.removeClass('x-trigger-wrap-focus');
10582 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10586 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10587 validateBlur : function(e, t){
10592 onDisable : function(){
10593 this.inputEl().dom.disabled = true;
10594 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10596 // this.wrap.addClass('x-item-disabled');
10601 onEnable : function(){
10602 this.inputEl().dom.disabled = false;
10603 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10605 // this.el.removeClass('x-item-disabled');
10610 onShow : function(){
10611 var ae = this.getActionEl();
10614 ae.dom.style.display = '';
10615 ae.dom.style.visibility = 'visible';
10621 onHide : function(){
10622 var ae = this.getActionEl();
10623 ae.dom.style.display = 'none';
10627 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10628 * by an implementing function.
10630 * @param {EventObject} e
10632 onTriggerClick : Roo.emptyFn
10636 * Ext JS Library 1.1.1
10637 * Copyright(c) 2006-2007, Ext JS, LLC.
10639 * Originally Released Under LGPL - original licence link has changed is not relivant.
10642 * <script type="text/javascript">
10647 * @class Roo.data.SortTypes
10649 * Defines the default sorting (casting?) comparison functions used when sorting data.
10651 Roo.data.SortTypes = {
10653 * Default sort that does nothing
10654 * @param {Mixed} s The value being converted
10655 * @return {Mixed} The comparison value
10657 none : function(s){
10662 * The regular expression used to strip tags
10666 stripTagsRE : /<\/?[^>]+>/gi,
10669 * Strips all HTML tags to sort on text only
10670 * @param {Mixed} s The value being converted
10671 * @return {String} The comparison value
10673 asText : function(s){
10674 return String(s).replace(this.stripTagsRE, "");
10678 * Strips all HTML tags to sort on text only - Case insensitive
10679 * @param {Mixed} s The value being converted
10680 * @return {String} The comparison value
10682 asUCText : function(s){
10683 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10687 * Case insensitive string
10688 * @param {Mixed} s The value being converted
10689 * @return {String} The comparison value
10691 asUCString : function(s) {
10692 return String(s).toUpperCase();
10697 * @param {Mixed} s The value being converted
10698 * @return {Number} The comparison value
10700 asDate : function(s) {
10704 if(s instanceof Date){
10705 return s.getTime();
10707 return Date.parse(String(s));
10712 * @param {Mixed} s The value being converted
10713 * @return {Float} The comparison value
10715 asFloat : function(s) {
10716 var val = parseFloat(String(s).replace(/,/g, ""));
10725 * @param {Mixed} s The value being converted
10726 * @return {Number} The comparison value
10728 asInt : function(s) {
10729 var val = parseInt(String(s).replace(/,/g, ""));
10737 * Ext JS Library 1.1.1
10738 * Copyright(c) 2006-2007, Ext JS, LLC.
10740 * Originally Released Under LGPL - original licence link has changed is not relivant.
10743 * <script type="text/javascript">
10747 * @class Roo.data.Record
10748 * Instances of this class encapsulate both record <em>definition</em> information, and record
10749 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10750 * to access Records cached in an {@link Roo.data.Store} object.<br>
10752 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10753 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10756 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10758 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10759 * {@link #create}. The parameters are the same.
10760 * @param {Array} data An associative Array of data values keyed by the field name.
10761 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10762 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10763 * not specified an integer id is generated.
10765 Roo.data.Record = function(data, id){
10766 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10771 * Generate a constructor for a specific record layout.
10772 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10773 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10774 * Each field definition object may contain the following properties: <ul>
10775 * <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,
10776 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10777 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10778 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10779 * is being used, then this is a string containing the javascript expression to reference the data relative to
10780 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10781 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10782 * this may be omitted.</p></li>
10783 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10784 * <ul><li>auto (Default, implies no conversion)</li>
10789 * <li>date</li></ul></p></li>
10790 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10791 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10792 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10793 * by the Reader into an object that will be stored in the Record. It is passed the
10794 * following parameters:<ul>
10795 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10797 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10799 * <br>usage:<br><pre><code>
10800 var TopicRecord = Roo.data.Record.create(
10801 {name: 'title', mapping: 'topic_title'},
10802 {name: 'author', mapping: 'username'},
10803 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10804 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10805 {name: 'lastPoster', mapping: 'user2'},
10806 {name: 'excerpt', mapping: 'post_text'}
10809 var myNewRecord = new TopicRecord({
10810 title: 'Do my job please',
10813 lastPost: new Date(),
10814 lastPoster: 'Animal',
10815 excerpt: 'No way dude!'
10817 myStore.add(myNewRecord);
10822 Roo.data.Record.create = function(o){
10823 var f = function(){
10824 f.superclass.constructor.apply(this, arguments);
10826 Roo.extend(f, Roo.data.Record);
10827 var p = f.prototype;
10828 p.fields = new Roo.util.MixedCollection(false, function(field){
10831 for(var i = 0, len = o.length; i < len; i++){
10832 p.fields.add(new Roo.data.Field(o[i]));
10834 f.getField = function(name){
10835 return p.fields.get(name);
10840 Roo.data.Record.AUTO_ID = 1000;
10841 Roo.data.Record.EDIT = 'edit';
10842 Roo.data.Record.REJECT = 'reject';
10843 Roo.data.Record.COMMIT = 'commit';
10845 Roo.data.Record.prototype = {
10847 * Readonly flag - true if this record has been modified.
10856 join : function(store){
10857 this.store = store;
10861 * Set the named field to the specified value.
10862 * @param {String} name The name of the field to set.
10863 * @param {Object} value The value to set the field to.
10865 set : function(name, value){
10866 if(this.data[name] == value){
10870 if(!this.modified){
10871 this.modified = {};
10873 if(typeof this.modified[name] == 'undefined'){
10874 this.modified[name] = this.data[name];
10876 this.data[name] = value;
10877 if(!this.editing && this.store){
10878 this.store.afterEdit(this);
10883 * Get the value of the named field.
10884 * @param {String} name The name of the field to get the value of.
10885 * @return {Object} The value of the field.
10887 get : function(name){
10888 return this.data[name];
10892 beginEdit : function(){
10893 this.editing = true;
10894 this.modified = {};
10898 cancelEdit : function(){
10899 this.editing = false;
10900 delete this.modified;
10904 endEdit : function(){
10905 this.editing = false;
10906 if(this.dirty && this.store){
10907 this.store.afterEdit(this);
10912 * Usually called by the {@link Roo.data.Store} which owns the Record.
10913 * Rejects all changes made to the Record since either creation, or the last commit operation.
10914 * Modified fields are reverted to their original values.
10916 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10917 * of reject operations.
10919 reject : function(){
10920 var m = this.modified;
10922 if(typeof m[n] != "function"){
10923 this.data[n] = m[n];
10926 this.dirty = false;
10927 delete this.modified;
10928 this.editing = false;
10930 this.store.afterReject(this);
10935 * Usually called by the {@link Roo.data.Store} which owns the Record.
10936 * Commits all changes made to the Record since either creation, or the last commit operation.
10938 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10939 * of commit operations.
10941 commit : function(){
10942 this.dirty = false;
10943 delete this.modified;
10944 this.editing = false;
10946 this.store.afterCommit(this);
10951 hasError : function(){
10952 return this.error != null;
10956 clearError : function(){
10961 * Creates a copy of this record.
10962 * @param {String} id (optional) A new record id if you don't want to use this record's id
10965 copy : function(newId) {
10966 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10970 * Ext JS Library 1.1.1
10971 * Copyright(c) 2006-2007, Ext JS, LLC.
10973 * Originally Released Under LGPL - original licence link has changed is not relivant.
10976 * <script type="text/javascript">
10982 * @class Roo.data.Store
10983 * @extends Roo.util.Observable
10984 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10985 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10987 * 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
10988 * has no knowledge of the format of the data returned by the Proxy.<br>
10990 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10991 * instances from the data object. These records are cached and made available through accessor functions.
10993 * Creates a new Store.
10994 * @param {Object} config A config object containing the objects needed for the Store to access data,
10995 * and read the data into Records.
10997 Roo.data.Store = function(config){
10998 this.data = new Roo.util.MixedCollection(false);
10999 this.data.getKey = function(o){
11002 this.baseParams = {};
11004 this.paramNames = {
11009 "multisort" : "_multisort"
11012 if(config && config.data){
11013 this.inlineData = config.data;
11014 delete config.data;
11017 Roo.apply(this, config);
11019 if(this.reader){ // reader passed
11020 this.reader = Roo.factory(this.reader, Roo.data);
11021 this.reader.xmodule = this.xmodule || false;
11022 if(!this.recordType){
11023 this.recordType = this.reader.recordType;
11025 if(this.reader.onMetaChange){
11026 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11030 if(this.recordType){
11031 this.fields = this.recordType.prototype.fields;
11033 this.modified = [];
11037 * @event datachanged
11038 * Fires when the data cache has changed, and a widget which is using this Store
11039 * as a Record cache should refresh its view.
11040 * @param {Store} this
11042 datachanged : true,
11044 * @event metachange
11045 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11046 * @param {Store} this
11047 * @param {Object} meta The JSON metadata
11052 * Fires when Records have been added to the Store
11053 * @param {Store} this
11054 * @param {Roo.data.Record[]} records The array of Records added
11055 * @param {Number} index The index at which the record(s) were added
11060 * Fires when a Record has been removed from the Store
11061 * @param {Store} this
11062 * @param {Roo.data.Record} record The Record that was removed
11063 * @param {Number} index The index at which the record was removed
11068 * Fires when a Record has been updated
11069 * @param {Store} this
11070 * @param {Roo.data.Record} record The Record that was updated
11071 * @param {String} operation The update operation being performed. Value may be one of:
11073 Roo.data.Record.EDIT
11074 Roo.data.Record.REJECT
11075 Roo.data.Record.COMMIT
11081 * Fires when the data cache has been cleared.
11082 * @param {Store} this
11086 * @event beforeload
11087 * Fires before a request is made for a new data object. If the beforeload handler returns false
11088 * the load action will be canceled.
11089 * @param {Store} this
11090 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11094 * @event beforeloadadd
11095 * Fires after a new set of Records has been loaded.
11096 * @param {Store} this
11097 * @param {Roo.data.Record[]} records The Records that were loaded
11098 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11100 beforeloadadd : true,
11103 * Fires after a new set of Records has been loaded, before they are added to the store.
11104 * @param {Store} this
11105 * @param {Roo.data.Record[]} records The Records that were loaded
11106 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11107 * @params {Object} return from reader
11111 * @event loadexception
11112 * Fires if an exception occurs in the Proxy during loading.
11113 * Called with the signature of the Proxy's "loadexception" event.
11114 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11117 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11118 * @param {Object} load options
11119 * @param {Object} jsonData from your request (normally this contains the Exception)
11121 loadexception : true
11125 this.proxy = Roo.factory(this.proxy, Roo.data);
11126 this.proxy.xmodule = this.xmodule || false;
11127 this.relayEvents(this.proxy, ["loadexception"]);
11129 this.sortToggle = {};
11130 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11132 Roo.data.Store.superclass.constructor.call(this);
11134 if(this.inlineData){
11135 this.loadData(this.inlineData);
11136 delete this.inlineData;
11140 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11142 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11143 * without a remote query - used by combo/forms at present.
11147 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11150 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11153 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11154 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11157 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11158 * on any HTTP request
11161 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11164 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11168 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11169 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11171 remoteSort : false,
11174 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11175 * loaded or when a record is removed. (defaults to false).
11177 pruneModifiedRecords : false,
11180 lastOptions : null,
11183 * Add Records to the Store and fires the add event.
11184 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11186 add : function(records){
11187 records = [].concat(records);
11188 for(var i = 0, len = records.length; i < len; i++){
11189 records[i].join(this);
11191 var index = this.data.length;
11192 this.data.addAll(records);
11193 this.fireEvent("add", this, records, index);
11197 * Remove a Record from the Store and fires the remove event.
11198 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11200 remove : function(record){
11201 var index = this.data.indexOf(record);
11202 this.data.removeAt(index);
11204 if(this.pruneModifiedRecords){
11205 this.modified.remove(record);
11207 this.fireEvent("remove", this, record, index);
11211 * Remove all Records from the Store and fires the clear event.
11213 removeAll : function(){
11215 if(this.pruneModifiedRecords){
11216 this.modified = [];
11218 this.fireEvent("clear", this);
11222 * Inserts Records to the Store at the given index and fires the add event.
11223 * @param {Number} index The start index at which to insert the passed Records.
11224 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11226 insert : function(index, records){
11227 records = [].concat(records);
11228 for(var i = 0, len = records.length; i < len; i++){
11229 this.data.insert(index, records[i]);
11230 records[i].join(this);
11232 this.fireEvent("add", this, records, index);
11236 * Get the index within the cache of the passed Record.
11237 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11238 * @return {Number} The index of the passed Record. Returns -1 if not found.
11240 indexOf : function(record){
11241 return this.data.indexOf(record);
11245 * Get the index within the cache of the Record with the passed id.
11246 * @param {String} id The id of the Record to find.
11247 * @return {Number} The index of the Record. Returns -1 if not found.
11249 indexOfId : function(id){
11250 return this.data.indexOfKey(id);
11254 * Get the Record with the specified id.
11255 * @param {String} id The id of the Record to find.
11256 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11258 getById : function(id){
11259 return this.data.key(id);
11263 * Get the Record at the specified index.
11264 * @param {Number} index The index of the Record to find.
11265 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11267 getAt : function(index){
11268 return this.data.itemAt(index);
11272 * Returns a range of Records between specified indices.
11273 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11274 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11275 * @return {Roo.data.Record[]} An array of Records
11277 getRange : function(start, end){
11278 return this.data.getRange(start, end);
11282 storeOptions : function(o){
11283 o = Roo.apply({}, o);
11286 this.lastOptions = o;
11290 * Loads the Record cache from the configured Proxy using the configured Reader.
11292 * If using remote paging, then the first load call must specify the <em>start</em>
11293 * and <em>limit</em> properties in the options.params property to establish the initial
11294 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11296 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11297 * and this call will return before the new data has been loaded. Perform any post-processing
11298 * in a callback function, or in a "load" event handler.</strong>
11300 * @param {Object} options An object containing properties which control loading options:<ul>
11301 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11302 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11303 * passed the following arguments:<ul>
11304 * <li>r : Roo.data.Record[]</li>
11305 * <li>options: Options object from the load call</li>
11306 * <li>success: Boolean success indicator</li></ul></li>
11307 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11308 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11311 load : function(options){
11312 options = options || {};
11313 if(this.fireEvent("beforeload", this, options) !== false){
11314 this.storeOptions(options);
11315 var p = Roo.apply(options.params || {}, this.baseParams);
11316 // if meta was not loaded from remote source.. try requesting it.
11317 if (!this.reader.metaFromRemote) {
11318 p._requestMeta = 1;
11320 if(this.sortInfo && this.remoteSort){
11321 var pn = this.paramNames;
11322 p[pn["sort"]] = this.sortInfo.field;
11323 p[pn["dir"]] = this.sortInfo.direction;
11325 if (this.multiSort) {
11326 var pn = this.paramNames;
11327 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11330 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11335 * Reloads the Record cache from the configured Proxy using the configured Reader and
11336 * the options from the last load operation performed.
11337 * @param {Object} options (optional) An object containing properties which may override the options
11338 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11339 * the most recently used options are reused).
11341 reload : function(options){
11342 this.load(Roo.applyIf(options||{}, this.lastOptions));
11346 // Called as a callback by the Reader during a load operation.
11347 loadRecords : function(o, options, success){
11348 if(!o || success === false){
11349 if(success !== false){
11350 this.fireEvent("load", this, [], options, o);
11352 if(options.callback){
11353 options.callback.call(options.scope || this, [], options, false);
11357 // if data returned failure - throw an exception.
11358 if (o.success === false) {
11359 // show a message if no listener is registered.
11360 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11361 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11363 // loadmask wil be hooked into this..
11364 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11367 var r = o.records, t = o.totalRecords || r.length;
11369 this.fireEvent("beforeloadadd", this, r, options, o);
11371 if(!options || options.add !== true){
11372 if(this.pruneModifiedRecords){
11373 this.modified = [];
11375 for(var i = 0, len = r.length; i < len; i++){
11379 this.data = this.snapshot;
11380 delete this.snapshot;
11383 this.data.addAll(r);
11384 this.totalLength = t;
11386 this.fireEvent("datachanged", this);
11388 this.totalLength = Math.max(t, this.data.length+r.length);
11392 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11394 var e = new Roo.data.Record({});
11396 e.set(this.parent.displayField, this.parent.emptyTitle);
11397 e.set(this.parent.valueField, '');
11402 this.fireEvent("load", this, r, options, o);
11403 if(options.callback){
11404 options.callback.call(options.scope || this, r, options, true);
11410 * Loads data from a passed data block. A Reader which understands the format of the data
11411 * must have been configured in the constructor.
11412 * @param {Object} data The data block from which to read the Records. The format of the data expected
11413 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11414 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11416 loadData : function(o, append){
11417 var r = this.reader.readRecords(o);
11418 this.loadRecords(r, {add: append}, true);
11422 * Gets the number of cached records.
11424 * <em>If using paging, this may not be the total size of the dataset. If the data object
11425 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11426 * the data set size</em>
11428 getCount : function(){
11429 return this.data.length || 0;
11433 * Gets the total number of records in the dataset as returned by the server.
11435 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11436 * the dataset size</em>
11438 getTotalCount : function(){
11439 return this.totalLength || 0;
11443 * Returns the sort state of the Store as an object with two properties:
11445 field {String} The name of the field by which the Records are sorted
11446 direction {String} The sort order, "ASC" or "DESC"
11449 getSortState : function(){
11450 return this.sortInfo;
11454 applySort : function(){
11455 if(this.sortInfo && !this.remoteSort){
11456 var s = this.sortInfo, f = s.field;
11457 var st = this.fields.get(f).sortType;
11458 var fn = function(r1, r2){
11459 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11460 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11462 this.data.sort(s.direction, fn);
11463 if(this.snapshot && this.snapshot != this.data){
11464 this.snapshot.sort(s.direction, fn);
11470 * Sets the default sort column and order to be used by the next load operation.
11471 * @param {String} fieldName The name of the field to sort by.
11472 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11474 setDefaultSort : function(field, dir){
11475 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11479 * Sort the Records.
11480 * If remote sorting is used, the sort is performed on the server, and the cache is
11481 * reloaded. If local sorting is used, the cache is sorted internally.
11482 * @param {String} fieldName The name of the field to sort by.
11483 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11485 sort : function(fieldName, dir){
11486 var f = this.fields.get(fieldName);
11488 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11490 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11491 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11496 this.sortToggle[f.name] = dir;
11497 this.sortInfo = {field: f.name, direction: dir};
11498 if(!this.remoteSort){
11500 this.fireEvent("datachanged", this);
11502 this.load(this.lastOptions);
11507 * Calls the specified function for each of the Records in the cache.
11508 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11509 * Returning <em>false</em> aborts and exits the iteration.
11510 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11512 each : function(fn, scope){
11513 this.data.each(fn, scope);
11517 * Gets all records modified since the last commit. Modified records are persisted across load operations
11518 * (e.g., during paging).
11519 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11521 getModifiedRecords : function(){
11522 return this.modified;
11526 createFilterFn : function(property, value, anyMatch){
11527 if(!value.exec){ // not a regex
11528 value = String(value);
11529 if(value.length == 0){
11532 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11534 return function(r){
11535 return value.test(r.data[property]);
11540 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11541 * @param {String} property A field on your records
11542 * @param {Number} start The record index to start at (defaults to 0)
11543 * @param {Number} end The last record index to include (defaults to length - 1)
11544 * @return {Number} The sum
11546 sum : function(property, start, end){
11547 var rs = this.data.items, v = 0;
11548 start = start || 0;
11549 end = (end || end === 0) ? end : rs.length-1;
11551 for(var i = start; i <= end; i++){
11552 v += (rs[i].data[property] || 0);
11558 * Filter 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
11564 filter : function(property, value, anyMatch){
11565 var fn = this.createFilterFn(property, value, anyMatch);
11566 return fn ? this.filterBy(fn) : this.clearFilter();
11570 * Filter by a function. The specified function will be called with each
11571 * record in this data source. If the function returns true the record is included,
11572 * otherwise it is filtered.
11573 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11574 * @param {Object} scope (optional) The scope of the function (defaults to this)
11576 filterBy : function(fn, scope){
11577 this.snapshot = this.snapshot || this.data;
11578 this.data = this.queryBy(fn, scope||this);
11579 this.fireEvent("datachanged", this);
11583 * Query the records by a specified property.
11584 * @param {String} field A field on your records
11585 * @param {String/RegExp} value Either a string that the field
11586 * should start with or a RegExp to test against the field
11587 * @param {Boolean} anyMatch True to match any part not just the beginning
11588 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11590 query : function(property, value, anyMatch){
11591 var fn = this.createFilterFn(property, value, anyMatch);
11592 return fn ? this.queryBy(fn) : this.data.clone();
11596 * Query by a function. The specified function will be called with each
11597 * record in this data source. If the function returns true the record is included
11599 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11600 * @param {Object} scope (optional) The scope of the function (defaults to this)
11601 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11603 queryBy : function(fn, scope){
11604 var data = this.snapshot || this.data;
11605 return data.filterBy(fn, scope||this);
11609 * Collects unique values for a particular dataIndex from this store.
11610 * @param {String} dataIndex The property to collect
11611 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11612 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11613 * @return {Array} An array of the unique values
11615 collect : function(dataIndex, allowNull, bypassFilter){
11616 var d = (bypassFilter === true && this.snapshot) ?
11617 this.snapshot.items : this.data.items;
11618 var v, sv, r = [], l = {};
11619 for(var i = 0, len = d.length; i < len; i++){
11620 v = d[i].data[dataIndex];
11622 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11631 * Revert to a view of the Record cache with no filtering applied.
11632 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11634 clearFilter : function(suppressEvent){
11635 if(this.snapshot && this.snapshot != this.data){
11636 this.data = this.snapshot;
11637 delete this.snapshot;
11638 if(suppressEvent !== true){
11639 this.fireEvent("datachanged", this);
11645 afterEdit : function(record){
11646 if(this.modified.indexOf(record) == -1){
11647 this.modified.push(record);
11649 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11653 afterReject : function(record){
11654 this.modified.remove(record);
11655 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11659 afterCommit : function(record){
11660 this.modified.remove(record);
11661 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11665 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11666 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11668 commitChanges : function(){
11669 var m = this.modified.slice(0);
11670 this.modified = [];
11671 for(var i = 0, len = m.length; i < len; i++){
11677 * Cancel outstanding changes on all changed records.
11679 rejectChanges : function(){
11680 var m = this.modified.slice(0);
11681 this.modified = [];
11682 for(var i = 0, len = m.length; i < len; i++){
11687 onMetaChange : function(meta, rtype, o){
11688 this.recordType = rtype;
11689 this.fields = rtype.prototype.fields;
11690 delete this.snapshot;
11691 this.sortInfo = meta.sortInfo || this.sortInfo;
11692 this.modified = [];
11693 this.fireEvent('metachange', this, this.reader.meta);
11696 moveIndex : function(data, type)
11698 var index = this.indexOf(data);
11700 var newIndex = index + type;
11704 this.insert(newIndex, data);
11709 * Ext JS Library 1.1.1
11710 * Copyright(c) 2006-2007, Ext JS, LLC.
11712 * Originally Released Under LGPL - original licence link has changed is not relivant.
11715 * <script type="text/javascript">
11719 * @class Roo.data.SimpleStore
11720 * @extends Roo.data.Store
11721 * Small helper class to make creating Stores from Array data easier.
11722 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11723 * @cfg {Array} fields An array of field definition objects, or field name strings.
11724 * @cfg {Array} data The multi-dimensional array of data
11726 * @param {Object} config
11728 Roo.data.SimpleStore = function(config){
11729 Roo.data.SimpleStore.superclass.constructor.call(this, {
11731 reader: new Roo.data.ArrayReader({
11734 Roo.data.Record.create(config.fields)
11736 proxy : new Roo.data.MemoryProxy(config.data)
11740 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11742 * Ext JS Library 1.1.1
11743 * Copyright(c) 2006-2007, Ext JS, LLC.
11745 * Originally Released Under LGPL - original licence link has changed is not relivant.
11748 * <script type="text/javascript">
11753 * @extends Roo.data.Store
11754 * @class Roo.data.JsonStore
11755 * Small helper class to make creating Stores for JSON data easier. <br/>
11757 var store = new Roo.data.JsonStore({
11758 url: 'get-images.php',
11760 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11763 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11764 * JsonReader and HttpProxy (unless inline data is provided).</b>
11765 * @cfg {Array} fields An array of field definition objects, or field name strings.
11767 * @param {Object} config
11769 Roo.data.JsonStore = function(c){
11770 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11771 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11772 reader: new Roo.data.JsonReader(c, c.fields)
11775 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11777 * Ext JS Library 1.1.1
11778 * Copyright(c) 2006-2007, Ext JS, LLC.
11780 * Originally Released Under LGPL - original licence link has changed is not relivant.
11783 * <script type="text/javascript">
11787 Roo.data.Field = function(config){
11788 if(typeof config == "string"){
11789 config = {name: config};
11791 Roo.apply(this, config);
11794 this.type = "auto";
11797 var st = Roo.data.SortTypes;
11798 // named sortTypes are supported, here we look them up
11799 if(typeof this.sortType == "string"){
11800 this.sortType = st[this.sortType];
11803 // set default sortType for strings and dates
11804 if(!this.sortType){
11807 this.sortType = st.asUCString;
11810 this.sortType = st.asDate;
11813 this.sortType = st.none;
11818 var stripRe = /[\$,%]/g;
11820 // prebuilt conversion function for this field, instead of
11821 // switching every time we're reading a value
11823 var cv, dateFormat = this.dateFormat;
11828 cv = function(v){ return v; };
11831 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11835 return v !== undefined && v !== null && v !== '' ?
11836 parseInt(String(v).replace(stripRe, ""), 10) : '';
11841 return v !== undefined && v !== null && v !== '' ?
11842 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11847 cv = function(v){ return v === true || v === "true" || v == 1; };
11854 if(v instanceof Date){
11858 if(dateFormat == "timestamp"){
11859 return new Date(v*1000);
11861 return Date.parseDate(v, dateFormat);
11863 var parsed = Date.parse(v);
11864 return parsed ? new Date(parsed) : null;
11873 Roo.data.Field.prototype = {
11881 * Ext JS Library 1.1.1
11882 * Copyright(c) 2006-2007, Ext JS, LLC.
11884 * Originally Released Under LGPL - original licence link has changed is not relivant.
11887 * <script type="text/javascript">
11890 // Base class for reading structured data from a data source. This class is intended to be
11891 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11894 * @class Roo.data.DataReader
11895 * Base class for reading structured data from a data source. This class is intended to be
11896 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11899 Roo.data.DataReader = function(meta, recordType){
11903 this.recordType = recordType instanceof Array ?
11904 Roo.data.Record.create(recordType) : recordType;
11907 Roo.data.DataReader.prototype = {
11909 * Create an empty record
11910 * @param {Object} data (optional) - overlay some values
11911 * @return {Roo.data.Record} record created.
11913 newRow : function(d) {
11915 this.recordType.prototype.fields.each(function(c) {
11917 case 'int' : da[c.name] = 0; break;
11918 case 'date' : da[c.name] = new Date(); break;
11919 case 'float' : da[c.name] = 0.0; break;
11920 case 'boolean' : da[c.name] = false; break;
11921 default : da[c.name] = ""; break;
11925 return new this.recordType(Roo.apply(da, d));
11930 * Ext JS Library 1.1.1
11931 * Copyright(c) 2006-2007, Ext JS, LLC.
11933 * Originally Released Under LGPL - original licence link has changed is not relivant.
11936 * <script type="text/javascript">
11940 * @class Roo.data.DataProxy
11941 * @extends Roo.data.Observable
11942 * This class is an abstract base class for implementations which provide retrieval of
11943 * unformatted data objects.<br>
11945 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11946 * (of the appropriate type which knows how to parse the data object) to provide a block of
11947 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11949 * Custom implementations must implement the load method as described in
11950 * {@link Roo.data.HttpProxy#load}.
11952 Roo.data.DataProxy = function(){
11955 * @event beforeload
11956 * Fires before a network request is made to retrieve a data object.
11957 * @param {Object} This DataProxy object.
11958 * @param {Object} params The params parameter to the load function.
11963 * Fires before the load method's callback is called.
11964 * @param {Object} This DataProxy object.
11965 * @param {Object} o The data object.
11966 * @param {Object} arg The callback argument object passed to the load function.
11970 * @event loadexception
11971 * Fires if an Exception occurs during data retrieval.
11972 * @param {Object} This DataProxy object.
11973 * @param {Object} o The data object.
11974 * @param {Object} arg The callback argument object passed to the load function.
11975 * @param {Object} e The Exception.
11977 loadexception : true
11979 Roo.data.DataProxy.superclass.constructor.call(this);
11982 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11985 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11989 * Ext JS Library 1.1.1
11990 * Copyright(c) 2006-2007, Ext JS, LLC.
11992 * Originally Released Under LGPL - original licence link has changed is not relivant.
11995 * <script type="text/javascript">
11998 * @class Roo.data.MemoryProxy
11999 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12000 * to the Reader when its load method is called.
12002 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12004 Roo.data.MemoryProxy = function(data){
12008 Roo.data.MemoryProxy.superclass.constructor.call(this);
12012 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12015 * Load data from the requested source (in this case an in-memory
12016 * data object passed to the constructor), read the data object into
12017 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12018 * process that block using the passed callback.
12019 * @param {Object} params This parameter is not used by the MemoryProxy class.
12020 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12021 * object into a block of Roo.data.Records.
12022 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12023 * The function must be passed <ul>
12024 * <li>The Record block object</li>
12025 * <li>The "arg" argument from the load function</li>
12026 * <li>A boolean success indicator</li>
12028 * @param {Object} scope The scope in which to call the callback
12029 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12031 load : function(params, reader, callback, scope, arg){
12032 params = params || {};
12035 result = reader.readRecords(this.data);
12037 this.fireEvent("loadexception", this, arg, null, e);
12038 callback.call(scope, null, arg, false);
12041 callback.call(scope, result, arg, true);
12045 update : function(params, records){
12050 * Ext JS Library 1.1.1
12051 * Copyright(c) 2006-2007, Ext JS, LLC.
12053 * Originally Released Under LGPL - original licence link has changed is not relivant.
12056 * <script type="text/javascript">
12059 * @class Roo.data.HttpProxy
12060 * @extends Roo.data.DataProxy
12061 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12062 * configured to reference a certain URL.<br><br>
12064 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12065 * from which the running page was served.<br><br>
12067 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12069 * Be aware that to enable the browser to parse an XML document, the server must set
12070 * the Content-Type header in the HTTP response to "text/xml".
12072 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12073 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12074 * will be used to make the request.
12076 Roo.data.HttpProxy = function(conn){
12077 Roo.data.HttpProxy.superclass.constructor.call(this);
12078 // is conn a conn config or a real conn?
12080 this.useAjax = !conn || !conn.events;
12084 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12085 // thse are take from connection...
12088 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12091 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12092 * extra parameters to each request made by this object. (defaults to undefined)
12095 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12096 * to each request made by this object. (defaults to undefined)
12099 * @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)
12102 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12105 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12111 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12115 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12116 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12117 * a finer-grained basis than the DataProxy events.
12119 getConnection : function(){
12120 return this.useAjax ? Roo.Ajax : this.conn;
12124 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12125 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12126 * process that block using the passed callback.
12127 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12128 * for the request to the remote server.
12129 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12130 * object into a block of Roo.data.Records.
12131 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12132 * The function must be passed <ul>
12133 * <li>The Record block object</li>
12134 * <li>The "arg" argument from the load function</li>
12135 * <li>A boolean success indicator</li>
12137 * @param {Object} scope The scope in which to call the callback
12138 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12140 load : function(params, reader, callback, scope, arg){
12141 if(this.fireEvent("beforeload", this, params) !== false){
12143 params : params || {},
12145 callback : callback,
12150 callback : this.loadResponse,
12154 Roo.applyIf(o, this.conn);
12155 if(this.activeRequest){
12156 Roo.Ajax.abort(this.activeRequest);
12158 this.activeRequest = Roo.Ajax.request(o);
12160 this.conn.request(o);
12163 callback.call(scope||this, null, arg, false);
12168 loadResponse : function(o, success, response){
12169 delete this.activeRequest;
12171 this.fireEvent("loadexception", this, o, response);
12172 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12177 result = o.reader.read(response);
12179 this.fireEvent("loadexception", this, o, response, e);
12180 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12184 this.fireEvent("load", this, o, o.request.arg);
12185 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12189 update : function(dataSet){
12194 updateResponse : function(dataSet){
12199 * Ext JS Library 1.1.1
12200 * Copyright(c) 2006-2007, Ext JS, LLC.
12202 * Originally Released Under LGPL - original licence link has changed is not relivant.
12205 * <script type="text/javascript">
12209 * @class Roo.data.ScriptTagProxy
12210 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12211 * other than the originating domain of the running page.<br><br>
12213 * <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
12214 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12216 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12217 * source code that is used as the source inside a <script> tag.<br><br>
12219 * In order for the browser to process the returned data, the server must wrap the data object
12220 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12221 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12222 * depending on whether the callback name was passed:
12225 boolean scriptTag = false;
12226 String cb = request.getParameter("callback");
12229 response.setContentType("text/javascript");
12231 response.setContentType("application/x-json");
12233 Writer out = response.getWriter();
12235 out.write(cb + "(");
12237 out.print(dataBlock.toJsonString());
12244 * @param {Object} config A configuration object.
12246 Roo.data.ScriptTagProxy = function(config){
12247 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12248 Roo.apply(this, config);
12249 this.head = document.getElementsByTagName("head")[0];
12252 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12254 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12256 * @cfg {String} url The URL from which to request the data object.
12259 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12263 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12264 * the server the name of the callback function set up by the load call to process the returned data object.
12265 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12266 * javascript output which calls this named function passing the data object as its only parameter.
12268 callbackParam : "callback",
12270 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12271 * name to the request.
12276 * Load data from the configured URL, read the data object into
12277 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12278 * process that block using the passed callback.
12279 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12280 * for the request to the remote server.
12281 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12282 * object into a block of Roo.data.Records.
12283 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12284 * The function must be passed <ul>
12285 * <li>The Record block object</li>
12286 * <li>The "arg" argument from the load function</li>
12287 * <li>A boolean success indicator</li>
12289 * @param {Object} scope The scope in which to call the callback
12290 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12292 load : function(params, reader, callback, scope, arg){
12293 if(this.fireEvent("beforeload", this, params) !== false){
12295 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12297 var url = this.url;
12298 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12300 url += "&_dc=" + (new Date().getTime());
12302 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12305 cb : "stcCallback"+transId,
12306 scriptId : "stcScript"+transId,
12310 callback : callback,
12316 window[trans.cb] = function(o){
12317 conn.handleResponse(o, trans);
12320 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12322 if(this.autoAbort !== false){
12326 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12328 var script = document.createElement("script");
12329 script.setAttribute("src", url);
12330 script.setAttribute("type", "text/javascript");
12331 script.setAttribute("id", trans.scriptId);
12332 this.head.appendChild(script);
12334 this.trans = trans;
12336 callback.call(scope||this, null, arg, false);
12341 isLoading : function(){
12342 return this.trans ? true : false;
12346 * Abort the current server request.
12348 abort : function(){
12349 if(this.isLoading()){
12350 this.destroyTrans(this.trans);
12355 destroyTrans : function(trans, isLoaded){
12356 this.head.removeChild(document.getElementById(trans.scriptId));
12357 clearTimeout(trans.timeoutId);
12359 window[trans.cb] = undefined;
12361 delete window[trans.cb];
12364 // if hasn't been loaded, wait for load to remove it to prevent script error
12365 window[trans.cb] = function(){
12366 window[trans.cb] = undefined;
12368 delete window[trans.cb];
12375 handleResponse : function(o, trans){
12376 this.trans = false;
12377 this.destroyTrans(trans, true);
12380 result = trans.reader.readRecords(o);
12382 this.fireEvent("loadexception", this, o, trans.arg, e);
12383 trans.callback.call(trans.scope||window, null, trans.arg, false);
12386 this.fireEvent("load", this, o, trans.arg);
12387 trans.callback.call(trans.scope||window, result, trans.arg, true);
12391 handleFailure : function(trans){
12392 this.trans = false;
12393 this.destroyTrans(trans, false);
12394 this.fireEvent("loadexception", this, null, trans.arg);
12395 trans.callback.call(trans.scope||window, null, trans.arg, false);
12399 * Ext JS Library 1.1.1
12400 * Copyright(c) 2006-2007, Ext JS, LLC.
12402 * Originally Released Under LGPL - original licence link has changed is not relivant.
12405 * <script type="text/javascript">
12409 * @class Roo.data.JsonReader
12410 * @extends Roo.data.DataReader
12411 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12412 * based on mappings in a provided Roo.data.Record constructor.
12414 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12415 * in the reply previously.
12420 var RecordDef = Roo.data.Record.create([
12421 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12422 {name: 'occupation'} // This field will use "occupation" as the mapping.
12424 var myReader = new Roo.data.JsonReader({
12425 totalProperty: "results", // The property which contains the total dataset size (optional)
12426 root: "rows", // The property which contains an Array of row objects
12427 id: "id" // The property within each row object that provides an ID for the record (optional)
12431 * This would consume a JSON file like this:
12433 { 'results': 2, 'rows': [
12434 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12435 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12438 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12439 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12440 * paged from the remote server.
12441 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12442 * @cfg {String} root name of the property which contains the Array of row objects.
12443 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12444 * @cfg {Array} fields Array of field definition objects
12446 * Create a new JsonReader
12447 * @param {Object} meta Metadata configuration options
12448 * @param {Object} recordType Either an Array of field definition objects,
12449 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12451 Roo.data.JsonReader = function(meta, recordType){
12454 // set some defaults:
12455 Roo.applyIf(meta, {
12456 totalProperty: 'total',
12457 successProperty : 'success',
12462 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12464 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12467 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12468 * Used by Store query builder to append _requestMeta to params.
12471 metaFromRemote : false,
12473 * This method is only used by a DataProxy which has retrieved data from a remote server.
12474 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12475 * @return {Object} data A data block which is used by an Roo.data.Store object as
12476 * a cache of Roo.data.Records.
12478 read : function(response){
12479 var json = response.responseText;
12481 var o = /* eval:var:o */ eval("("+json+")");
12483 throw {message: "JsonReader.read: Json object not found"};
12489 this.metaFromRemote = true;
12490 this.meta = o.metaData;
12491 this.recordType = Roo.data.Record.create(o.metaData.fields);
12492 this.onMetaChange(this.meta, this.recordType, o);
12494 return this.readRecords(o);
12497 // private function a store will implement
12498 onMetaChange : function(meta, recordType, o){
12505 simpleAccess: function(obj, subsc) {
12512 getJsonAccessor: function(){
12514 return function(expr) {
12516 return(re.test(expr))
12517 ? new Function("obj", "return obj." + expr)
12522 return Roo.emptyFn;
12527 * Create a data block containing Roo.data.Records from an XML document.
12528 * @param {Object} o An object which contains an Array of row objects in the property specified
12529 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12530 * which contains the total size of the dataset.
12531 * @return {Object} data A data block which is used by an Roo.data.Store object as
12532 * a cache of Roo.data.Records.
12534 readRecords : function(o){
12536 * After any data loads, the raw JSON data is available for further custom processing.
12540 var s = this.meta, Record = this.recordType,
12541 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12543 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12545 if(s.totalProperty) {
12546 this.getTotal = this.getJsonAccessor(s.totalProperty);
12548 if(s.successProperty) {
12549 this.getSuccess = this.getJsonAccessor(s.successProperty);
12551 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12553 var g = this.getJsonAccessor(s.id);
12554 this.getId = function(rec) {
12556 return (r === undefined || r === "") ? null : r;
12559 this.getId = function(){return null;};
12562 for(var jj = 0; jj < fl; jj++){
12564 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12565 this.ef[jj] = this.getJsonAccessor(map);
12569 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12570 if(s.totalProperty){
12571 var vt = parseInt(this.getTotal(o), 10);
12576 if(s.successProperty){
12577 var vs = this.getSuccess(o);
12578 if(vs === false || vs === 'false'){
12583 for(var i = 0; i < c; i++){
12586 var id = this.getId(n);
12587 for(var j = 0; j < fl; j++){
12589 var v = this.ef[j](n);
12591 Roo.log('missing convert for ' + f.name);
12595 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12597 var record = new Record(values, id);
12599 records[i] = record;
12605 totalRecords : totalRecords
12610 * Ext JS Library 1.1.1
12611 * Copyright(c) 2006-2007, Ext JS, LLC.
12613 * Originally Released Under LGPL - original licence link has changed is not relivant.
12616 * <script type="text/javascript">
12620 * @class Roo.data.ArrayReader
12621 * @extends Roo.data.DataReader
12622 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12623 * Each element of that Array represents a row of data fields. The
12624 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12625 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12629 var RecordDef = Roo.data.Record.create([
12630 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12631 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12633 var myReader = new Roo.data.ArrayReader({
12634 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12638 * This would consume an Array like this:
12640 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12642 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12644 * Create a new JsonReader
12645 * @param {Object} meta Metadata configuration options.
12646 * @param {Object} recordType Either an Array of field definition objects
12647 * as specified to {@link Roo.data.Record#create},
12648 * or an {@link Roo.data.Record} object
12649 * created using {@link Roo.data.Record#create}.
12651 Roo.data.ArrayReader = function(meta, recordType){
12652 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12655 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12657 * Create a data block containing Roo.data.Records from an XML document.
12658 * @param {Object} o An Array of row objects which represents the dataset.
12659 * @return {Object} data A data block which is used by an Roo.data.Store object as
12660 * a cache of Roo.data.Records.
12662 readRecords : function(o){
12663 var sid = this.meta ? this.meta.id : null;
12664 var recordType = this.recordType, fields = recordType.prototype.fields;
12667 for(var i = 0; i < root.length; i++){
12670 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12671 for(var j = 0, jlen = fields.length; j < jlen; j++){
12672 var f = fields.items[j];
12673 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12674 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12676 values[f.name] = v;
12678 var record = new recordType(values, id);
12680 records[records.length] = record;
12684 totalRecords : records.length
12693 * @class Roo.bootstrap.ComboBox
12694 * @extends Roo.bootstrap.TriggerField
12695 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12696 * @cfg {Boolean} append (true|false) default false
12697 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12698 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12699 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12700 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12701 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12702 * @cfg {Boolean} animate default true
12703 * @cfg {Boolean} emptyResultText only for touch device
12704 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12705 * @cfg {String} emptyTitle default ''
12707 * Create a new ComboBox.
12708 * @param {Object} config Configuration options
12710 Roo.bootstrap.ComboBox = function(config){
12711 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12715 * Fires when the dropdown list is expanded
12716 * @param {Roo.bootstrap.ComboBox} combo This combo box
12721 * Fires when the dropdown list is collapsed
12722 * @param {Roo.bootstrap.ComboBox} combo This combo box
12726 * @event beforeselect
12727 * Fires before a list item is selected. Return false to cancel the selection.
12728 * @param {Roo.bootstrap.ComboBox} combo This combo box
12729 * @param {Roo.data.Record} record The data record returned from the underlying store
12730 * @param {Number} index The index of the selected item in the dropdown list
12732 'beforeselect' : true,
12735 * Fires when a list item is selected
12736 * @param {Roo.bootstrap.ComboBox} combo This combo box
12737 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12738 * @param {Number} index The index of the selected item in the dropdown list
12742 * @event beforequery
12743 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12744 * The event object passed has these properties:
12745 * @param {Roo.bootstrap.ComboBox} combo This combo box
12746 * @param {String} query The query
12747 * @param {Boolean} forceAll true to force "all" query
12748 * @param {Boolean} cancel true to cancel the query
12749 * @param {Object} e The query event object
12751 'beforequery': true,
12754 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12755 * @param {Roo.bootstrap.ComboBox} combo This combo box
12760 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12761 * @param {Roo.bootstrap.ComboBox} combo This combo box
12762 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12767 * Fires when the remove value from the combobox array
12768 * @param {Roo.bootstrap.ComboBox} combo This combo box
12772 * @event afterremove
12773 * Fires when the remove value from the combobox array
12774 * @param {Roo.bootstrap.ComboBox} combo This combo box
12776 'afterremove' : true,
12778 * @event specialfilter
12779 * Fires when specialfilter
12780 * @param {Roo.bootstrap.ComboBox} combo This combo box
12782 'specialfilter' : true,
12785 * Fires when tick the element
12786 * @param {Roo.bootstrap.ComboBox} combo This combo box
12790 * @event touchviewdisplay
12791 * Fires when touch view require special display (default is using displayField)
12792 * @param {Roo.bootstrap.ComboBox} combo This combo box
12793 * @param {Object} cfg set html .
12795 'touchviewdisplay' : true
12800 this.tickItems = [];
12802 this.selectedIndex = -1;
12803 if(this.mode == 'local'){
12804 if(config.queryDelay === undefined){
12805 this.queryDelay = 10;
12807 if(config.minChars === undefined){
12813 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12816 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12817 * rendering into an Roo.Editor, defaults to false)
12820 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12821 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12824 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12827 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12828 * the dropdown list (defaults to undefined, with no header element)
12832 * @cfg {String/Roo.Template} tpl The template to use to render the output
12836 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12838 listWidth: undefined,
12840 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12841 * mode = 'remote' or 'text' if mode = 'local')
12843 displayField: undefined,
12846 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12847 * mode = 'remote' or 'value' if mode = 'local').
12848 * Note: use of a valueField requires the user make a selection
12849 * in order for a value to be mapped.
12851 valueField: undefined,
12853 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12858 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12859 * field's data value (defaults to the underlying DOM element's name)
12861 hiddenName: undefined,
12863 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12867 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12869 selectedClass: 'active',
12872 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12876 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12877 * anchor positions (defaults to 'tl-bl')
12879 listAlign: 'tl-bl?',
12881 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12885 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12886 * query specified by the allQuery config option (defaults to 'query')
12888 triggerAction: 'query',
12890 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12891 * (defaults to 4, does not apply if editable = false)
12895 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12896 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12900 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12901 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12905 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12906 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12910 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12911 * when editable = true (defaults to false)
12913 selectOnFocus:false,
12915 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12917 queryParam: 'query',
12919 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12920 * when mode = 'remote' (defaults to 'Loading...')
12922 loadingText: 'Loading...',
12924 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12928 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12932 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12933 * traditional select (defaults to true)
12937 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12941 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12945 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12946 * listWidth has a higher value)
12950 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12951 * allow the user to set arbitrary text into the field (defaults to false)
12953 forceSelection:false,
12955 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12956 * if typeAhead = true (defaults to 250)
12958 typeAheadDelay : 250,
12960 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12961 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12963 valueNotFoundText : undefined,
12965 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12967 blockFocus : false,
12970 * @cfg {Boolean} disableClear Disable showing of clear button.
12972 disableClear : false,
12974 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12976 alwaysQuery : false,
12979 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12984 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12986 invalidClass : "has-warning",
12989 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12991 validClass : "has-success",
12994 * @cfg {Boolean} specialFilter (true|false) special filter default false
12996 specialFilter : false,
12999 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13001 mobileTouchView : true,
13004 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13006 useNativeIOS : false,
13008 ios_options : false,
13020 btnPosition : 'right',
13021 triggerList : true,
13022 showToggleBtn : true,
13024 emptyResultText: 'Empty',
13025 triggerText : 'Select',
13028 // element that contains real text value.. (when hidden is used..)
13030 getAutoCreate : function()
13035 * Render classic select for iso
13038 if(Roo.isIOS && this.useNativeIOS){
13039 cfg = this.getAutoCreateNativeIOS();
13047 if(Roo.isTouch && this.mobileTouchView){
13048 cfg = this.getAutoCreateTouchView();
13055 if(!this.tickable){
13056 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13061 * ComboBox with tickable selections
13064 var align = this.labelAlign || this.parentLabelAlign();
13067 cls : 'form-group roo-combobox-tickable' //input-group
13070 var btn_text_select = '';
13071 var btn_text_done = '';
13072 var btn_text_cancel = '';
13074 if (this.btn_text_show) {
13075 btn_text_select = 'Select';
13076 btn_text_done = 'Done';
13077 btn_text_cancel = 'Cancel';
13082 cls : 'tickable-buttons',
13087 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13088 //html : this.triggerText
13089 html: btn_text_select
13095 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13097 html: btn_text_done
13103 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13105 html: btn_text_cancel
13111 buttons.cn.unshift({
13113 cls: 'roo-select2-search-field-input'
13119 Roo.each(buttons.cn, function(c){
13121 c.cls += ' btn-' + _this.size;
13124 if (_this.disabled) {
13135 cls: 'form-hidden-field'
13139 cls: 'roo-select2-choices',
13143 cls: 'roo-select2-search-field',
13154 cls: 'roo-select2-container input-group roo-select2-container-multi',
13159 // cls: 'typeahead typeahead-long dropdown-menu',
13160 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13165 if(this.hasFeedback && !this.allowBlank){
13169 cls: 'glyphicon form-control-feedback'
13172 combobox.cn.push(feedback);
13176 if (align ==='left' && this.fieldLabel.length) {
13178 cfg.cls += ' roo-form-group-label-left';
13183 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13184 tooltip : 'This field is required'
13189 cls : 'control-label',
13190 html : this.fieldLabel
13202 var labelCfg = cfg.cn[1];
13203 var contentCfg = cfg.cn[2];
13206 if(this.indicatorpos == 'right'){
13212 cls : 'control-label',
13216 html : this.fieldLabel
13220 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13221 tooltip : 'This field is required'
13236 labelCfg = cfg.cn[0];
13237 contentCfg = cfg.cn[1];
13241 if(this.labelWidth > 12){
13242 labelCfg.style = "width: " + this.labelWidth + 'px';
13245 if(this.labelWidth < 13 && this.labelmd == 0){
13246 this.labelmd = this.labelWidth;
13249 if(this.labellg > 0){
13250 labelCfg.cls += ' col-lg-' + this.labellg;
13251 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13254 if(this.labelmd > 0){
13255 labelCfg.cls += ' col-md-' + this.labelmd;
13256 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13259 if(this.labelsm > 0){
13260 labelCfg.cls += ' col-sm-' + this.labelsm;
13261 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13264 if(this.labelxs > 0){
13265 labelCfg.cls += ' col-xs-' + this.labelxs;
13266 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13270 } else if ( this.fieldLabel.length) {
13271 // Roo.log(" label");
13275 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13276 tooltip : 'This field is required'
13280 //cls : 'input-group-addon',
13281 html : this.fieldLabel
13286 if(this.indicatorpos == 'right'){
13290 //cls : 'input-group-addon',
13291 html : this.fieldLabel
13295 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13296 tooltip : 'This field is required'
13305 // Roo.log(" no label && no align");
13312 ['xs','sm','md','lg'].map(function(size){
13313 if (settings[size]) {
13314 cfg.cls += ' col-' + size + '-' + settings[size];
13322 _initEventsCalled : false,
13325 initEvents: function()
13327 if (this._initEventsCalled) { // as we call render... prevent looping...
13330 this._initEventsCalled = true;
13333 throw "can not find store for combo";
13336 this.indicator = this.indicatorEl();
13338 this.store = Roo.factory(this.store, Roo.data);
13339 this.store.parent = this;
13341 // if we are building from html. then this element is so complex, that we can not really
13342 // use the rendered HTML.
13343 // so we have to trash and replace the previous code.
13344 if (Roo.XComponent.build_from_html) {
13345 // remove this element....
13346 var e = this.el.dom, k=0;
13347 while (e ) { e = e.previousSibling; ++k;}
13352 this.rendered = false;
13354 this.render(this.parent().getChildContainer(true), k);
13357 if(Roo.isIOS && this.useNativeIOS){
13358 this.initIOSView();
13366 if(Roo.isTouch && this.mobileTouchView){
13367 this.initTouchView();
13372 this.initTickableEvents();
13376 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13378 if(this.hiddenName){
13380 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13382 this.hiddenField.dom.value =
13383 this.hiddenValue !== undefined ? this.hiddenValue :
13384 this.value !== undefined ? this.value : '';
13386 // prevent input submission
13387 this.el.dom.removeAttribute('name');
13388 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13393 // this.el.dom.setAttribute('autocomplete', 'off');
13396 var cls = 'x-combo-list';
13398 //this.list = new Roo.Layer({
13399 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13405 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13406 _this.list.setWidth(lw);
13409 this.list.on('mouseover', this.onViewOver, this);
13410 this.list.on('mousemove', this.onViewMove, this);
13411 this.list.on('scroll', this.onViewScroll, this);
13414 this.list.swallowEvent('mousewheel');
13415 this.assetHeight = 0;
13418 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13419 this.assetHeight += this.header.getHeight();
13422 this.innerList = this.list.createChild({cls:cls+'-inner'});
13423 this.innerList.on('mouseover', this.onViewOver, this);
13424 this.innerList.on('mousemove', this.onViewMove, this);
13425 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13427 if(this.allowBlank && !this.pageSize && !this.disableClear){
13428 this.footer = this.list.createChild({cls:cls+'-ft'});
13429 this.pageTb = new Roo.Toolbar(this.footer);
13433 this.footer = this.list.createChild({cls:cls+'-ft'});
13434 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13435 {pageSize: this.pageSize});
13439 if (this.pageTb && this.allowBlank && !this.disableClear) {
13441 this.pageTb.add(new Roo.Toolbar.Fill(), {
13442 cls: 'x-btn-icon x-btn-clear',
13444 handler: function()
13447 _this.clearValue();
13448 _this.onSelect(false, -1);
13453 this.assetHeight += this.footer.getHeight();
13458 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13461 this.view = new Roo.View(this.list, this.tpl, {
13462 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13464 //this.view.wrapEl.setDisplayed(false);
13465 this.view.on('click', this.onViewClick, this);
13468 this.store.on('beforeload', this.onBeforeLoad, this);
13469 this.store.on('load', this.onLoad, this);
13470 this.store.on('loadexception', this.onLoadException, this);
13472 if(this.resizable){
13473 this.resizer = new Roo.Resizable(this.list, {
13474 pinned:true, handles:'se'
13476 this.resizer.on('resize', function(r, w, h){
13477 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13478 this.listWidth = w;
13479 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13480 this.restrictHeight();
13482 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13485 if(!this.editable){
13486 this.editable = true;
13487 this.setEditable(false);
13492 if (typeof(this.events.add.listeners) != 'undefined') {
13494 this.addicon = this.wrap.createChild(
13495 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13497 this.addicon.on('click', function(e) {
13498 this.fireEvent('add', this);
13501 if (typeof(this.events.edit.listeners) != 'undefined') {
13503 this.editicon = this.wrap.createChild(
13504 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13505 if (this.addicon) {
13506 this.editicon.setStyle('margin-left', '40px');
13508 this.editicon.on('click', function(e) {
13510 // we fire even if inothing is selected..
13511 this.fireEvent('edit', this, this.lastData );
13517 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13518 "up" : function(e){
13519 this.inKeyMode = true;
13523 "down" : function(e){
13524 if(!this.isExpanded()){
13525 this.onTriggerClick();
13527 this.inKeyMode = true;
13532 "enter" : function(e){
13533 // this.onViewClick();
13537 if(this.fireEvent("specialkey", this, e)){
13538 this.onViewClick(false);
13544 "esc" : function(e){
13548 "tab" : function(e){
13551 if(this.fireEvent("specialkey", this, e)){
13552 this.onViewClick(false);
13560 doRelay : function(foo, bar, hname){
13561 if(hname == 'down' || this.scope.isExpanded()){
13562 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13571 this.queryDelay = Math.max(this.queryDelay || 10,
13572 this.mode == 'local' ? 10 : 250);
13575 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13577 if(this.typeAhead){
13578 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13580 if(this.editable !== false){
13581 this.inputEl().on("keyup", this.onKeyUp, this);
13583 if(this.forceSelection){
13584 this.inputEl().on('blur', this.doForce, this);
13588 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13589 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13593 initTickableEvents: function()
13597 if(this.hiddenName){
13599 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13601 this.hiddenField.dom.value =
13602 this.hiddenValue !== undefined ? this.hiddenValue :
13603 this.value !== undefined ? this.value : '';
13605 // prevent input submission
13606 this.el.dom.removeAttribute('name');
13607 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13612 // this.list = this.el.select('ul.dropdown-menu',true).first();
13614 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13615 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13616 if(this.triggerList){
13617 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13620 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13621 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13623 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13624 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13626 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13627 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13629 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13630 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13631 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13634 this.cancelBtn.hide();
13639 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13640 _this.list.setWidth(lw);
13643 this.list.on('mouseover', this.onViewOver, this);
13644 this.list.on('mousemove', this.onViewMove, this);
13646 this.list.on('scroll', this.onViewScroll, this);
13649 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13650 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13653 this.view = new Roo.View(this.list, this.tpl, {
13658 selectedClass: this.selectedClass
13661 //this.view.wrapEl.setDisplayed(false);
13662 this.view.on('click', this.onViewClick, this);
13666 this.store.on('beforeload', this.onBeforeLoad, this);
13667 this.store.on('load', this.onLoad, this);
13668 this.store.on('loadexception', this.onLoadException, this);
13671 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13672 "up" : function(e){
13673 this.inKeyMode = true;
13677 "down" : function(e){
13678 this.inKeyMode = true;
13682 "enter" : function(e){
13683 if(this.fireEvent("specialkey", this, e)){
13684 this.onViewClick(false);
13690 "esc" : function(e){
13691 this.onTickableFooterButtonClick(e, false, false);
13694 "tab" : function(e){
13695 this.fireEvent("specialkey", this, e);
13697 this.onTickableFooterButtonClick(e, false, false);
13704 doRelay : function(e, fn, key){
13705 if(this.scope.isExpanded()){
13706 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13715 this.queryDelay = Math.max(this.queryDelay || 10,
13716 this.mode == 'local' ? 10 : 250);
13719 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13721 if(this.typeAhead){
13722 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13725 if(this.editable !== false){
13726 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13729 this.indicator = this.indicatorEl();
13731 if(this.indicator){
13732 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13733 this.indicator.hide();
13738 onDestroy : function(){
13740 this.view.setStore(null);
13741 this.view.el.removeAllListeners();
13742 this.view.el.remove();
13743 this.view.purgeListeners();
13746 this.list.dom.innerHTML = '';
13750 this.store.un('beforeload', this.onBeforeLoad, this);
13751 this.store.un('load', this.onLoad, this);
13752 this.store.un('loadexception', this.onLoadException, this);
13754 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13758 fireKey : function(e){
13759 if(e.isNavKeyPress() && !this.list.isVisible()){
13760 this.fireEvent("specialkey", this, e);
13765 onResize: function(w, h){
13766 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13768 // if(typeof w != 'number'){
13769 // // we do not handle it!?!?
13772 // var tw = this.trigger.getWidth();
13773 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13774 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13776 // this.inputEl().setWidth( this.adjustWidth('input', x));
13778 // //this.trigger.setStyle('left', x+'px');
13780 // if(this.list && this.listWidth === undefined){
13781 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13782 // this.list.setWidth(lw);
13783 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13791 * Allow or prevent the user from directly editing the field text. If false is passed,
13792 * the user will only be able to select from the items defined in the dropdown list. This method
13793 * is the runtime equivalent of setting the 'editable' config option at config time.
13794 * @param {Boolean} value True to allow the user to directly edit the field text
13796 setEditable : function(value){
13797 if(value == this.editable){
13800 this.editable = value;
13802 this.inputEl().dom.setAttribute('readOnly', true);
13803 this.inputEl().on('mousedown', this.onTriggerClick, this);
13804 this.inputEl().addClass('x-combo-noedit');
13806 this.inputEl().dom.setAttribute('readOnly', false);
13807 this.inputEl().un('mousedown', this.onTriggerClick, this);
13808 this.inputEl().removeClass('x-combo-noedit');
13814 onBeforeLoad : function(combo,opts){
13815 if(!this.hasFocus){
13819 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13821 this.restrictHeight();
13822 this.selectedIndex = -1;
13826 onLoad : function(){
13828 this.hasQuery = false;
13830 if(!this.hasFocus){
13834 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13835 this.loading.hide();
13838 if(this.store.getCount() > 0){
13841 this.restrictHeight();
13842 if(this.lastQuery == this.allQuery){
13843 if(this.editable && !this.tickable){
13844 this.inputEl().dom.select();
13848 !this.selectByValue(this.value, true) &&
13851 !this.store.lastOptions ||
13852 typeof(this.store.lastOptions.add) == 'undefined' ||
13853 this.store.lastOptions.add != true
13856 this.select(0, true);
13859 if(this.autoFocus){
13862 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13863 this.taTask.delay(this.typeAheadDelay);
13867 this.onEmptyResults();
13873 onLoadException : function()
13875 this.hasQuery = false;
13877 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13878 this.loading.hide();
13881 if(this.tickable && this.editable){
13886 // only causes errors at present
13887 //Roo.log(this.store.reader.jsonData);
13888 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13890 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13896 onTypeAhead : function(){
13897 if(this.store.getCount() > 0){
13898 var r = this.store.getAt(0);
13899 var newValue = r.data[this.displayField];
13900 var len = newValue.length;
13901 var selStart = this.getRawValue().length;
13903 if(selStart != len){
13904 this.setRawValue(newValue);
13905 this.selectText(selStart, newValue.length);
13911 onSelect : function(record, index){
13913 if(this.fireEvent('beforeselect', this, record, index) !== false){
13915 this.setFromData(index > -1 ? record.data : false);
13918 this.fireEvent('select', this, record, index);
13923 * Returns the currently selected field value or empty string if no value is set.
13924 * @return {String} value The selected value
13926 getValue : function()
13928 if(Roo.isIOS && this.useNativeIOS){
13929 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13933 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13936 if(this.valueField){
13937 return typeof this.value != 'undefined' ? this.value : '';
13939 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13943 getRawValue : function()
13945 if(Roo.isIOS && this.useNativeIOS){
13946 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13949 var v = this.inputEl().getValue();
13955 * Clears any text/value currently set in the field
13957 clearValue : function(){
13959 if(this.hiddenField){
13960 this.hiddenField.dom.value = '';
13963 this.setRawValue('');
13964 this.lastSelectionText = '';
13965 this.lastData = false;
13967 var close = this.closeTriggerEl();
13978 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13979 * will be displayed in the field. If the value does not match the data value of an existing item,
13980 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13981 * Otherwise the field will be blank (although the value will still be set).
13982 * @param {String} value The value to match
13984 setValue : function(v)
13986 if(Roo.isIOS && this.useNativeIOS){
13987 this.setIOSValue(v);
13997 if(this.valueField){
13998 var r = this.findRecord(this.valueField, v);
14000 text = r.data[this.displayField];
14001 }else if(this.valueNotFoundText !== undefined){
14002 text = this.valueNotFoundText;
14005 this.lastSelectionText = text;
14006 if(this.hiddenField){
14007 this.hiddenField.dom.value = v;
14009 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14012 var close = this.closeTriggerEl();
14015 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14021 * @property {Object} the last set data for the element
14026 * Sets the value of the field based on a object which is related to the record format for the store.
14027 * @param {Object} value the value to set as. or false on reset?
14029 setFromData : function(o){
14036 var dv = ''; // display value
14037 var vv = ''; // value value..
14039 if (this.displayField) {
14040 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14042 // this is an error condition!!!
14043 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14046 if(this.valueField){
14047 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14050 var close = this.closeTriggerEl();
14053 if(dv.length || vv * 1 > 0){
14055 this.blockFocus=true;
14061 if(this.hiddenField){
14062 this.hiddenField.dom.value = vv;
14064 this.lastSelectionText = dv;
14065 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14069 // no hidden field.. - we store the value in 'value', but still display
14070 // display field!!!!
14071 this.lastSelectionText = dv;
14072 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14079 reset : function(){
14080 // overridden so that last data is reset..
14087 this.setValue(this.originalValue);
14088 //this.clearInvalid();
14089 this.lastData = false;
14091 this.view.clearSelections();
14097 findRecord : function(prop, value){
14099 if(this.store.getCount() > 0){
14100 this.store.each(function(r){
14101 if(r.data[prop] == value){
14111 getName: function()
14113 // returns hidden if it's set..
14114 if (!this.rendered) {return ''};
14115 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14119 onViewMove : function(e, t){
14120 this.inKeyMode = false;
14124 onViewOver : function(e, t){
14125 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14128 var item = this.view.findItemFromChild(t);
14131 var index = this.view.indexOf(item);
14132 this.select(index, false);
14137 onViewClick : function(view, doFocus, el, e)
14139 var index = this.view.getSelectedIndexes()[0];
14141 var r = this.store.getAt(index);
14145 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14152 Roo.each(this.tickItems, function(v,k){
14154 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14156 _this.tickItems.splice(k, 1);
14158 if(typeof(e) == 'undefined' && view == false){
14159 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14171 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14172 this.tickItems.push(r.data);
14175 if(typeof(e) == 'undefined' && view == false){
14176 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14183 this.onSelect(r, index);
14185 if(doFocus !== false && !this.blockFocus){
14186 this.inputEl().focus();
14191 restrictHeight : function(){
14192 //this.innerList.dom.style.height = '';
14193 //var inner = this.innerList.dom;
14194 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14195 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14196 //this.list.beginUpdate();
14197 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14198 this.list.alignTo(this.inputEl(), this.listAlign);
14199 this.list.alignTo(this.inputEl(), this.listAlign);
14200 //this.list.endUpdate();
14204 onEmptyResults : function(){
14206 if(this.tickable && this.editable){
14207 this.hasFocus = false;
14208 this.restrictHeight();
14216 * Returns true if the dropdown list is expanded, else false.
14218 isExpanded : function(){
14219 return this.list.isVisible();
14223 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14224 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14225 * @param {String} value The data value of the item to select
14226 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14227 * selected item if it is not currently in view (defaults to true)
14228 * @return {Boolean} True if the value matched an item in the list, else false
14230 selectByValue : function(v, scrollIntoView){
14231 if(v !== undefined && v !== null){
14232 var r = this.findRecord(this.valueField || this.displayField, v);
14234 this.select(this.store.indexOf(r), scrollIntoView);
14242 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14243 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14244 * @param {Number} index The zero-based index of the list item to select
14245 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14246 * selected item if it is not currently in view (defaults to true)
14248 select : function(index, scrollIntoView){
14249 this.selectedIndex = index;
14250 this.view.select(index);
14251 if(scrollIntoView !== false){
14252 var el = this.view.getNode(index);
14254 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14257 this.list.scrollChildIntoView(el, false);
14263 selectNext : function(){
14264 var ct = this.store.getCount();
14266 if(this.selectedIndex == -1){
14268 }else if(this.selectedIndex < ct-1){
14269 this.select(this.selectedIndex+1);
14275 selectPrev : function(){
14276 var ct = this.store.getCount();
14278 if(this.selectedIndex == -1){
14280 }else if(this.selectedIndex != 0){
14281 this.select(this.selectedIndex-1);
14287 onKeyUp : function(e){
14288 if(this.editable !== false && !e.isSpecialKey()){
14289 this.lastKey = e.getKey();
14290 this.dqTask.delay(this.queryDelay);
14295 validateBlur : function(){
14296 return !this.list || !this.list.isVisible();
14300 initQuery : function(){
14302 var v = this.getRawValue();
14304 if(this.tickable && this.editable){
14305 v = this.tickableInputEl().getValue();
14312 doForce : function(){
14313 if(this.inputEl().dom.value.length > 0){
14314 this.inputEl().dom.value =
14315 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14321 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14322 * query allowing the query action to be canceled if needed.
14323 * @param {String} query The SQL query to execute
14324 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14325 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14326 * saved in the current store (defaults to false)
14328 doQuery : function(q, forceAll){
14330 if(q === undefined || q === null){
14335 forceAll: forceAll,
14339 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14344 forceAll = qe.forceAll;
14345 if(forceAll === true || (q.length >= this.minChars)){
14347 this.hasQuery = true;
14349 if(this.lastQuery != q || this.alwaysQuery){
14350 this.lastQuery = q;
14351 if(this.mode == 'local'){
14352 this.selectedIndex = -1;
14354 this.store.clearFilter();
14357 if(this.specialFilter){
14358 this.fireEvent('specialfilter', this);
14363 this.store.filter(this.displayField, q);
14366 this.store.fireEvent("datachanged", this.store);
14373 this.store.baseParams[this.queryParam] = q;
14375 var options = {params : this.getParams(q)};
14378 options.add = true;
14379 options.params.start = this.page * this.pageSize;
14382 this.store.load(options);
14385 * this code will make the page width larger, at the beginning, the list not align correctly,
14386 * we should expand the list on onLoad
14387 * so command out it
14392 this.selectedIndex = -1;
14397 this.loadNext = false;
14401 getParams : function(q){
14403 //p[this.queryParam] = q;
14407 p.limit = this.pageSize;
14413 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14415 collapse : function(){
14416 if(!this.isExpanded()){
14422 this.hasFocus = false;
14426 this.cancelBtn.hide();
14427 this.trigger.show();
14430 this.tickableInputEl().dom.value = '';
14431 this.tickableInputEl().blur();
14436 Roo.get(document).un('mousedown', this.collapseIf, this);
14437 Roo.get(document).un('mousewheel', this.collapseIf, this);
14438 if (!this.editable) {
14439 Roo.get(document).un('keydown', this.listKeyPress, this);
14441 this.fireEvent('collapse', this);
14447 collapseIf : function(e){
14448 var in_combo = e.within(this.el);
14449 var in_list = e.within(this.list);
14450 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14452 if (in_combo || in_list || is_list) {
14453 //e.stopPropagation();
14458 this.onTickableFooterButtonClick(e, false, false);
14466 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14468 expand : function(){
14470 if(this.isExpanded() || !this.hasFocus){
14474 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14475 this.list.setWidth(lw);
14481 this.restrictHeight();
14485 this.tickItems = Roo.apply([], this.item);
14488 this.cancelBtn.show();
14489 this.trigger.hide();
14492 this.tickableInputEl().focus();
14497 Roo.get(document).on('mousedown', this.collapseIf, this);
14498 Roo.get(document).on('mousewheel', this.collapseIf, this);
14499 if (!this.editable) {
14500 Roo.get(document).on('keydown', this.listKeyPress, this);
14503 this.fireEvent('expand', this);
14507 // Implements the default empty TriggerField.onTriggerClick function
14508 onTriggerClick : function(e)
14510 Roo.log('trigger click');
14512 if(this.disabled || !this.triggerList){
14517 this.loadNext = false;
14519 if(this.isExpanded()){
14521 if (!this.blockFocus) {
14522 this.inputEl().focus();
14526 this.hasFocus = true;
14527 if(this.triggerAction == 'all') {
14528 this.doQuery(this.allQuery, true);
14530 this.doQuery(this.getRawValue());
14532 if (!this.blockFocus) {
14533 this.inputEl().focus();
14538 onTickableTriggerClick : function(e)
14545 this.loadNext = false;
14546 this.hasFocus = true;
14548 if(this.triggerAction == 'all') {
14549 this.doQuery(this.allQuery, true);
14551 this.doQuery(this.getRawValue());
14555 onSearchFieldClick : function(e)
14557 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14558 this.onTickableFooterButtonClick(e, false, false);
14562 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14567 this.loadNext = false;
14568 this.hasFocus = true;
14570 if(this.triggerAction == 'all') {
14571 this.doQuery(this.allQuery, true);
14573 this.doQuery(this.getRawValue());
14577 listKeyPress : function(e)
14579 //Roo.log('listkeypress');
14580 // scroll to first matching element based on key pres..
14581 if (e.isSpecialKey()) {
14584 var k = String.fromCharCode(e.getKey()).toUpperCase();
14587 var csel = this.view.getSelectedNodes();
14588 var cselitem = false;
14590 var ix = this.view.indexOf(csel[0]);
14591 cselitem = this.store.getAt(ix);
14592 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14598 this.store.each(function(v) {
14600 // start at existing selection.
14601 if (cselitem.id == v.id) {
14607 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14608 match = this.store.indexOf(v);
14614 if (match === false) {
14615 return true; // no more action?
14618 this.view.select(match);
14619 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14620 sn.scrollIntoView(sn.dom.parentNode, false);
14623 onViewScroll : function(e, t){
14625 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){
14629 this.hasQuery = true;
14631 this.loading = this.list.select('.loading', true).first();
14633 if(this.loading === null){
14634 this.list.createChild({
14636 cls: 'loading roo-select2-more-results roo-select2-active',
14637 html: 'Loading more results...'
14640 this.loading = this.list.select('.loading', true).first();
14642 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14644 this.loading.hide();
14647 this.loading.show();
14652 this.loadNext = true;
14654 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14659 addItem : function(o)
14661 var dv = ''; // display value
14663 if (this.displayField) {
14664 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14666 // this is an error condition!!!
14667 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14674 var choice = this.choices.createChild({
14676 cls: 'roo-select2-search-choice',
14685 cls: 'roo-select2-search-choice-close fa fa-times',
14690 }, this.searchField);
14692 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14694 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14702 this.inputEl().dom.value = '';
14707 onRemoveItem : function(e, _self, o)
14709 e.preventDefault();
14711 this.lastItem = Roo.apply([], this.item);
14713 var index = this.item.indexOf(o.data) * 1;
14716 Roo.log('not this item?!');
14720 this.item.splice(index, 1);
14725 this.fireEvent('remove', this, e);
14731 syncValue : function()
14733 if(!this.item.length){
14740 Roo.each(this.item, function(i){
14741 if(_this.valueField){
14742 value.push(i[_this.valueField]);
14749 this.value = value.join(',');
14751 if(this.hiddenField){
14752 this.hiddenField.dom.value = this.value;
14755 this.store.fireEvent("datachanged", this.store);
14760 clearItem : function()
14762 if(!this.multiple){
14768 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14776 if(this.tickable && !Roo.isTouch){
14777 this.view.refresh();
14781 inputEl: function ()
14783 if(Roo.isIOS && this.useNativeIOS){
14784 return this.el.select('select.roo-ios-select', true).first();
14787 if(Roo.isTouch && this.mobileTouchView){
14788 return this.el.select('input.form-control',true).first();
14792 return this.searchField;
14795 return this.el.select('input.form-control',true).first();
14798 onTickableFooterButtonClick : function(e, btn, el)
14800 e.preventDefault();
14802 this.lastItem = Roo.apply([], this.item);
14804 if(btn && btn.name == 'cancel'){
14805 this.tickItems = Roo.apply([], this.item);
14814 Roo.each(this.tickItems, function(o){
14822 validate : function()
14824 if(this.getVisibilityEl().hasClass('hidden')){
14828 var v = this.getRawValue();
14831 v = this.getValue();
14834 if(this.disabled || this.allowBlank || v.length){
14839 this.markInvalid();
14843 tickableInputEl : function()
14845 if(!this.tickable || !this.editable){
14846 return this.inputEl();
14849 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14853 getAutoCreateTouchView : function()
14858 cls: 'form-group' //input-group
14864 type : this.inputType,
14865 cls : 'form-control x-combo-noedit',
14866 autocomplete: 'new-password',
14867 placeholder : this.placeholder || '',
14872 input.name = this.name;
14876 input.cls += ' input-' + this.size;
14879 if (this.disabled) {
14880 input.disabled = true;
14891 inputblock.cls += ' input-group';
14893 inputblock.cn.unshift({
14895 cls : 'input-group-addon',
14900 if(this.removable && !this.multiple){
14901 inputblock.cls += ' roo-removable';
14903 inputblock.cn.push({
14906 cls : 'roo-combo-removable-btn close'
14910 if(this.hasFeedback && !this.allowBlank){
14912 inputblock.cls += ' has-feedback';
14914 inputblock.cn.push({
14916 cls: 'glyphicon form-control-feedback'
14923 inputblock.cls += (this.before) ? '' : ' input-group';
14925 inputblock.cn.push({
14927 cls : 'input-group-addon',
14938 cls: 'form-hidden-field'
14952 cls: 'form-hidden-field'
14956 cls: 'roo-select2-choices',
14960 cls: 'roo-select2-search-field',
14973 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14979 if(!this.multiple && this.showToggleBtn){
14986 if (this.caret != false) {
14989 cls: 'fa fa-' + this.caret
14996 cls : 'input-group-addon btn dropdown-toggle',
15001 cls: 'combobox-clear',
15015 combobox.cls += ' roo-select2-container-multi';
15018 var align = this.labelAlign || this.parentLabelAlign();
15020 if (align ==='left' && this.fieldLabel.length) {
15025 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15026 tooltip : 'This field is required'
15030 cls : 'control-label',
15031 html : this.fieldLabel
15042 var labelCfg = cfg.cn[1];
15043 var contentCfg = cfg.cn[2];
15046 if(this.indicatorpos == 'right'){
15051 cls : 'control-label',
15055 html : this.fieldLabel
15059 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15060 tooltip : 'This field is required'
15073 labelCfg = cfg.cn[0];
15074 contentCfg = cfg.cn[1];
15079 if(this.labelWidth > 12){
15080 labelCfg.style = "width: " + this.labelWidth + 'px';
15083 if(this.labelWidth < 13 && this.labelmd == 0){
15084 this.labelmd = this.labelWidth;
15087 if(this.labellg > 0){
15088 labelCfg.cls += ' col-lg-' + this.labellg;
15089 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15092 if(this.labelmd > 0){
15093 labelCfg.cls += ' col-md-' + this.labelmd;
15094 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15097 if(this.labelsm > 0){
15098 labelCfg.cls += ' col-sm-' + this.labelsm;
15099 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15102 if(this.labelxs > 0){
15103 labelCfg.cls += ' col-xs-' + this.labelxs;
15104 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15108 } else if ( this.fieldLabel.length) {
15112 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15113 tooltip : 'This field is required'
15117 cls : 'control-label',
15118 html : this.fieldLabel
15129 if(this.indicatorpos == 'right'){
15133 cls : 'control-label',
15134 html : this.fieldLabel,
15138 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15139 tooltip : 'This field is required'
15156 var settings = this;
15158 ['xs','sm','md','lg'].map(function(size){
15159 if (settings[size]) {
15160 cfg.cls += ' col-' + size + '-' + settings[size];
15167 initTouchView : function()
15169 this.renderTouchView();
15171 this.touchViewEl.on('scroll', function(){
15172 this.el.dom.scrollTop = 0;
15175 this.originalValue = this.getValue();
15177 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15179 this.inputEl().on("click", this.showTouchView, this);
15180 if (this.triggerEl) {
15181 this.triggerEl.on("click", this.showTouchView, this);
15185 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15186 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15188 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15190 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15191 this.store.on('load', this.onTouchViewLoad, this);
15192 this.store.on('loadexception', this.onTouchViewLoadException, this);
15194 if(this.hiddenName){
15196 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15198 this.hiddenField.dom.value =
15199 this.hiddenValue !== undefined ? this.hiddenValue :
15200 this.value !== undefined ? this.value : '';
15202 this.el.dom.removeAttribute('name');
15203 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15207 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15208 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15211 if(this.removable && !this.multiple){
15212 var close = this.closeTriggerEl();
15214 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15215 close.on('click', this.removeBtnClick, this, close);
15219 * fix the bug in Safari iOS8
15221 this.inputEl().on("focus", function(e){
15222 document.activeElement.blur();
15230 renderTouchView : function()
15232 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15233 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15235 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15236 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15238 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15239 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15240 this.touchViewBodyEl.setStyle('overflow', 'auto');
15242 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15243 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15245 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15246 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15250 showTouchView : function()
15256 this.touchViewHeaderEl.hide();
15258 if(this.modalTitle.length){
15259 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15260 this.touchViewHeaderEl.show();
15263 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15264 this.touchViewEl.show();
15266 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15268 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15269 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15271 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15273 if(this.modalTitle.length){
15274 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15277 this.touchViewBodyEl.setHeight(bodyHeight);
15281 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15283 this.touchViewEl.addClass('in');
15286 this.doTouchViewQuery();
15290 hideTouchView : function()
15292 this.touchViewEl.removeClass('in');
15296 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15298 this.touchViewEl.setStyle('display', 'none');
15303 setTouchViewValue : function()
15310 Roo.each(this.tickItems, function(o){
15315 this.hideTouchView();
15318 doTouchViewQuery : function()
15327 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15331 if(!this.alwaysQuery || this.mode == 'local'){
15332 this.onTouchViewLoad();
15339 onTouchViewBeforeLoad : function(combo,opts)
15345 onTouchViewLoad : function()
15347 if(this.store.getCount() < 1){
15348 this.onTouchViewEmptyResults();
15352 this.clearTouchView();
15354 var rawValue = this.getRawValue();
15356 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15358 this.tickItems = [];
15360 this.store.data.each(function(d, rowIndex){
15361 var row = this.touchViewListGroup.createChild(template);
15363 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15364 row.addClass(d.data.cls);
15367 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15370 html : d.data[this.displayField]
15373 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15374 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15377 row.removeClass('selected');
15378 if(!this.multiple && this.valueField &&
15379 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15382 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15383 row.addClass('selected');
15386 if(this.multiple && this.valueField &&
15387 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15391 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15392 this.tickItems.push(d.data);
15395 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15399 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15401 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15403 if(this.modalTitle.length){
15404 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15407 var listHeight = this.touchViewListGroup.getHeight();
15411 if(firstChecked && listHeight > bodyHeight){
15412 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15417 onTouchViewLoadException : function()
15419 this.hideTouchView();
15422 onTouchViewEmptyResults : function()
15424 this.clearTouchView();
15426 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15428 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15432 clearTouchView : function()
15434 this.touchViewListGroup.dom.innerHTML = '';
15437 onTouchViewClick : function(e, el, o)
15439 e.preventDefault();
15442 var rowIndex = o.rowIndex;
15444 var r = this.store.getAt(rowIndex);
15446 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15448 if(!this.multiple){
15449 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15450 c.dom.removeAttribute('checked');
15453 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15455 this.setFromData(r.data);
15457 var close = this.closeTriggerEl();
15463 this.hideTouchView();
15465 this.fireEvent('select', this, r, rowIndex);
15470 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15471 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15472 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15476 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15477 this.addItem(r.data);
15478 this.tickItems.push(r.data);
15482 getAutoCreateNativeIOS : function()
15485 cls: 'form-group' //input-group,
15490 cls : 'roo-ios-select'
15494 combobox.name = this.name;
15497 if (this.disabled) {
15498 combobox.disabled = true;
15501 var settings = this;
15503 ['xs','sm','md','lg'].map(function(size){
15504 if (settings[size]) {
15505 cfg.cls += ' col-' + size + '-' + settings[size];
15515 initIOSView : function()
15517 this.store.on('load', this.onIOSViewLoad, this);
15522 onIOSViewLoad : function()
15524 if(this.store.getCount() < 1){
15528 this.clearIOSView();
15530 if(this.allowBlank) {
15532 var default_text = '-- SELECT --';
15534 if(this.placeholder.length){
15535 default_text = this.placeholder;
15538 if(this.emptyTitle.length){
15539 default_text += ' - ' + this.emptyTitle + ' -';
15542 var opt = this.inputEl().createChild({
15545 html : default_text
15549 o[this.valueField] = 0;
15550 o[this.displayField] = default_text;
15552 this.ios_options.push({
15559 this.store.data.each(function(d, rowIndex){
15563 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15564 html = d.data[this.displayField];
15569 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15570 value = d.data[this.valueField];
15579 if(this.value == d.data[this.valueField]){
15580 option['selected'] = true;
15583 var opt = this.inputEl().createChild(option);
15585 this.ios_options.push({
15592 this.inputEl().on('change', function(){
15593 this.fireEvent('select', this);
15598 clearIOSView: function()
15600 this.inputEl().dom.innerHTML = '';
15602 this.ios_options = [];
15605 setIOSValue: function(v)
15609 if(!this.ios_options){
15613 Roo.each(this.ios_options, function(opts){
15615 opts.el.dom.removeAttribute('selected');
15617 if(opts.data[this.valueField] != v){
15621 opts.el.dom.setAttribute('selected', true);
15627 * @cfg {Boolean} grow
15631 * @cfg {Number} growMin
15635 * @cfg {Number} growMax
15644 Roo.apply(Roo.bootstrap.ComboBox, {
15648 cls: 'modal-header',
15670 cls: 'list-group-item',
15674 cls: 'roo-combobox-list-group-item-value'
15678 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15692 listItemCheckbox : {
15694 cls: 'list-group-item',
15698 cls: 'roo-combobox-list-group-item-value'
15702 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15718 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15723 cls: 'modal-footer',
15731 cls: 'col-xs-6 text-left',
15734 cls: 'btn btn-danger roo-touch-view-cancel',
15740 cls: 'col-xs-6 text-right',
15743 cls: 'btn btn-success roo-touch-view-ok',
15754 Roo.apply(Roo.bootstrap.ComboBox, {
15756 touchViewTemplate : {
15758 cls: 'modal fade roo-combobox-touch-view',
15762 cls: 'modal-dialog',
15763 style : 'position:fixed', // we have to fix position....
15767 cls: 'modal-content',
15769 Roo.bootstrap.ComboBox.header,
15770 Roo.bootstrap.ComboBox.body,
15771 Roo.bootstrap.ComboBox.footer
15780 * Ext JS Library 1.1.1
15781 * Copyright(c) 2006-2007, Ext JS, LLC.
15783 * Originally Released Under LGPL - original licence link has changed is not relivant.
15786 * <script type="text/javascript">
15791 * @extends Roo.util.Observable
15792 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15793 * This class also supports single and multi selection modes. <br>
15794 * Create a data model bound view:
15796 var store = new Roo.data.Store(...);
15798 var view = new Roo.View({
15800 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15802 singleSelect: true,
15803 selectedClass: "ydataview-selected",
15807 // listen for node click?
15808 view.on("click", function(vw, index, node, e){
15809 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15813 dataModel.load("foobar.xml");
15815 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15817 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15818 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15820 * Note: old style constructor is still suported (container, template, config)
15823 * Create a new View
15824 * @param {Object} config The config object
15827 Roo.View = function(config, depreciated_tpl, depreciated_config){
15829 this.parent = false;
15831 if (typeof(depreciated_tpl) == 'undefined') {
15832 // new way.. - universal constructor.
15833 Roo.apply(this, config);
15834 this.el = Roo.get(this.el);
15837 this.el = Roo.get(config);
15838 this.tpl = depreciated_tpl;
15839 Roo.apply(this, depreciated_config);
15841 this.wrapEl = this.el.wrap().wrap();
15842 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15845 if(typeof(this.tpl) == "string"){
15846 this.tpl = new Roo.Template(this.tpl);
15848 // support xtype ctors..
15849 this.tpl = new Roo.factory(this.tpl, Roo);
15853 this.tpl.compile();
15858 * @event beforeclick
15859 * Fires before a click is processed. Returns false to cancel the default action.
15860 * @param {Roo.View} this
15861 * @param {Number} index The index of the target node
15862 * @param {HTMLElement} node The target node
15863 * @param {Roo.EventObject} e The raw event object
15865 "beforeclick" : true,
15868 * Fires when a template node is clicked.
15869 * @param {Roo.View} this
15870 * @param {Number} index The index of the target node
15871 * @param {HTMLElement} node The target node
15872 * @param {Roo.EventObject} e The raw event object
15877 * Fires when a template node is double clicked.
15878 * @param {Roo.View} this
15879 * @param {Number} index The index of the target node
15880 * @param {HTMLElement} node The target node
15881 * @param {Roo.EventObject} e The raw event object
15885 * @event contextmenu
15886 * Fires when a template node is right clicked.
15887 * @param {Roo.View} this
15888 * @param {Number} index The index of the target node
15889 * @param {HTMLElement} node The target node
15890 * @param {Roo.EventObject} e The raw event object
15892 "contextmenu" : true,
15894 * @event selectionchange
15895 * Fires when the selected nodes change.
15896 * @param {Roo.View} this
15897 * @param {Array} selections Array of the selected nodes
15899 "selectionchange" : true,
15902 * @event beforeselect
15903 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15904 * @param {Roo.View} this
15905 * @param {HTMLElement} node The node to be selected
15906 * @param {Array} selections Array of currently selected nodes
15908 "beforeselect" : true,
15910 * @event preparedata
15911 * Fires on every row to render, to allow you to change the data.
15912 * @param {Roo.View} this
15913 * @param {Object} data to be rendered (change this)
15915 "preparedata" : true
15923 "click": this.onClick,
15924 "dblclick": this.onDblClick,
15925 "contextmenu": this.onContextMenu,
15929 this.selections = [];
15931 this.cmp = new Roo.CompositeElementLite([]);
15933 this.store = Roo.factory(this.store, Roo.data);
15934 this.setStore(this.store, true);
15937 if ( this.footer && this.footer.xtype) {
15939 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15941 this.footer.dataSource = this.store;
15942 this.footer.container = fctr;
15943 this.footer = Roo.factory(this.footer, Roo);
15944 fctr.insertFirst(this.el);
15946 // this is a bit insane - as the paging toolbar seems to detach the el..
15947 // dom.parentNode.parentNode.parentNode
15948 // they get detached?
15952 Roo.View.superclass.constructor.call(this);
15957 Roo.extend(Roo.View, Roo.util.Observable, {
15960 * @cfg {Roo.data.Store} store Data store to load data from.
15965 * @cfg {String|Roo.Element} el The container element.
15970 * @cfg {String|Roo.Template} tpl The template used by this View
15974 * @cfg {String} dataName the named area of the template to use as the data area
15975 * Works with domtemplates roo-name="name"
15979 * @cfg {String} selectedClass The css class to add to selected nodes
15981 selectedClass : "x-view-selected",
15983 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15988 * @cfg {String} text to display on mask (default Loading)
15992 * @cfg {Boolean} multiSelect Allow multiple selection
15994 multiSelect : false,
15996 * @cfg {Boolean} singleSelect Allow single selection
15998 singleSelect: false,
16001 * @cfg {Boolean} toggleSelect - selecting
16003 toggleSelect : false,
16006 * @cfg {Boolean} tickable - selecting
16011 * Returns the element this view is bound to.
16012 * @return {Roo.Element}
16014 getEl : function(){
16015 return this.wrapEl;
16021 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16023 refresh : function(){
16024 //Roo.log('refresh');
16027 // if we are using something like 'domtemplate', then
16028 // the what gets used is:
16029 // t.applySubtemplate(NAME, data, wrapping data..)
16030 // the outer template then get' applied with
16031 // the store 'extra data'
16032 // and the body get's added to the
16033 // roo-name="data" node?
16034 // <span class='roo-tpl-{name}'></span> ?????
16038 this.clearSelections();
16039 this.el.update("");
16041 var records = this.store.getRange();
16042 if(records.length < 1) {
16044 // is this valid?? = should it render a template??
16046 this.el.update(this.emptyText);
16050 if (this.dataName) {
16051 this.el.update(t.apply(this.store.meta)); //????
16052 el = this.el.child('.roo-tpl-' + this.dataName);
16055 for(var i = 0, len = records.length; i < len; i++){
16056 var data = this.prepareData(records[i].data, i, records[i]);
16057 this.fireEvent("preparedata", this, data, i, records[i]);
16059 var d = Roo.apply({}, data);
16062 Roo.apply(d, {'roo-id' : Roo.id()});
16066 Roo.each(this.parent.item, function(item){
16067 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16070 Roo.apply(d, {'roo-data-checked' : 'checked'});
16074 html[html.length] = Roo.util.Format.trim(
16076 t.applySubtemplate(this.dataName, d, this.store.meta) :
16083 el.update(html.join(""));
16084 this.nodes = el.dom.childNodes;
16085 this.updateIndexes(0);
16090 * Function to override to reformat the data that is sent to
16091 * the template for each node.
16092 * DEPRICATED - use the preparedata event handler.
16093 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16094 * a JSON object for an UpdateManager bound view).
16096 prepareData : function(data, index, record)
16098 this.fireEvent("preparedata", this, data, index, record);
16102 onUpdate : function(ds, record){
16103 // Roo.log('on update');
16104 this.clearSelections();
16105 var index = this.store.indexOf(record);
16106 var n = this.nodes[index];
16107 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16108 n.parentNode.removeChild(n);
16109 this.updateIndexes(index, index);
16115 onAdd : function(ds, records, index)
16117 //Roo.log(['on Add', ds, records, index] );
16118 this.clearSelections();
16119 if(this.nodes.length == 0){
16123 var n = this.nodes[index];
16124 for(var i = 0, len = records.length; i < len; i++){
16125 var d = this.prepareData(records[i].data, i, records[i]);
16127 this.tpl.insertBefore(n, d);
16130 this.tpl.append(this.el, d);
16133 this.updateIndexes(index);
16136 onRemove : function(ds, record, index){
16137 // Roo.log('onRemove');
16138 this.clearSelections();
16139 var el = this.dataName ?
16140 this.el.child('.roo-tpl-' + this.dataName) :
16143 el.dom.removeChild(this.nodes[index]);
16144 this.updateIndexes(index);
16148 * Refresh an individual node.
16149 * @param {Number} index
16151 refreshNode : function(index){
16152 this.onUpdate(this.store, this.store.getAt(index));
16155 updateIndexes : function(startIndex, endIndex){
16156 var ns = this.nodes;
16157 startIndex = startIndex || 0;
16158 endIndex = endIndex || ns.length - 1;
16159 for(var i = startIndex; i <= endIndex; i++){
16160 ns[i].nodeIndex = i;
16165 * Changes the data store this view uses and refresh the view.
16166 * @param {Store} store
16168 setStore : function(store, initial){
16169 if(!initial && this.store){
16170 this.store.un("datachanged", this.refresh);
16171 this.store.un("add", this.onAdd);
16172 this.store.un("remove", this.onRemove);
16173 this.store.un("update", this.onUpdate);
16174 this.store.un("clear", this.refresh);
16175 this.store.un("beforeload", this.onBeforeLoad);
16176 this.store.un("load", this.onLoad);
16177 this.store.un("loadexception", this.onLoad);
16181 store.on("datachanged", this.refresh, this);
16182 store.on("add", this.onAdd, this);
16183 store.on("remove", this.onRemove, this);
16184 store.on("update", this.onUpdate, this);
16185 store.on("clear", this.refresh, this);
16186 store.on("beforeload", this.onBeforeLoad, this);
16187 store.on("load", this.onLoad, this);
16188 store.on("loadexception", this.onLoad, this);
16196 * onbeforeLoad - masks the loading area.
16199 onBeforeLoad : function(store,opts)
16201 //Roo.log('onBeforeLoad');
16203 this.el.update("");
16205 this.el.mask(this.mask ? this.mask : "Loading" );
16207 onLoad : function ()
16214 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16215 * @param {HTMLElement} node
16216 * @return {HTMLElement} The template node
16218 findItemFromChild : function(node){
16219 var el = this.dataName ?
16220 this.el.child('.roo-tpl-' + this.dataName,true) :
16223 if(!node || node.parentNode == el){
16226 var p = node.parentNode;
16227 while(p && p != el){
16228 if(p.parentNode == el){
16237 onClick : function(e){
16238 var item = this.findItemFromChild(e.getTarget());
16240 var index = this.indexOf(item);
16241 if(this.onItemClick(item, index, e) !== false){
16242 this.fireEvent("click", this, index, item, e);
16245 this.clearSelections();
16250 onContextMenu : function(e){
16251 var item = this.findItemFromChild(e.getTarget());
16253 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16258 onDblClick : function(e){
16259 var item = this.findItemFromChild(e.getTarget());
16261 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16265 onItemClick : function(item, index, e)
16267 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16270 if (this.toggleSelect) {
16271 var m = this.isSelected(item) ? 'unselect' : 'select';
16274 _t[m](item, true, false);
16277 if(this.multiSelect || this.singleSelect){
16278 if(this.multiSelect && e.shiftKey && this.lastSelection){
16279 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16281 this.select(item, this.multiSelect && e.ctrlKey);
16282 this.lastSelection = item;
16285 if(!this.tickable){
16286 e.preventDefault();
16294 * Get the number of selected nodes.
16297 getSelectionCount : function(){
16298 return this.selections.length;
16302 * Get the currently selected nodes.
16303 * @return {Array} An array of HTMLElements
16305 getSelectedNodes : function(){
16306 return this.selections;
16310 * Get the indexes of the selected nodes.
16313 getSelectedIndexes : function(){
16314 var indexes = [], s = this.selections;
16315 for(var i = 0, len = s.length; i < len; i++){
16316 indexes.push(s[i].nodeIndex);
16322 * Clear all selections
16323 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16325 clearSelections : function(suppressEvent){
16326 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16327 this.cmp.elements = this.selections;
16328 this.cmp.removeClass(this.selectedClass);
16329 this.selections = [];
16330 if(!suppressEvent){
16331 this.fireEvent("selectionchange", this, this.selections);
16337 * Returns true if the passed node is selected
16338 * @param {HTMLElement/Number} node The node or node index
16339 * @return {Boolean}
16341 isSelected : function(node){
16342 var s = this.selections;
16346 node = this.getNode(node);
16347 return s.indexOf(node) !== -1;
16352 * @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
16353 * @param {Boolean} keepExisting (optional) true to keep existing selections
16354 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16356 select : function(nodeInfo, keepExisting, suppressEvent){
16357 if(nodeInfo instanceof Array){
16359 this.clearSelections(true);
16361 for(var i = 0, len = nodeInfo.length; i < len; i++){
16362 this.select(nodeInfo[i], true, true);
16366 var node = this.getNode(nodeInfo);
16367 if(!node || this.isSelected(node)){
16368 return; // already selected.
16371 this.clearSelections(true);
16374 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16375 Roo.fly(node).addClass(this.selectedClass);
16376 this.selections.push(node);
16377 if(!suppressEvent){
16378 this.fireEvent("selectionchange", this, this.selections);
16386 * @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
16387 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16388 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16390 unselect : function(nodeInfo, keepExisting, suppressEvent)
16392 if(nodeInfo instanceof Array){
16393 Roo.each(this.selections, function(s) {
16394 this.unselect(s, nodeInfo);
16398 var node = this.getNode(nodeInfo);
16399 if(!node || !this.isSelected(node)){
16400 //Roo.log("not selected");
16401 return; // not selected.
16405 Roo.each(this.selections, function(s) {
16407 Roo.fly(node).removeClass(this.selectedClass);
16414 this.selections= ns;
16415 this.fireEvent("selectionchange", this, this.selections);
16419 * Gets a template node.
16420 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16421 * @return {HTMLElement} The node or null if it wasn't found
16423 getNode : function(nodeInfo){
16424 if(typeof nodeInfo == "string"){
16425 return document.getElementById(nodeInfo);
16426 }else if(typeof nodeInfo == "number"){
16427 return this.nodes[nodeInfo];
16433 * Gets a range template nodes.
16434 * @param {Number} startIndex
16435 * @param {Number} endIndex
16436 * @return {Array} An array of nodes
16438 getNodes : function(start, end){
16439 var ns = this.nodes;
16440 start = start || 0;
16441 end = typeof end == "undefined" ? ns.length - 1 : end;
16444 for(var i = start; i <= end; i++){
16448 for(var i = start; i >= end; i--){
16456 * Finds the index of the passed node
16457 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16458 * @return {Number} The index of the node or -1
16460 indexOf : function(node){
16461 node = this.getNode(node);
16462 if(typeof node.nodeIndex == "number"){
16463 return node.nodeIndex;
16465 var ns = this.nodes;
16466 for(var i = 0, len = ns.length; i < len; i++){
16477 * based on jquery fullcalendar
16481 Roo.bootstrap = Roo.bootstrap || {};
16483 * @class Roo.bootstrap.Calendar
16484 * @extends Roo.bootstrap.Component
16485 * Bootstrap Calendar class
16486 * @cfg {Boolean} loadMask (true|false) default false
16487 * @cfg {Object} header generate the user specific header of the calendar, default false
16490 * Create a new Container
16491 * @param {Object} config The config object
16496 Roo.bootstrap.Calendar = function(config){
16497 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16501 * Fires when a date is selected
16502 * @param {DatePicker} this
16503 * @param {Date} date The selected date
16507 * @event monthchange
16508 * Fires when the displayed month changes
16509 * @param {DatePicker} this
16510 * @param {Date} date The selected month
16512 'monthchange': true,
16514 * @event evententer
16515 * Fires when mouse over an event
16516 * @param {Calendar} this
16517 * @param {event} Event
16519 'evententer': true,
16521 * @event eventleave
16522 * Fires when the mouse leaves an
16523 * @param {Calendar} this
16526 'eventleave': true,
16528 * @event eventclick
16529 * Fires when the mouse click an
16530 * @param {Calendar} this
16539 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16542 * @cfg {Number} startDay
16543 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16551 getAutoCreate : function(){
16554 var fc_button = function(name, corner, style, content ) {
16555 return Roo.apply({},{
16557 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16559 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16562 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16573 style : 'width:100%',
16580 cls : 'fc-header-left',
16582 fc_button('prev', 'left', 'arrow', '‹' ),
16583 fc_button('next', 'right', 'arrow', '›' ),
16584 { tag: 'span', cls: 'fc-header-space' },
16585 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16593 cls : 'fc-header-center',
16597 cls: 'fc-header-title',
16600 html : 'month / year'
16608 cls : 'fc-header-right',
16610 /* fc_button('month', 'left', '', 'month' ),
16611 fc_button('week', '', '', 'week' ),
16612 fc_button('day', 'right', '', 'day' )
16624 header = this.header;
16627 var cal_heads = function() {
16629 // fixme - handle this.
16631 for (var i =0; i < Date.dayNames.length; i++) {
16632 var d = Date.dayNames[i];
16635 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16636 html : d.substring(0,3)
16640 ret[0].cls += ' fc-first';
16641 ret[6].cls += ' fc-last';
16644 var cal_cell = function(n) {
16647 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16652 cls: 'fc-day-number',
16656 cls: 'fc-day-content',
16660 style: 'position: relative;' // height: 17px;
16672 var cal_rows = function() {
16675 for (var r = 0; r < 6; r++) {
16682 for (var i =0; i < Date.dayNames.length; i++) {
16683 var d = Date.dayNames[i];
16684 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16687 row.cn[0].cls+=' fc-first';
16688 row.cn[0].cn[0].style = 'min-height:90px';
16689 row.cn[6].cls+=' fc-last';
16693 ret[0].cls += ' fc-first';
16694 ret[4].cls += ' fc-prev-last';
16695 ret[5].cls += ' fc-last';
16702 cls: 'fc-border-separate',
16703 style : 'width:100%',
16711 cls : 'fc-first fc-last',
16729 cls : 'fc-content',
16730 style : "position: relative;",
16733 cls : 'fc-view fc-view-month fc-grid',
16734 style : 'position: relative',
16735 unselectable : 'on',
16738 cls : 'fc-event-container',
16739 style : 'position:absolute;z-index:8;top:0;left:0;'
16757 initEvents : function()
16760 throw "can not find store for calendar";
16766 style: "text-align:center",
16770 style: "background-color:white;width:50%;margin:250 auto",
16774 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16785 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16787 var size = this.el.select('.fc-content', true).first().getSize();
16788 this.maskEl.setSize(size.width, size.height);
16789 this.maskEl.enableDisplayMode("block");
16790 if(!this.loadMask){
16791 this.maskEl.hide();
16794 this.store = Roo.factory(this.store, Roo.data);
16795 this.store.on('load', this.onLoad, this);
16796 this.store.on('beforeload', this.onBeforeLoad, this);
16800 this.cells = this.el.select('.fc-day',true);
16801 //Roo.log(this.cells);
16802 this.textNodes = this.el.query('.fc-day-number');
16803 this.cells.addClassOnOver('fc-state-hover');
16805 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16806 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16807 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16808 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16810 this.on('monthchange', this.onMonthChange, this);
16812 this.update(new Date().clearTime());
16815 resize : function() {
16816 var sz = this.el.getSize();
16818 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16819 this.el.select('.fc-day-content div',true).setHeight(34);
16824 showPrevMonth : function(e){
16825 this.update(this.activeDate.add("mo", -1));
16827 showToday : function(e){
16828 this.update(new Date().clearTime());
16831 showNextMonth : function(e){
16832 this.update(this.activeDate.add("mo", 1));
16836 showPrevYear : function(){
16837 this.update(this.activeDate.add("y", -1));
16841 showNextYear : function(){
16842 this.update(this.activeDate.add("y", 1));
16847 update : function(date)
16849 var vd = this.activeDate;
16850 this.activeDate = date;
16851 // if(vd && this.el){
16852 // var t = date.getTime();
16853 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16854 // Roo.log('using add remove');
16856 // this.fireEvent('monthchange', this, date);
16858 // this.cells.removeClass("fc-state-highlight");
16859 // this.cells.each(function(c){
16860 // if(c.dateValue == t){
16861 // c.addClass("fc-state-highlight");
16862 // setTimeout(function(){
16863 // try{c.dom.firstChild.focus();}catch(e){}
16873 var days = date.getDaysInMonth();
16875 var firstOfMonth = date.getFirstDateOfMonth();
16876 var startingPos = firstOfMonth.getDay()-this.startDay;
16878 if(startingPos < this.startDay){
16882 var pm = date.add(Date.MONTH, -1);
16883 var prevStart = pm.getDaysInMonth()-startingPos;
16885 this.cells = this.el.select('.fc-day',true);
16886 this.textNodes = this.el.query('.fc-day-number');
16887 this.cells.addClassOnOver('fc-state-hover');
16889 var cells = this.cells.elements;
16890 var textEls = this.textNodes;
16892 Roo.each(cells, function(cell){
16893 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16896 days += startingPos;
16898 // convert everything to numbers so it's fast
16899 var day = 86400000;
16900 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16903 //Roo.log(prevStart);
16905 var today = new Date().clearTime().getTime();
16906 var sel = date.clearTime().getTime();
16907 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16908 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16909 var ddMatch = this.disabledDatesRE;
16910 var ddText = this.disabledDatesText;
16911 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16912 var ddaysText = this.disabledDaysText;
16913 var format = this.format;
16915 var setCellClass = function(cal, cell){
16919 //Roo.log('set Cell Class');
16921 var t = d.getTime();
16925 cell.dateValue = t;
16927 cell.className += " fc-today";
16928 cell.className += " fc-state-highlight";
16929 cell.title = cal.todayText;
16932 // disable highlight in other month..
16933 //cell.className += " fc-state-highlight";
16938 cell.className = " fc-state-disabled";
16939 cell.title = cal.minText;
16943 cell.className = " fc-state-disabled";
16944 cell.title = cal.maxText;
16948 if(ddays.indexOf(d.getDay()) != -1){
16949 cell.title = ddaysText;
16950 cell.className = " fc-state-disabled";
16953 if(ddMatch && format){
16954 var fvalue = d.dateFormat(format);
16955 if(ddMatch.test(fvalue)){
16956 cell.title = ddText.replace("%0", fvalue);
16957 cell.className = " fc-state-disabled";
16961 if (!cell.initialClassName) {
16962 cell.initialClassName = cell.dom.className;
16965 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16970 for(; i < startingPos; i++) {
16971 textEls[i].innerHTML = (++prevStart);
16972 d.setDate(d.getDate()+1);
16974 cells[i].className = "fc-past fc-other-month";
16975 setCellClass(this, cells[i]);
16980 for(; i < days; i++){
16981 intDay = i - startingPos + 1;
16982 textEls[i].innerHTML = (intDay);
16983 d.setDate(d.getDate()+1);
16985 cells[i].className = ''; // "x-date-active";
16986 setCellClass(this, cells[i]);
16990 for(; i < 42; i++) {
16991 textEls[i].innerHTML = (++extraDays);
16992 d.setDate(d.getDate()+1);
16994 cells[i].className = "fc-future fc-other-month";
16995 setCellClass(this, cells[i]);
16998 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17000 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17002 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17003 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17005 if(totalRows != 6){
17006 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17007 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17010 this.fireEvent('monthchange', this, date);
17014 if(!this.internalRender){
17015 var main = this.el.dom.firstChild;
17016 var w = main.offsetWidth;
17017 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17018 Roo.fly(main).setWidth(w);
17019 this.internalRender = true;
17020 // opera does not respect the auto grow header center column
17021 // then, after it gets a width opera refuses to recalculate
17022 // without a second pass
17023 if(Roo.isOpera && !this.secondPass){
17024 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17025 this.secondPass = true;
17026 this.update.defer(10, this, [date]);
17033 findCell : function(dt) {
17034 dt = dt.clearTime().getTime();
17036 this.cells.each(function(c){
17037 //Roo.log("check " +c.dateValue + '?=' + dt);
17038 if(c.dateValue == dt){
17048 findCells : function(ev) {
17049 var s = ev.start.clone().clearTime().getTime();
17051 var e= ev.end.clone().clearTime().getTime();
17054 this.cells.each(function(c){
17055 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17057 if(c.dateValue > e){
17060 if(c.dateValue < s){
17069 // findBestRow: function(cells)
17073 // for (var i =0 ; i < cells.length;i++) {
17074 // ret = Math.max(cells[i].rows || 0,ret);
17081 addItem : function(ev)
17083 // look for vertical location slot in
17084 var cells = this.findCells(ev);
17086 // ev.row = this.findBestRow(cells);
17088 // work out the location.
17092 for(var i =0; i < cells.length; i++) {
17094 cells[i].row = cells[0].row;
17097 cells[i].row = cells[i].row + 1;
17107 if (crow.start.getY() == cells[i].getY()) {
17109 crow.end = cells[i];
17126 cells[0].events.push(ev);
17128 this.calevents.push(ev);
17131 clearEvents: function() {
17133 if(!this.calevents){
17137 Roo.each(this.cells.elements, function(c){
17143 Roo.each(this.calevents, function(e) {
17144 Roo.each(e.els, function(el) {
17145 el.un('mouseenter' ,this.onEventEnter, this);
17146 el.un('mouseleave' ,this.onEventLeave, this);
17151 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17157 renderEvents: function()
17161 this.cells.each(function(c) {
17170 if(c.row != c.events.length){
17171 r = 4 - (4 - (c.row - c.events.length));
17174 c.events = ev.slice(0, r);
17175 c.more = ev.slice(r);
17177 if(c.more.length && c.more.length == 1){
17178 c.events.push(c.more.pop());
17181 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17185 this.cells.each(function(c) {
17187 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17190 for (var e = 0; e < c.events.length; e++){
17191 var ev = c.events[e];
17192 var rows = ev.rows;
17194 for(var i = 0; i < rows.length; i++) {
17196 // how many rows should it span..
17199 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17200 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17202 unselectable : "on",
17205 cls: 'fc-event-inner',
17209 // cls: 'fc-event-time',
17210 // html : cells.length > 1 ? '' : ev.time
17214 cls: 'fc-event-title',
17215 html : String.format('{0}', ev.title)
17222 cls: 'ui-resizable-handle ui-resizable-e',
17223 html : '  '
17230 cfg.cls += ' fc-event-start';
17232 if ((i+1) == rows.length) {
17233 cfg.cls += ' fc-event-end';
17236 var ctr = _this.el.select('.fc-event-container',true).first();
17237 var cg = ctr.createChild(cfg);
17239 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17240 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17242 var r = (c.more.length) ? 1 : 0;
17243 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17244 cg.setWidth(ebox.right - sbox.x -2);
17246 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17247 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17248 cg.on('click', _this.onEventClick, _this, ev);
17259 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17260 style : 'position: absolute',
17261 unselectable : "on",
17264 cls: 'fc-event-inner',
17268 cls: 'fc-event-title',
17276 cls: 'ui-resizable-handle ui-resizable-e',
17277 html : '  '
17283 var ctr = _this.el.select('.fc-event-container',true).first();
17284 var cg = ctr.createChild(cfg);
17286 var sbox = c.select('.fc-day-content',true).first().getBox();
17287 var ebox = c.select('.fc-day-content',true).first().getBox();
17289 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17290 cg.setWidth(ebox.right - sbox.x -2);
17292 cg.on('click', _this.onMoreEventClick, _this, c.more);
17302 onEventEnter: function (e, el,event,d) {
17303 this.fireEvent('evententer', this, el, event);
17306 onEventLeave: function (e, el,event,d) {
17307 this.fireEvent('eventleave', this, el, event);
17310 onEventClick: function (e, el,event,d) {
17311 this.fireEvent('eventclick', this, el, event);
17314 onMonthChange: function () {
17318 onMoreEventClick: function(e, el, more)
17322 this.calpopover.placement = 'right';
17323 this.calpopover.setTitle('More');
17325 this.calpopover.setContent('');
17327 var ctr = this.calpopover.el.select('.popover-content', true).first();
17329 Roo.each(more, function(m){
17331 cls : 'fc-event-hori fc-event-draggable',
17334 var cg = ctr.createChild(cfg);
17336 cg.on('click', _this.onEventClick, _this, m);
17339 this.calpopover.show(el);
17344 onLoad: function ()
17346 this.calevents = [];
17349 if(this.store.getCount() > 0){
17350 this.store.data.each(function(d){
17353 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17354 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17355 time : d.data.start_time,
17356 title : d.data.title,
17357 description : d.data.description,
17358 venue : d.data.venue
17363 this.renderEvents();
17365 if(this.calevents.length && this.loadMask){
17366 this.maskEl.hide();
17370 onBeforeLoad: function()
17372 this.clearEvents();
17374 this.maskEl.show();
17388 * @class Roo.bootstrap.Popover
17389 * @extends Roo.bootstrap.Component
17390 * Bootstrap Popover class
17391 * @cfg {String} html contents of the popover (or false to use children..)
17392 * @cfg {String} title of popover (or false to hide)
17393 * @cfg {String} placement how it is placed
17394 * @cfg {String} trigger click || hover (or false to trigger manually)
17395 * @cfg {String} over what (parent or false to trigger manually.)
17396 * @cfg {Number} delay - delay before showing
17399 * Create a new Popover
17400 * @param {Object} config The config object
17403 Roo.bootstrap.Popover = function(config){
17404 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17410 * After the popover show
17412 * @param {Roo.bootstrap.Popover} this
17417 * After the popover hide
17419 * @param {Roo.bootstrap.Popover} this
17425 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17427 title: 'Fill in a title',
17430 placement : 'right',
17431 trigger : 'hover', // hover
17437 can_build_overlaid : false,
17439 getChildContainer : function()
17441 return this.el.select('.popover-content',true).first();
17444 getAutoCreate : function(){
17447 cls : 'popover roo-dynamic',
17448 style: 'display:block',
17454 cls : 'popover-inner',
17458 cls: 'popover-title',
17462 cls : 'popover-content',
17473 setTitle: function(str)
17476 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17478 setContent: function(str)
17481 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17483 // as it get's added to the bottom of the page.
17484 onRender : function(ct, position)
17486 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17488 var cfg = Roo.apply({}, this.getAutoCreate());
17492 cfg.cls += ' ' + this.cls;
17495 cfg.style = this.style;
17497 //Roo.log("adding to ");
17498 this.el = Roo.get(document.body).createChild(cfg, position);
17499 // Roo.log(this.el);
17504 initEvents : function()
17506 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17507 this.el.enableDisplayMode('block');
17509 if (this.over === false) {
17512 if (this.triggers === false) {
17515 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17516 var triggers = this.trigger ? this.trigger.split(' ') : [];
17517 Roo.each(triggers, function(trigger) {
17519 if (trigger == 'click') {
17520 on_el.on('click', this.toggle, this);
17521 } else if (trigger != 'manual') {
17522 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17523 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17525 on_el.on(eventIn ,this.enter, this);
17526 on_el.on(eventOut, this.leave, this);
17537 toggle : function () {
17538 this.hoverState == 'in' ? this.leave() : this.enter();
17541 enter : function () {
17543 clearTimeout(this.timeout);
17545 this.hoverState = 'in';
17547 if (!this.delay || !this.delay.show) {
17552 this.timeout = setTimeout(function () {
17553 if (_t.hoverState == 'in') {
17556 }, this.delay.show)
17559 leave : function() {
17560 clearTimeout(this.timeout);
17562 this.hoverState = 'out';
17564 if (!this.delay || !this.delay.hide) {
17569 this.timeout = setTimeout(function () {
17570 if (_t.hoverState == 'out') {
17573 }, this.delay.hide)
17576 show : function (on_el)
17579 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17583 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17584 if (this.html !== false) {
17585 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17587 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17588 if (!this.title.length) {
17589 this.el.select('.popover-title',true).hide();
17592 var placement = typeof this.placement == 'function' ?
17593 this.placement.call(this, this.el, on_el) :
17596 var autoToken = /\s?auto?\s?/i;
17597 var autoPlace = autoToken.test(placement);
17599 placement = placement.replace(autoToken, '') || 'top';
17603 //this.el.setXY([0,0]);
17605 this.el.dom.style.display='block';
17606 this.el.addClass(placement);
17608 //this.el.appendTo(on_el);
17610 var p = this.getPosition();
17611 var box = this.el.getBox();
17616 var align = Roo.bootstrap.Popover.alignment[placement];
17619 this.el.alignTo(on_el, align[0],align[1]);
17620 //var arrow = this.el.select('.arrow',true).first();
17621 //arrow.set(align[2],
17623 this.el.addClass('in');
17626 if (this.el.hasClass('fade')) {
17630 this.hoverState = 'in';
17632 this.fireEvent('show', this);
17637 this.el.setXY([0,0]);
17638 this.el.removeClass('in');
17640 this.hoverState = null;
17642 this.fireEvent('hide', this);
17647 Roo.bootstrap.Popover.alignment = {
17648 'left' : ['r-l', [-10,0], 'right'],
17649 'right' : ['l-r', [10,0], 'left'],
17650 'bottom' : ['t-b', [0,10], 'top'],
17651 'top' : [ 'b-t', [0,-10], 'bottom']
17662 * @class Roo.bootstrap.Progress
17663 * @extends Roo.bootstrap.Component
17664 * Bootstrap Progress class
17665 * @cfg {Boolean} striped striped of the progress bar
17666 * @cfg {Boolean} active animated of the progress bar
17670 * Create a new Progress
17671 * @param {Object} config The config object
17674 Roo.bootstrap.Progress = function(config){
17675 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17678 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17683 getAutoCreate : function(){
17691 cfg.cls += ' progress-striped';
17695 cfg.cls += ' active';
17714 * @class Roo.bootstrap.ProgressBar
17715 * @extends Roo.bootstrap.Component
17716 * Bootstrap ProgressBar class
17717 * @cfg {Number} aria_valuenow aria-value now
17718 * @cfg {Number} aria_valuemin aria-value min
17719 * @cfg {Number} aria_valuemax aria-value max
17720 * @cfg {String} label label for the progress bar
17721 * @cfg {String} panel (success | info | warning | danger )
17722 * @cfg {String} role role of the progress bar
17723 * @cfg {String} sr_only text
17727 * Create a new ProgressBar
17728 * @param {Object} config The config object
17731 Roo.bootstrap.ProgressBar = function(config){
17732 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17735 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17739 aria_valuemax : 100,
17745 getAutoCreate : function()
17750 cls: 'progress-bar',
17751 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17763 cfg.role = this.role;
17766 if(this.aria_valuenow){
17767 cfg['aria-valuenow'] = this.aria_valuenow;
17770 if(this.aria_valuemin){
17771 cfg['aria-valuemin'] = this.aria_valuemin;
17774 if(this.aria_valuemax){
17775 cfg['aria-valuemax'] = this.aria_valuemax;
17778 if(this.label && !this.sr_only){
17779 cfg.html = this.label;
17783 cfg.cls += ' progress-bar-' + this.panel;
17789 update : function(aria_valuenow)
17791 this.aria_valuenow = aria_valuenow;
17793 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17808 * @class Roo.bootstrap.TabGroup
17809 * @extends Roo.bootstrap.Column
17810 * Bootstrap Column class
17811 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17812 * @cfg {Boolean} carousel true to make the group behave like a carousel
17813 * @cfg {Boolean} bullets show bullets for the panels
17814 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17815 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17816 * @cfg {Boolean} showarrow (true|false) show arrow default true
17819 * Create a new TabGroup
17820 * @param {Object} config The config object
17823 Roo.bootstrap.TabGroup = function(config){
17824 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17826 this.navId = Roo.id();
17829 Roo.bootstrap.TabGroup.register(this);
17833 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17836 transition : false,
17841 slideOnTouch : false,
17844 getAutoCreate : function()
17846 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17848 cfg.cls += ' tab-content';
17850 if (this.carousel) {
17851 cfg.cls += ' carousel slide';
17854 cls : 'carousel-inner',
17858 if(this.bullets && !Roo.isTouch){
17861 cls : 'carousel-bullets',
17865 if(this.bullets_cls){
17866 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17873 cfg.cn[0].cn.push(bullets);
17876 if(this.showarrow){
17877 cfg.cn[0].cn.push({
17879 class : 'carousel-arrow',
17883 class : 'carousel-prev',
17887 class : 'fa fa-chevron-left'
17893 class : 'carousel-next',
17897 class : 'fa fa-chevron-right'
17910 initEvents: function()
17912 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17913 // this.el.on("touchstart", this.onTouchStart, this);
17916 if(this.autoslide){
17919 this.slideFn = window.setInterval(function() {
17920 _this.showPanelNext();
17924 if(this.showarrow){
17925 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17926 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17932 // onTouchStart : function(e, el, o)
17934 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17938 // this.showPanelNext();
17942 getChildContainer : function()
17944 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17948 * register a Navigation item
17949 * @param {Roo.bootstrap.NavItem} the navitem to add
17951 register : function(item)
17953 this.tabs.push( item);
17954 item.navId = this.navId; // not really needed..
17959 getActivePanel : function()
17962 Roo.each(this.tabs, function(t) {
17972 getPanelByName : function(n)
17975 Roo.each(this.tabs, function(t) {
17976 if (t.tabId == n) {
17984 indexOfPanel : function(p)
17987 Roo.each(this.tabs, function(t,i) {
17988 if (t.tabId == p.tabId) {
17997 * show a specific panel
17998 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17999 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18001 showPanel : function (pan)
18003 if(this.transition || typeof(pan) == 'undefined'){
18004 Roo.log("waiting for the transitionend");
18008 if (typeof(pan) == 'number') {
18009 pan = this.tabs[pan];
18012 if (typeof(pan) == 'string') {
18013 pan = this.getPanelByName(pan);
18016 var cur = this.getActivePanel();
18019 Roo.log('pan or acitve pan is undefined');
18023 if (pan.tabId == this.getActivePanel().tabId) {
18027 if (false === cur.fireEvent('beforedeactivate')) {
18031 if(this.bullets > 0 && !Roo.isTouch){
18032 this.setActiveBullet(this.indexOfPanel(pan));
18035 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18037 this.transition = true;
18038 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18039 var lr = dir == 'next' ? 'left' : 'right';
18040 pan.el.addClass(dir); // or prev
18041 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18042 cur.el.addClass(lr); // or right
18043 pan.el.addClass(lr);
18046 cur.el.on('transitionend', function() {
18047 Roo.log("trans end?");
18049 pan.el.removeClass([lr,dir]);
18050 pan.setActive(true);
18052 cur.el.removeClass([lr]);
18053 cur.setActive(false);
18055 _this.transition = false;
18057 }, this, { single: true } );
18062 cur.setActive(false);
18063 pan.setActive(true);
18068 showPanelNext : function()
18070 var i = this.indexOfPanel(this.getActivePanel());
18072 if (i >= this.tabs.length - 1 && !this.autoslide) {
18076 if (i >= this.tabs.length - 1 && this.autoslide) {
18080 this.showPanel(this.tabs[i+1]);
18083 showPanelPrev : function()
18085 var i = this.indexOfPanel(this.getActivePanel());
18087 if (i < 1 && !this.autoslide) {
18091 if (i < 1 && this.autoslide) {
18092 i = this.tabs.length;
18095 this.showPanel(this.tabs[i-1]);
18099 addBullet: function()
18101 if(!this.bullets || Roo.isTouch){
18104 var ctr = this.el.select('.carousel-bullets',true).first();
18105 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18106 var bullet = ctr.createChild({
18107 cls : 'bullet bullet-' + i
18108 },ctr.dom.lastChild);
18113 bullet.on('click', (function(e, el, o, ii, t){
18115 e.preventDefault();
18117 this.showPanel(ii);
18119 if(this.autoslide && this.slideFn){
18120 clearInterval(this.slideFn);
18121 this.slideFn = window.setInterval(function() {
18122 _this.showPanelNext();
18126 }).createDelegate(this, [i, bullet], true));
18131 setActiveBullet : function(i)
18137 Roo.each(this.el.select('.bullet', true).elements, function(el){
18138 el.removeClass('selected');
18141 var bullet = this.el.select('.bullet-' + i, true).first();
18147 bullet.addClass('selected');
18158 Roo.apply(Roo.bootstrap.TabGroup, {
18162 * register a Navigation Group
18163 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18165 register : function(navgrp)
18167 this.groups[navgrp.navId] = navgrp;
18171 * fetch a Navigation Group based on the navigation ID
18172 * if one does not exist , it will get created.
18173 * @param {string} the navgroup to add
18174 * @returns {Roo.bootstrap.NavGroup} the navgroup
18176 get: function(navId) {
18177 if (typeof(this.groups[navId]) == 'undefined') {
18178 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18180 return this.groups[navId] ;
18195 * @class Roo.bootstrap.TabPanel
18196 * @extends Roo.bootstrap.Component
18197 * Bootstrap TabPanel class
18198 * @cfg {Boolean} active panel active
18199 * @cfg {String} html panel content
18200 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18201 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18202 * @cfg {String} href click to link..
18206 * Create a new TabPanel
18207 * @param {Object} config The config object
18210 Roo.bootstrap.TabPanel = function(config){
18211 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18215 * Fires when the active status changes
18216 * @param {Roo.bootstrap.TabPanel} this
18217 * @param {Boolean} state the new state
18222 * @event beforedeactivate
18223 * Fires before a tab is de-activated - can be used to do validation on a form.
18224 * @param {Roo.bootstrap.TabPanel} this
18225 * @return {Boolean} false if there is an error
18228 'beforedeactivate': true
18231 this.tabId = this.tabId || Roo.id();
18235 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18243 getAutoCreate : function(){
18246 // item is needed for carousel - not sure if it has any effect otherwise
18247 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18248 html: this.html || ''
18252 cfg.cls += ' active';
18256 cfg.tabId = this.tabId;
18263 initEvents: function()
18265 var p = this.parent();
18267 this.navId = this.navId || p.navId;
18269 if (typeof(this.navId) != 'undefined') {
18270 // not really needed.. but just in case.. parent should be a NavGroup.
18271 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18275 var i = tg.tabs.length - 1;
18277 if(this.active && tg.bullets > 0 && i < tg.bullets){
18278 tg.setActiveBullet(i);
18282 this.el.on('click', this.onClick, this);
18285 this.el.on("touchstart", this.onTouchStart, this);
18286 this.el.on("touchmove", this.onTouchMove, this);
18287 this.el.on("touchend", this.onTouchEnd, this);
18292 onRender : function(ct, position)
18294 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18297 setActive : function(state)
18299 Roo.log("panel - set active " + this.tabId + "=" + state);
18301 this.active = state;
18303 this.el.removeClass('active');
18305 } else if (!this.el.hasClass('active')) {
18306 this.el.addClass('active');
18309 this.fireEvent('changed', this, state);
18312 onClick : function(e)
18314 e.preventDefault();
18316 if(!this.href.length){
18320 window.location.href = this.href;
18329 onTouchStart : function(e)
18331 this.swiping = false;
18333 this.startX = e.browserEvent.touches[0].clientX;
18334 this.startY = e.browserEvent.touches[0].clientY;
18337 onTouchMove : function(e)
18339 this.swiping = true;
18341 this.endX = e.browserEvent.touches[0].clientX;
18342 this.endY = e.browserEvent.touches[0].clientY;
18345 onTouchEnd : function(e)
18352 var tabGroup = this.parent();
18354 if(this.endX > this.startX){ // swiping right
18355 tabGroup.showPanelPrev();
18359 if(this.startX > this.endX){ // swiping left
18360 tabGroup.showPanelNext();
18379 * @class Roo.bootstrap.DateField
18380 * @extends Roo.bootstrap.Input
18381 * Bootstrap DateField class
18382 * @cfg {Number} weekStart default 0
18383 * @cfg {String} viewMode default empty, (months|years)
18384 * @cfg {String} minViewMode default empty, (months|years)
18385 * @cfg {Number} startDate default -Infinity
18386 * @cfg {Number} endDate default Infinity
18387 * @cfg {Boolean} todayHighlight default false
18388 * @cfg {Boolean} todayBtn default false
18389 * @cfg {Boolean} calendarWeeks default false
18390 * @cfg {Object} daysOfWeekDisabled default empty
18391 * @cfg {Boolean} singleMode default false (true | false)
18393 * @cfg {Boolean} keyboardNavigation default true
18394 * @cfg {String} language default en
18397 * Create a new DateField
18398 * @param {Object} config The config object
18401 Roo.bootstrap.DateField = function(config){
18402 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18406 * Fires when this field show.
18407 * @param {Roo.bootstrap.DateField} this
18408 * @param {Mixed} date The date value
18413 * Fires when this field hide.
18414 * @param {Roo.bootstrap.DateField} this
18415 * @param {Mixed} date The date value
18420 * Fires when select a date.
18421 * @param {Roo.bootstrap.DateField} this
18422 * @param {Mixed} date The date value
18426 * @event beforeselect
18427 * Fires when before select a date.
18428 * @param {Roo.bootstrap.DateField} this
18429 * @param {Mixed} date The date value
18431 beforeselect : true
18435 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18438 * @cfg {String} format
18439 * The default date format string which can be overriden for localization support. The format must be
18440 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18444 * @cfg {String} altFormats
18445 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18446 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18448 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18456 todayHighlight : false,
18462 keyboardNavigation: true,
18464 calendarWeeks: false,
18466 startDate: -Infinity,
18470 daysOfWeekDisabled: [],
18474 singleMode : false,
18476 UTCDate: function()
18478 return new Date(Date.UTC.apply(Date, arguments));
18481 UTCToday: function()
18483 var today = new Date();
18484 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18487 getDate: function() {
18488 var d = this.getUTCDate();
18489 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18492 getUTCDate: function() {
18496 setDate: function(d) {
18497 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18500 setUTCDate: function(d) {
18502 this.setValue(this.formatDate(this.date));
18505 onRender: function(ct, position)
18508 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18510 this.language = this.language || 'en';
18511 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18512 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18514 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18515 this.format = this.format || 'm/d/y';
18516 this.isInline = false;
18517 this.isInput = true;
18518 this.component = this.el.select('.add-on', true).first() || false;
18519 this.component = (this.component && this.component.length === 0) ? false : this.component;
18520 this.hasInput = this.component && this.inputEl().length;
18522 if (typeof(this.minViewMode === 'string')) {
18523 switch (this.minViewMode) {
18525 this.minViewMode = 1;
18528 this.minViewMode = 2;
18531 this.minViewMode = 0;
18536 if (typeof(this.viewMode === 'string')) {
18537 switch (this.viewMode) {
18550 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18552 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18554 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18556 this.picker().on('mousedown', this.onMousedown, this);
18557 this.picker().on('click', this.onClick, this);
18559 this.picker().addClass('datepicker-dropdown');
18561 this.startViewMode = this.viewMode;
18563 if(this.singleMode){
18564 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18565 v.setVisibilityMode(Roo.Element.DISPLAY);
18569 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18570 v.setStyle('width', '189px');
18574 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18575 if(!this.calendarWeeks){
18580 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18581 v.attr('colspan', function(i, val){
18582 return parseInt(val) + 1;
18587 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18589 this.setStartDate(this.startDate);
18590 this.setEndDate(this.endDate);
18592 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18599 if(this.isInline) {
18604 picker : function()
18606 return this.pickerEl;
18607 // return this.el.select('.datepicker', true).first();
18610 fillDow: function()
18612 var dowCnt = this.weekStart;
18621 if(this.calendarWeeks){
18629 while (dowCnt < this.weekStart + 7) {
18633 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18637 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18640 fillMonths: function()
18643 var months = this.picker().select('>.datepicker-months td', true).first();
18645 months.dom.innerHTML = '';
18651 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18654 months.createChild(month);
18661 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;
18663 if (this.date < this.startDate) {
18664 this.viewDate = new Date(this.startDate);
18665 } else if (this.date > this.endDate) {
18666 this.viewDate = new Date(this.endDate);
18668 this.viewDate = new Date(this.date);
18676 var d = new Date(this.viewDate),
18677 year = d.getUTCFullYear(),
18678 month = d.getUTCMonth(),
18679 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18680 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18681 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18682 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18683 currentDate = this.date && this.date.valueOf(),
18684 today = this.UTCToday();
18686 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18688 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18690 // this.picker.select('>tfoot th.today').
18691 // .text(dates[this.language].today)
18692 // .toggle(this.todayBtn !== false);
18694 this.updateNavArrows();
18697 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18699 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18701 prevMonth.setUTCDate(day);
18703 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18705 var nextMonth = new Date(prevMonth);
18707 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18709 nextMonth = nextMonth.valueOf();
18711 var fillMonths = false;
18713 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18715 while(prevMonth.valueOf() <= nextMonth) {
18718 if (prevMonth.getUTCDay() === this.weekStart) {
18720 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18728 if(this.calendarWeeks){
18729 // ISO 8601: First week contains first thursday.
18730 // ISO also states week starts on Monday, but we can be more abstract here.
18732 // Start of current week: based on weekstart/current date
18733 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18734 // Thursday of this week
18735 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18736 // First Thursday of year, year from thursday
18737 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18738 // Calendar week: ms between thursdays, div ms per day, div 7 days
18739 calWeek = (th - yth) / 864e5 / 7 + 1;
18741 fillMonths.cn.push({
18749 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18751 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18754 if (this.todayHighlight &&
18755 prevMonth.getUTCFullYear() == today.getFullYear() &&
18756 prevMonth.getUTCMonth() == today.getMonth() &&
18757 prevMonth.getUTCDate() == today.getDate()) {
18758 clsName += ' today';
18761 if (currentDate && prevMonth.valueOf() === currentDate) {
18762 clsName += ' active';
18765 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18766 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18767 clsName += ' disabled';
18770 fillMonths.cn.push({
18772 cls: 'day ' + clsName,
18773 html: prevMonth.getDate()
18776 prevMonth.setDate(prevMonth.getDate()+1);
18779 var currentYear = this.date && this.date.getUTCFullYear();
18780 var currentMonth = this.date && this.date.getUTCMonth();
18782 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18784 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18785 v.removeClass('active');
18787 if(currentYear === year && k === currentMonth){
18788 v.addClass('active');
18791 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18792 v.addClass('disabled');
18798 year = parseInt(year/10, 10) * 10;
18800 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18802 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18805 for (var i = -1; i < 11; i++) {
18806 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18808 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18816 showMode: function(dir)
18819 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18822 Roo.each(this.picker().select('>div',true).elements, function(v){
18823 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18826 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18831 if(this.isInline) {
18835 this.picker().removeClass(['bottom', 'top']);
18837 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18839 * place to the top of element!
18843 this.picker().addClass('top');
18844 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18849 this.picker().addClass('bottom');
18851 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18854 parseDate : function(value)
18856 if(!value || value instanceof Date){
18859 var v = Date.parseDate(value, this.format);
18860 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18861 v = Date.parseDate(value, 'Y-m-d');
18863 if(!v && this.altFormats){
18864 if(!this.altFormatsArray){
18865 this.altFormatsArray = this.altFormats.split("|");
18867 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18868 v = Date.parseDate(value, this.altFormatsArray[i]);
18874 formatDate : function(date, fmt)
18876 return (!date || !(date instanceof Date)) ?
18877 date : date.dateFormat(fmt || this.format);
18880 onFocus : function()
18882 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18886 onBlur : function()
18888 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18890 var d = this.inputEl().getValue();
18897 showPopup : function()
18899 this.picker().show();
18903 this.fireEvent('showpopup', this, this.date);
18906 hidePopup : function()
18908 if(this.isInline) {
18911 this.picker().hide();
18912 this.viewMode = this.startViewMode;
18915 this.fireEvent('hidepopup', this, this.date);
18919 onMousedown: function(e)
18921 e.stopPropagation();
18922 e.preventDefault();
18927 Roo.bootstrap.DateField.superclass.keyup.call(this);
18931 setValue: function(v)
18933 if(this.fireEvent('beforeselect', this, v) !== false){
18934 var d = new Date(this.parseDate(v) ).clearTime();
18936 if(isNaN(d.getTime())){
18937 this.date = this.viewDate = '';
18938 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18942 v = this.formatDate(d);
18944 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18946 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18950 this.fireEvent('select', this, this.date);
18954 getValue: function()
18956 return this.formatDate(this.date);
18959 fireKey: function(e)
18961 if (!this.picker().isVisible()){
18962 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18968 var dateChanged = false,
18970 newDate, newViewDate;
18975 e.preventDefault();
18979 if (!this.keyboardNavigation) {
18982 dir = e.keyCode == 37 ? -1 : 1;
18985 newDate = this.moveYear(this.date, dir);
18986 newViewDate = this.moveYear(this.viewDate, dir);
18987 } else if (e.shiftKey){
18988 newDate = this.moveMonth(this.date, dir);
18989 newViewDate = this.moveMonth(this.viewDate, dir);
18991 newDate = new Date(this.date);
18992 newDate.setUTCDate(this.date.getUTCDate() + dir);
18993 newViewDate = new Date(this.viewDate);
18994 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18996 if (this.dateWithinRange(newDate)){
18997 this.date = newDate;
18998 this.viewDate = newViewDate;
18999 this.setValue(this.formatDate(this.date));
19001 e.preventDefault();
19002 dateChanged = true;
19007 if (!this.keyboardNavigation) {
19010 dir = e.keyCode == 38 ? -1 : 1;
19012 newDate = this.moveYear(this.date, dir);
19013 newViewDate = this.moveYear(this.viewDate, dir);
19014 } else if (e.shiftKey){
19015 newDate = this.moveMonth(this.date, dir);
19016 newViewDate = this.moveMonth(this.viewDate, dir);
19018 newDate = new Date(this.date);
19019 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19020 newViewDate = new Date(this.viewDate);
19021 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19023 if (this.dateWithinRange(newDate)){
19024 this.date = newDate;
19025 this.viewDate = newViewDate;
19026 this.setValue(this.formatDate(this.date));
19028 e.preventDefault();
19029 dateChanged = true;
19033 this.setValue(this.formatDate(this.date));
19035 e.preventDefault();
19038 this.setValue(this.formatDate(this.date));
19052 onClick: function(e)
19054 e.stopPropagation();
19055 e.preventDefault();
19057 var target = e.getTarget();
19059 if(target.nodeName.toLowerCase() === 'i'){
19060 target = Roo.get(target).dom.parentNode;
19063 var nodeName = target.nodeName;
19064 var className = target.className;
19065 var html = target.innerHTML;
19066 //Roo.log(nodeName);
19068 switch(nodeName.toLowerCase()) {
19070 switch(className) {
19076 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19077 switch(this.viewMode){
19079 this.viewDate = this.moveMonth(this.viewDate, dir);
19083 this.viewDate = this.moveYear(this.viewDate, dir);
19089 var date = new Date();
19090 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19092 this.setValue(this.formatDate(this.date));
19099 if (className.indexOf('disabled') < 0) {
19100 this.viewDate.setUTCDate(1);
19101 if (className.indexOf('month') > -1) {
19102 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19104 var year = parseInt(html, 10) || 0;
19105 this.viewDate.setUTCFullYear(year);
19109 if(this.singleMode){
19110 this.setValue(this.formatDate(this.viewDate));
19121 //Roo.log(className);
19122 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19123 var day = parseInt(html, 10) || 1;
19124 var year = this.viewDate.getUTCFullYear(),
19125 month = this.viewDate.getUTCMonth();
19127 if (className.indexOf('old') > -1) {
19134 } else if (className.indexOf('new') > -1) {
19142 //Roo.log([year,month,day]);
19143 this.date = this.UTCDate(year, month, day,0,0,0,0);
19144 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19146 //Roo.log(this.formatDate(this.date));
19147 this.setValue(this.formatDate(this.date));
19154 setStartDate: function(startDate)
19156 this.startDate = startDate || -Infinity;
19157 if (this.startDate !== -Infinity) {
19158 this.startDate = this.parseDate(this.startDate);
19161 this.updateNavArrows();
19164 setEndDate: function(endDate)
19166 this.endDate = endDate || Infinity;
19167 if (this.endDate !== Infinity) {
19168 this.endDate = this.parseDate(this.endDate);
19171 this.updateNavArrows();
19174 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19176 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19177 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19178 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19180 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19181 return parseInt(d, 10);
19184 this.updateNavArrows();
19187 updateNavArrows: function()
19189 if(this.singleMode){
19193 var d = new Date(this.viewDate),
19194 year = d.getUTCFullYear(),
19195 month = d.getUTCMonth();
19197 Roo.each(this.picker().select('.prev', true).elements, function(v){
19199 switch (this.viewMode) {
19202 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19208 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19215 Roo.each(this.picker().select('.next', true).elements, function(v){
19217 switch (this.viewMode) {
19220 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19226 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19234 moveMonth: function(date, dir)
19239 var new_date = new Date(date.valueOf()),
19240 day = new_date.getUTCDate(),
19241 month = new_date.getUTCMonth(),
19242 mag = Math.abs(dir),
19244 dir = dir > 0 ? 1 : -1;
19247 // If going back one month, make sure month is not current month
19248 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19250 return new_date.getUTCMonth() == month;
19252 // If going forward one month, make sure month is as expected
19253 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19255 return new_date.getUTCMonth() != new_month;
19257 new_month = month + dir;
19258 new_date.setUTCMonth(new_month);
19259 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19260 if (new_month < 0 || new_month > 11) {
19261 new_month = (new_month + 12) % 12;
19264 // For magnitudes >1, move one month at a time...
19265 for (var i=0; i<mag; i++) {
19266 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19267 new_date = this.moveMonth(new_date, dir);
19269 // ...then reset the day, keeping it in the new month
19270 new_month = new_date.getUTCMonth();
19271 new_date.setUTCDate(day);
19273 return new_month != new_date.getUTCMonth();
19276 // Common date-resetting loop -- if date is beyond end of month, make it
19279 new_date.setUTCDate(--day);
19280 new_date.setUTCMonth(new_month);
19285 moveYear: function(date, dir)
19287 return this.moveMonth(date, dir*12);
19290 dateWithinRange: function(date)
19292 return date >= this.startDate && date <= this.endDate;
19298 this.picker().remove();
19301 validateValue : function(value)
19303 if(this.getVisibilityEl().hasClass('hidden')){
19307 if(value.length < 1) {
19308 if(this.allowBlank){
19314 if(value.length < this.minLength){
19317 if(value.length > this.maxLength){
19321 var vt = Roo.form.VTypes;
19322 if(!vt[this.vtype](value, this)){
19326 if(typeof this.validator == "function"){
19327 var msg = this.validator(value);
19333 if(this.regex && !this.regex.test(value)){
19337 if(typeof(this.parseDate(value)) == 'undefined'){
19341 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19345 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19355 this.date = this.viewDate = '';
19357 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19362 Roo.apply(Roo.bootstrap.DateField, {
19373 html: '<i class="fa fa-arrow-left"/>'
19383 html: '<i class="fa fa-arrow-right"/>'
19425 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19426 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19427 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19428 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19429 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19442 navFnc: 'FullYear',
19447 navFnc: 'FullYear',
19452 Roo.apply(Roo.bootstrap.DateField, {
19456 cls: 'datepicker dropdown-menu roo-dynamic',
19460 cls: 'datepicker-days',
19464 cls: 'table-condensed',
19466 Roo.bootstrap.DateField.head,
19470 Roo.bootstrap.DateField.footer
19477 cls: 'datepicker-months',
19481 cls: 'table-condensed',
19483 Roo.bootstrap.DateField.head,
19484 Roo.bootstrap.DateField.content,
19485 Roo.bootstrap.DateField.footer
19492 cls: 'datepicker-years',
19496 cls: 'table-condensed',
19498 Roo.bootstrap.DateField.head,
19499 Roo.bootstrap.DateField.content,
19500 Roo.bootstrap.DateField.footer
19519 * @class Roo.bootstrap.TimeField
19520 * @extends Roo.bootstrap.Input
19521 * Bootstrap DateField class
19525 * Create a new TimeField
19526 * @param {Object} config The config object
19529 Roo.bootstrap.TimeField = function(config){
19530 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19534 * Fires when this field show.
19535 * @param {Roo.bootstrap.DateField} thisthis
19536 * @param {Mixed} date The date value
19541 * Fires when this field hide.
19542 * @param {Roo.bootstrap.DateField} this
19543 * @param {Mixed} date The date value
19548 * Fires when select a date.
19549 * @param {Roo.bootstrap.DateField} this
19550 * @param {Mixed} date The date value
19556 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19559 * @cfg {String} format
19560 * The default time format string which can be overriden for localization support. The format must be
19561 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19565 onRender: function(ct, position)
19568 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19570 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19572 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19574 this.pop = this.picker().select('>.datepicker-time',true).first();
19575 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19577 this.picker().on('mousedown', this.onMousedown, this);
19578 this.picker().on('click', this.onClick, this);
19580 this.picker().addClass('datepicker-dropdown');
19585 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19586 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19587 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19588 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19589 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19590 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19594 fireKey: function(e){
19595 if (!this.picker().isVisible()){
19596 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19602 e.preventDefault();
19610 this.onTogglePeriod();
19613 this.onIncrementMinutes();
19616 this.onDecrementMinutes();
19625 onClick: function(e) {
19626 e.stopPropagation();
19627 e.preventDefault();
19630 picker : function()
19632 return this.el.select('.datepicker', true).first();
19635 fillTime: function()
19637 var time = this.pop.select('tbody', true).first();
19639 time.dom.innerHTML = '';
19654 cls: 'hours-up glyphicon glyphicon-chevron-up'
19674 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19695 cls: 'timepicker-hour',
19710 cls: 'timepicker-minute',
19725 cls: 'btn btn-primary period',
19747 cls: 'hours-down glyphicon glyphicon-chevron-down'
19767 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19785 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19792 var hours = this.time.getHours();
19793 var minutes = this.time.getMinutes();
19806 hours = hours - 12;
19810 hours = '0' + hours;
19814 minutes = '0' + minutes;
19817 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19818 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19819 this.pop.select('button', true).first().dom.innerHTML = period;
19825 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19827 var cls = ['bottom'];
19829 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19836 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19841 this.picker().addClass(cls.join('-'));
19845 Roo.each(cls, function(c){
19847 _this.picker().setTop(_this.inputEl().getHeight());
19851 _this.picker().setTop(0 - _this.picker().getHeight());
19856 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19860 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19867 onFocus : function()
19869 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19873 onBlur : function()
19875 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19881 this.picker().show();
19886 this.fireEvent('show', this, this.date);
19891 this.picker().hide();
19894 this.fireEvent('hide', this, this.date);
19897 setTime : function()
19900 this.setValue(this.time.format(this.format));
19902 this.fireEvent('select', this, this.date);
19907 onMousedown: function(e){
19908 e.stopPropagation();
19909 e.preventDefault();
19912 onIncrementHours: function()
19914 Roo.log('onIncrementHours');
19915 this.time = this.time.add(Date.HOUR, 1);
19920 onDecrementHours: function()
19922 Roo.log('onDecrementHours');
19923 this.time = this.time.add(Date.HOUR, -1);
19927 onIncrementMinutes: function()
19929 Roo.log('onIncrementMinutes');
19930 this.time = this.time.add(Date.MINUTE, 1);
19934 onDecrementMinutes: function()
19936 Roo.log('onDecrementMinutes');
19937 this.time = this.time.add(Date.MINUTE, -1);
19941 onTogglePeriod: function()
19943 Roo.log('onTogglePeriod');
19944 this.time = this.time.add(Date.HOUR, 12);
19951 Roo.apply(Roo.bootstrap.TimeField, {
19981 cls: 'btn btn-info ok',
19993 Roo.apply(Roo.bootstrap.TimeField, {
19997 cls: 'datepicker dropdown-menu',
20001 cls: 'datepicker-time',
20005 cls: 'table-condensed',
20007 Roo.bootstrap.TimeField.content,
20008 Roo.bootstrap.TimeField.footer
20027 * @class Roo.bootstrap.MonthField
20028 * @extends Roo.bootstrap.Input
20029 * Bootstrap MonthField class
20031 * @cfg {String} language default en
20034 * Create a new MonthField
20035 * @param {Object} config The config object
20038 Roo.bootstrap.MonthField = function(config){
20039 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20044 * Fires when this field show.
20045 * @param {Roo.bootstrap.MonthField} this
20046 * @param {Mixed} date The date value
20051 * Fires when this field hide.
20052 * @param {Roo.bootstrap.MonthField} this
20053 * @param {Mixed} date The date value
20058 * Fires when select a date.
20059 * @param {Roo.bootstrap.MonthField} this
20060 * @param {String} oldvalue The old value
20061 * @param {String} newvalue The new value
20067 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20069 onRender: function(ct, position)
20072 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20074 this.language = this.language || 'en';
20075 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20076 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20078 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20079 this.isInline = false;
20080 this.isInput = true;
20081 this.component = this.el.select('.add-on', true).first() || false;
20082 this.component = (this.component && this.component.length === 0) ? false : this.component;
20083 this.hasInput = this.component && this.inputEL().length;
20085 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20087 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20089 this.picker().on('mousedown', this.onMousedown, this);
20090 this.picker().on('click', this.onClick, this);
20092 this.picker().addClass('datepicker-dropdown');
20094 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20095 v.setStyle('width', '189px');
20102 if(this.isInline) {
20108 setValue: function(v, suppressEvent)
20110 var o = this.getValue();
20112 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20116 if(suppressEvent !== true){
20117 this.fireEvent('select', this, o, v);
20122 getValue: function()
20127 onClick: function(e)
20129 e.stopPropagation();
20130 e.preventDefault();
20132 var target = e.getTarget();
20134 if(target.nodeName.toLowerCase() === 'i'){
20135 target = Roo.get(target).dom.parentNode;
20138 var nodeName = target.nodeName;
20139 var className = target.className;
20140 var html = target.innerHTML;
20142 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20146 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20148 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20154 picker : function()
20156 return this.pickerEl;
20159 fillMonths: function()
20162 var months = this.picker().select('>.datepicker-months td', true).first();
20164 months.dom.innerHTML = '';
20170 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20173 months.createChild(month);
20182 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20183 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20186 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20187 e.removeClass('active');
20189 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20190 e.addClass('active');
20197 if(this.isInline) {
20201 this.picker().removeClass(['bottom', 'top']);
20203 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20205 * place to the top of element!
20209 this.picker().addClass('top');
20210 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20215 this.picker().addClass('bottom');
20217 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20220 onFocus : function()
20222 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20226 onBlur : function()
20228 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20230 var d = this.inputEl().getValue();
20239 this.picker().show();
20240 this.picker().select('>.datepicker-months', true).first().show();
20244 this.fireEvent('show', this, this.date);
20249 if(this.isInline) {
20252 this.picker().hide();
20253 this.fireEvent('hide', this, this.date);
20257 onMousedown: function(e)
20259 e.stopPropagation();
20260 e.preventDefault();
20265 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20269 fireKey: function(e)
20271 if (!this.picker().isVisible()){
20272 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20283 e.preventDefault();
20287 dir = e.keyCode == 37 ? -1 : 1;
20289 this.vIndex = this.vIndex + dir;
20291 if(this.vIndex < 0){
20295 if(this.vIndex > 11){
20299 if(isNaN(this.vIndex)){
20303 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20309 dir = e.keyCode == 38 ? -1 : 1;
20311 this.vIndex = this.vIndex + dir * 4;
20313 if(this.vIndex < 0){
20317 if(this.vIndex > 11){
20321 if(isNaN(this.vIndex)){
20325 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20330 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20331 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20335 e.preventDefault();
20338 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20339 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20355 this.picker().remove();
20360 Roo.apply(Roo.bootstrap.MonthField, {
20379 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20380 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20385 Roo.apply(Roo.bootstrap.MonthField, {
20389 cls: 'datepicker dropdown-menu roo-dynamic',
20393 cls: 'datepicker-months',
20397 cls: 'table-condensed',
20399 Roo.bootstrap.DateField.content
20419 * @class Roo.bootstrap.CheckBox
20420 * @extends Roo.bootstrap.Input
20421 * Bootstrap CheckBox class
20423 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20424 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20425 * @cfg {String} boxLabel The text that appears beside the checkbox
20426 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20427 * @cfg {Boolean} checked initnal the element
20428 * @cfg {Boolean} inline inline the element (default false)
20429 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20430 * @cfg {String} tooltip label tooltip
20433 * Create a new CheckBox
20434 * @param {Object} config The config object
20437 Roo.bootstrap.CheckBox = function(config){
20438 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20443 * Fires when the element is checked or unchecked.
20444 * @param {Roo.bootstrap.CheckBox} this This input
20445 * @param {Boolean} checked The new checked value
20450 * Fires when the element is click.
20451 * @param {Roo.bootstrap.CheckBox} this This input
20458 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20460 inputType: 'checkbox',
20469 getAutoCreate : function()
20471 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20477 cfg.cls = 'form-group ' + this.inputType; //input-group
20480 cfg.cls += ' ' + this.inputType + '-inline';
20486 type : this.inputType,
20487 value : this.inputValue,
20488 cls : 'roo-' + this.inputType, //'form-box',
20489 placeholder : this.placeholder || ''
20493 if(this.inputType != 'radio'){
20497 cls : 'roo-hidden-value',
20498 value : this.checked ? this.inputValue : this.valueOff
20503 if (this.weight) { // Validity check?
20504 cfg.cls += " " + this.inputType + "-" + this.weight;
20507 if (this.disabled) {
20508 input.disabled=true;
20512 input.checked = this.checked;
20517 input.name = this.name;
20519 if(this.inputType != 'radio'){
20520 hidden.name = this.name;
20521 input.name = '_hidden_' + this.name;
20526 input.cls += ' input-' + this.size;
20531 ['xs','sm','md','lg'].map(function(size){
20532 if (settings[size]) {
20533 cfg.cls += ' col-' + size + '-' + settings[size];
20537 var inputblock = input;
20539 if (this.before || this.after) {
20542 cls : 'input-group',
20547 inputblock.cn.push({
20549 cls : 'input-group-addon',
20554 inputblock.cn.push(input);
20556 if(this.inputType != 'radio'){
20557 inputblock.cn.push(hidden);
20561 inputblock.cn.push({
20563 cls : 'input-group-addon',
20570 if (align ==='left' && this.fieldLabel.length) {
20571 // Roo.log("left and has label");
20576 cls : 'control-label',
20577 html : this.fieldLabel
20587 if(this.labelWidth > 12){
20588 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20591 if(this.labelWidth < 13 && this.labelmd == 0){
20592 this.labelmd = this.labelWidth;
20595 if(this.labellg > 0){
20596 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20597 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20600 if(this.labelmd > 0){
20601 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20602 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20605 if(this.labelsm > 0){
20606 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20607 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20610 if(this.labelxs > 0){
20611 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20612 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20615 } else if ( this.fieldLabel.length) {
20616 // Roo.log(" label");
20620 tag: this.boxLabel ? 'span' : 'label',
20622 cls: 'control-label box-input-label',
20623 //cls : 'input-group-addon',
20624 html : this.fieldLabel
20633 // Roo.log(" no label && no align");
20634 cfg.cn = [ inputblock ] ;
20640 var boxLabelCfg = {
20642 //'for': id, // box label is handled by onclick - so no for...
20644 html: this.boxLabel
20648 boxLabelCfg.tooltip = this.tooltip;
20651 cfg.cn.push(boxLabelCfg);
20654 if(this.inputType != 'radio'){
20655 cfg.cn.push(hidden);
20663 * return the real input element.
20665 inputEl: function ()
20667 return this.el.select('input.roo-' + this.inputType,true).first();
20669 hiddenEl: function ()
20671 return this.el.select('input.roo-hidden-value',true).first();
20674 labelEl: function()
20676 return this.el.select('label.control-label',true).first();
20678 /* depricated... */
20682 return this.labelEl();
20685 boxLabelEl: function()
20687 return this.el.select('label.box-label',true).first();
20690 initEvents : function()
20692 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20694 this.inputEl().on('click', this.onClick, this);
20696 if (this.boxLabel) {
20697 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20700 this.startValue = this.getValue();
20703 Roo.bootstrap.CheckBox.register(this);
20707 onClick : function(e)
20709 if(this.fireEvent('click', this, e) !== false){
20710 this.setChecked(!this.checked);
20715 setChecked : function(state,suppressEvent)
20717 this.startValue = this.getValue();
20719 if(this.inputType == 'radio'){
20721 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20722 e.dom.checked = false;
20725 this.inputEl().dom.checked = true;
20727 this.inputEl().dom.value = this.inputValue;
20729 if(suppressEvent !== true){
20730 this.fireEvent('check', this, true);
20738 this.checked = state;
20740 this.inputEl().dom.checked = state;
20743 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20745 if(suppressEvent !== true){
20746 this.fireEvent('check', this, state);
20752 getValue : function()
20754 if(this.inputType == 'radio'){
20755 return this.getGroupValue();
20758 return this.hiddenEl().dom.value;
20762 getGroupValue : function()
20764 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20768 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20771 setValue : function(v,suppressEvent)
20773 if(this.inputType == 'radio'){
20774 this.setGroupValue(v, suppressEvent);
20778 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20783 setGroupValue : function(v, suppressEvent)
20785 this.startValue = this.getValue();
20787 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20788 e.dom.checked = false;
20790 if(e.dom.value == v){
20791 e.dom.checked = true;
20795 if(suppressEvent !== true){
20796 this.fireEvent('check', this, true);
20804 validate : function()
20806 if(this.getVisibilityEl().hasClass('hidden')){
20812 (this.inputType == 'radio' && this.validateRadio()) ||
20813 (this.inputType == 'checkbox' && this.validateCheckbox())
20819 this.markInvalid();
20823 validateRadio : function()
20825 if(this.getVisibilityEl().hasClass('hidden')){
20829 if(this.allowBlank){
20835 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20836 if(!e.dom.checked){
20848 validateCheckbox : function()
20851 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20852 //return (this.getValue() == this.inputValue) ? true : false;
20855 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20863 for(var i in group){
20864 if(group[i].el.isVisible(true)){
20872 for(var i in group){
20877 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20884 * Mark this field as valid
20886 markValid : function()
20890 this.fireEvent('valid', this);
20892 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20895 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20902 if(this.inputType == 'radio'){
20903 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20904 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20905 e.findParent('.form-group', false, true).addClass(_this.validClass);
20912 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20913 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20917 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20923 for(var i in group){
20924 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20925 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20930 * Mark this field as invalid
20931 * @param {String} msg The validation message
20933 markInvalid : function(msg)
20935 if(this.allowBlank){
20941 this.fireEvent('invalid', this, msg);
20943 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20946 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20950 label.markInvalid();
20953 if(this.inputType == 'radio'){
20954 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20955 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20956 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20963 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20964 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20968 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20974 for(var i in group){
20975 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20976 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20981 clearInvalid : function()
20983 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20985 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20987 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20989 if (label && label.iconEl) {
20990 label.iconEl.removeClass(label.validClass);
20991 label.iconEl.removeClass(label.invalidClass);
20995 disable : function()
20997 if(this.inputType != 'radio'){
20998 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21005 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21006 _this.getActionEl().addClass(this.disabledClass);
21007 e.dom.disabled = true;
21011 this.disabled = true;
21012 this.fireEvent("disable", this);
21016 enable : function()
21018 if(this.inputType != 'radio'){
21019 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21026 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21027 _this.getActionEl().removeClass(this.disabledClass);
21028 e.dom.disabled = false;
21032 this.disabled = false;
21033 this.fireEvent("enable", this);
21037 setBoxLabel : function(v)
21042 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21048 Roo.apply(Roo.bootstrap.CheckBox, {
21053 * register a CheckBox Group
21054 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21056 register : function(checkbox)
21058 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21059 this.groups[checkbox.groupId] = {};
21062 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21066 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21070 * fetch a CheckBox Group based on the group ID
21071 * @param {string} the group ID
21072 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21074 get: function(groupId) {
21075 if (typeof(this.groups[groupId]) == 'undefined') {
21079 return this.groups[groupId] ;
21092 * @class Roo.bootstrap.Radio
21093 * @extends Roo.bootstrap.Component
21094 * Bootstrap Radio class
21095 * @cfg {String} boxLabel - the label associated
21096 * @cfg {String} value - the value of radio
21099 * Create a new Radio
21100 * @param {Object} config The config object
21102 Roo.bootstrap.Radio = function(config){
21103 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21107 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21113 getAutoCreate : function()
21117 cls : 'form-group radio',
21122 html : this.boxLabel
21130 initEvents : function()
21132 this.parent().register(this);
21134 this.el.on('click', this.onClick, this);
21138 onClick : function(e)
21140 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21141 this.setChecked(true);
21145 setChecked : function(state, suppressEvent)
21147 this.parent().setValue(this.value, suppressEvent);
21151 setBoxLabel : function(v)
21156 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21171 * @class Roo.bootstrap.SecurePass
21172 * @extends Roo.bootstrap.Input
21173 * Bootstrap SecurePass class
21177 * Create a new SecurePass
21178 * @param {Object} config The config object
21181 Roo.bootstrap.SecurePass = function (config) {
21182 // these go here, so the translation tool can replace them..
21184 PwdEmpty: "Please type a password, and then retype it to confirm.",
21185 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21186 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21187 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21188 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21189 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21190 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21191 TooWeak: "Your password is Too Weak."
21193 this.meterLabel = "Password strength:";
21194 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21195 this.meterClass = [
21196 "roo-password-meter-tooweak",
21197 "roo-password-meter-weak",
21198 "roo-password-meter-medium",
21199 "roo-password-meter-strong",
21200 "roo-password-meter-grey"
21205 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21208 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21210 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21212 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21213 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21214 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21215 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21216 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21217 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21218 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21228 * @cfg {String/Object} Label for the strength meter (defaults to
21229 * 'Password strength:')
21234 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21235 * ['Weak', 'Medium', 'Strong'])
21238 pwdStrengths: false,
21251 initEvents: function ()
21253 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21255 if (this.el.is('input[type=password]') && Roo.isSafari) {
21256 this.el.on('keydown', this.SafariOnKeyDown, this);
21259 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21262 onRender: function (ct, position)
21264 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21265 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21266 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21268 this.trigger.createChild({
21273 cls: 'roo-password-meter-grey col-xs-12',
21276 //width: this.meterWidth + 'px'
21280 cls: 'roo-password-meter-text'
21286 if (this.hideTrigger) {
21287 this.trigger.setDisplayed(false);
21289 this.setSize(this.width || '', this.height || '');
21292 onDestroy: function ()
21294 if (this.trigger) {
21295 this.trigger.removeAllListeners();
21296 this.trigger.remove();
21299 this.wrap.remove();
21301 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21304 checkStrength: function ()
21306 var pwd = this.inputEl().getValue();
21307 if (pwd == this._lastPwd) {
21312 if (this.ClientSideStrongPassword(pwd)) {
21314 } else if (this.ClientSideMediumPassword(pwd)) {
21316 } else if (this.ClientSideWeakPassword(pwd)) {
21322 Roo.log('strength1: ' + strength);
21324 //var pm = this.trigger.child('div/div/div').dom;
21325 var pm = this.trigger.child('div/div');
21326 pm.removeClass(this.meterClass);
21327 pm.addClass(this.meterClass[strength]);
21330 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21332 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21334 this._lastPwd = pwd;
21338 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21340 this._lastPwd = '';
21342 var pm = this.trigger.child('div/div');
21343 pm.removeClass(this.meterClass);
21344 pm.addClass('roo-password-meter-grey');
21347 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21350 this.inputEl().dom.type='password';
21353 validateValue: function (value)
21356 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21359 if (value.length == 0) {
21360 if (this.allowBlank) {
21361 this.clearInvalid();
21365 this.markInvalid(this.errors.PwdEmpty);
21366 this.errorMsg = this.errors.PwdEmpty;
21374 if ('[\x21-\x7e]*'.match(value)) {
21375 this.markInvalid(this.errors.PwdBadChar);
21376 this.errorMsg = this.errors.PwdBadChar;
21379 if (value.length < 6) {
21380 this.markInvalid(this.errors.PwdShort);
21381 this.errorMsg = this.errors.PwdShort;
21384 if (value.length > 16) {
21385 this.markInvalid(this.errors.PwdLong);
21386 this.errorMsg = this.errors.PwdLong;
21390 if (this.ClientSideStrongPassword(value)) {
21392 } else if (this.ClientSideMediumPassword(value)) {
21394 } else if (this.ClientSideWeakPassword(value)) {
21401 if (strength < 2) {
21402 //this.markInvalid(this.errors.TooWeak);
21403 this.errorMsg = this.errors.TooWeak;
21408 console.log('strength2: ' + strength);
21410 //var pm = this.trigger.child('div/div/div').dom;
21412 var pm = this.trigger.child('div/div');
21413 pm.removeClass(this.meterClass);
21414 pm.addClass(this.meterClass[strength]);
21416 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21418 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21420 this.errorMsg = '';
21424 CharacterSetChecks: function (type)
21427 this.fResult = false;
21430 isctype: function (character, type)
21433 case this.kCapitalLetter:
21434 if (character >= 'A' && character <= 'Z') {
21439 case this.kSmallLetter:
21440 if (character >= 'a' && character <= 'z') {
21446 if (character >= '0' && character <= '9') {
21451 case this.kPunctuation:
21452 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21463 IsLongEnough: function (pwd, size)
21465 return !(pwd == null || isNaN(size) || pwd.length < size);
21468 SpansEnoughCharacterSets: function (word, nb)
21470 if (!this.IsLongEnough(word, nb))
21475 var characterSetChecks = new Array(
21476 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21477 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21480 for (var index = 0; index < word.length; ++index) {
21481 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21482 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21483 characterSetChecks[nCharSet].fResult = true;
21490 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21491 if (characterSetChecks[nCharSet].fResult) {
21496 if (nCharSets < nb) {
21502 ClientSideStrongPassword: function (pwd)
21504 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21507 ClientSideMediumPassword: function (pwd)
21509 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21512 ClientSideWeakPassword: function (pwd)
21514 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21517 })//<script type="text/javascript">
21520 * Based Ext JS Library 1.1.1
21521 * Copyright(c) 2006-2007, Ext JS, LLC.
21527 * @class Roo.HtmlEditorCore
21528 * @extends Roo.Component
21529 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21531 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21534 Roo.HtmlEditorCore = function(config){
21537 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21542 * @event initialize
21543 * Fires when the editor is fully initialized (including the iframe)
21544 * @param {Roo.HtmlEditorCore} this
21549 * Fires when the editor is first receives the focus. Any insertion must wait
21550 * until after this event.
21551 * @param {Roo.HtmlEditorCore} this
21555 * @event beforesync
21556 * Fires before the textarea is updated with content from the editor iframe. Return false
21557 * to cancel the sync.
21558 * @param {Roo.HtmlEditorCore} this
21559 * @param {String} html
21563 * @event beforepush
21564 * Fires before the iframe editor is updated with content from the textarea. Return false
21565 * to cancel the push.
21566 * @param {Roo.HtmlEditorCore} this
21567 * @param {String} html
21572 * Fires when the textarea is updated with content from the editor iframe.
21573 * @param {Roo.HtmlEditorCore} this
21574 * @param {String} html
21579 * Fires when the iframe editor is updated with content from the textarea.
21580 * @param {Roo.HtmlEditorCore} this
21581 * @param {String} html
21586 * @event editorevent
21587 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21588 * @param {Roo.HtmlEditorCore} this
21594 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21596 // defaults : white / black...
21597 this.applyBlacklists();
21604 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21608 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21614 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21619 * @cfg {Number} height (in pixels)
21623 * @cfg {Number} width (in pixels)
21628 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21631 stylesheets: false,
21636 // private properties
21637 validationEvent : false,
21639 initialized : false,
21641 sourceEditMode : false,
21642 onFocus : Roo.emptyFn,
21644 hideMode:'offsets',
21648 // blacklist + whitelisted elements..
21655 * Protected method that will not generally be called directly. It
21656 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21657 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21659 getDocMarkup : function(){
21663 // inherit styels from page...??
21664 if (this.stylesheets === false) {
21666 Roo.get(document.head).select('style').each(function(node) {
21667 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21670 Roo.get(document.head).select('link').each(function(node) {
21671 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21674 } else if (!this.stylesheets.length) {
21676 st = '<style type="text/css">' +
21677 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21680 st = '<style type="text/css">' +
21685 st += '<style type="text/css">' +
21686 'IMG { cursor: pointer } ' +
21689 var cls = 'roo-htmleditor-body';
21691 if(this.bodyCls.length){
21692 cls += ' ' + this.bodyCls;
21695 return '<html><head>' + st +
21696 //<style type="text/css">' +
21697 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21699 ' </head><body class="' + cls + '"></body></html>';
21703 onRender : function(ct, position)
21706 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21707 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21710 this.el.dom.style.border = '0 none';
21711 this.el.dom.setAttribute('tabIndex', -1);
21712 this.el.addClass('x-hidden hide');
21716 if(Roo.isIE){ // fix IE 1px bogus margin
21717 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21721 this.frameId = Roo.id();
21725 var iframe = this.owner.wrap.createChild({
21727 cls: 'form-control', // bootstrap..
21729 name: this.frameId,
21730 frameBorder : 'no',
21731 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21736 this.iframe = iframe.dom;
21738 this.assignDocWin();
21740 this.doc.designMode = 'on';
21743 this.doc.write(this.getDocMarkup());
21747 var task = { // must defer to wait for browser to be ready
21749 //console.log("run task?" + this.doc.readyState);
21750 this.assignDocWin();
21751 if(this.doc.body || this.doc.readyState == 'complete'){
21753 this.doc.designMode="on";
21757 Roo.TaskMgr.stop(task);
21758 this.initEditor.defer(10, this);
21765 Roo.TaskMgr.start(task);
21770 onResize : function(w, h)
21772 Roo.log('resize: ' +w + ',' + h );
21773 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21777 if(typeof w == 'number'){
21779 this.iframe.style.width = w + 'px';
21781 if(typeof h == 'number'){
21783 this.iframe.style.height = h + 'px';
21785 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21792 * Toggles the editor between standard and source edit mode.
21793 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21795 toggleSourceEdit : function(sourceEditMode){
21797 this.sourceEditMode = sourceEditMode === true;
21799 if(this.sourceEditMode){
21801 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21804 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21805 //this.iframe.className = '';
21808 //this.setSize(this.owner.wrap.getSize());
21809 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21816 * Protected method that will not generally be called directly. If you need/want
21817 * custom HTML cleanup, this is the method you should override.
21818 * @param {String} html The HTML to be cleaned
21819 * return {String} The cleaned HTML
21821 cleanHtml : function(html){
21822 html = String(html);
21823 if(html.length > 5){
21824 if(Roo.isSafari){ // strip safari nonsense
21825 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21828 if(html == ' '){
21835 * HTML Editor -> Textarea
21836 * Protected method that will not generally be called directly. Syncs the contents
21837 * of the editor iframe with the textarea.
21839 syncValue : function(){
21840 if(this.initialized){
21841 var bd = (this.doc.body || this.doc.documentElement);
21842 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21843 var html = bd.innerHTML;
21845 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21846 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21848 html = '<div style="'+m[0]+'">' + html + '</div>';
21851 html = this.cleanHtml(html);
21852 // fix up the special chars.. normaly like back quotes in word...
21853 // however we do not want to do this with chinese..
21854 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21855 var cc = b.charCodeAt();
21857 (cc >= 0x4E00 && cc < 0xA000 ) ||
21858 (cc >= 0x3400 && cc < 0x4E00 ) ||
21859 (cc >= 0xf900 && cc < 0xfb00 )
21865 if(this.owner.fireEvent('beforesync', this, html) !== false){
21866 this.el.dom.value = html;
21867 this.owner.fireEvent('sync', this, html);
21873 * Protected method that will not generally be called directly. Pushes the value of the textarea
21874 * into the iframe editor.
21876 pushValue : function(){
21877 if(this.initialized){
21878 var v = this.el.dom.value.trim();
21880 // if(v.length < 1){
21884 if(this.owner.fireEvent('beforepush', this, v) !== false){
21885 var d = (this.doc.body || this.doc.documentElement);
21887 this.cleanUpPaste();
21888 this.el.dom.value = d.innerHTML;
21889 this.owner.fireEvent('push', this, v);
21895 deferFocus : function(){
21896 this.focus.defer(10, this);
21900 focus : function(){
21901 if(this.win && !this.sourceEditMode){
21908 assignDocWin: function()
21910 var iframe = this.iframe;
21913 this.doc = iframe.contentWindow.document;
21914 this.win = iframe.contentWindow;
21916 // if (!Roo.get(this.frameId)) {
21919 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21920 // this.win = Roo.get(this.frameId).dom.contentWindow;
21922 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21926 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21927 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21932 initEditor : function(){
21933 //console.log("INIT EDITOR");
21934 this.assignDocWin();
21938 this.doc.designMode="on";
21940 this.doc.write(this.getDocMarkup());
21943 var dbody = (this.doc.body || this.doc.documentElement);
21944 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21945 // this copies styles from the containing element into thsi one..
21946 // not sure why we need all of this..
21947 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21949 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21950 //ss['background-attachment'] = 'fixed'; // w3c
21951 dbody.bgProperties = 'fixed'; // ie
21952 //Roo.DomHelper.applyStyles(dbody, ss);
21953 Roo.EventManager.on(this.doc, {
21954 //'mousedown': this.onEditorEvent,
21955 'mouseup': this.onEditorEvent,
21956 'dblclick': this.onEditorEvent,
21957 'click': this.onEditorEvent,
21958 'keyup': this.onEditorEvent,
21963 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21965 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21966 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21968 this.initialized = true;
21970 this.owner.fireEvent('initialize', this);
21975 onDestroy : function(){
21981 //for (var i =0; i < this.toolbars.length;i++) {
21982 // // fixme - ask toolbars for heights?
21983 // this.toolbars[i].onDestroy();
21986 //this.wrap.dom.innerHTML = '';
21987 //this.wrap.remove();
21992 onFirstFocus : function(){
21994 this.assignDocWin();
21997 this.activated = true;
22000 if(Roo.isGecko){ // prevent silly gecko errors
22002 var s = this.win.getSelection();
22003 if(!s.focusNode || s.focusNode.nodeType != 3){
22004 var r = s.getRangeAt(0);
22005 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22010 this.execCmd('useCSS', true);
22011 this.execCmd('styleWithCSS', false);
22014 this.owner.fireEvent('activate', this);
22018 adjustFont: function(btn){
22019 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22020 //if(Roo.isSafari){ // safari
22023 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22024 if(Roo.isSafari){ // safari
22025 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22026 v = (v < 10) ? 10 : v;
22027 v = (v > 48) ? 48 : v;
22028 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22033 v = Math.max(1, v+adjust);
22035 this.execCmd('FontSize', v );
22038 onEditorEvent : function(e)
22040 this.owner.fireEvent('editorevent', this, e);
22041 // this.updateToolbar();
22042 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22045 insertTag : function(tg)
22047 // could be a bit smarter... -> wrap the current selected tRoo..
22048 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22050 range = this.createRange(this.getSelection());
22051 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22052 wrappingNode.appendChild(range.extractContents());
22053 range.insertNode(wrappingNode);
22060 this.execCmd("formatblock", tg);
22064 insertText : function(txt)
22068 var range = this.createRange();
22069 range.deleteContents();
22070 //alert(Sender.getAttribute('label'));
22072 range.insertNode(this.doc.createTextNode(txt));
22078 * Executes a Midas editor command on the editor document and performs necessary focus and
22079 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22080 * @param {String} cmd The Midas command
22081 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22083 relayCmd : function(cmd, value){
22085 this.execCmd(cmd, value);
22086 this.owner.fireEvent('editorevent', this);
22087 //this.updateToolbar();
22088 this.owner.deferFocus();
22092 * Executes a Midas editor command directly on the editor document.
22093 * For visual commands, you should use {@link #relayCmd} instead.
22094 * <b>This should only be called after the editor is initialized.</b>
22095 * @param {String} cmd The Midas command
22096 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22098 execCmd : function(cmd, value){
22099 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22106 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22108 * @param {String} text | dom node..
22110 insertAtCursor : function(text)
22113 if(!this.activated){
22119 var r = this.doc.selection.createRange();
22130 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22134 // from jquery ui (MIT licenced)
22136 var win = this.win;
22138 if (win.getSelection && win.getSelection().getRangeAt) {
22139 range = win.getSelection().getRangeAt(0);
22140 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22141 range.insertNode(node);
22142 } else if (win.document.selection && win.document.selection.createRange) {
22143 // no firefox support
22144 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22145 win.document.selection.createRange().pasteHTML(txt);
22147 // no firefox support
22148 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22149 this.execCmd('InsertHTML', txt);
22158 mozKeyPress : function(e){
22160 var c = e.getCharCode(), cmd;
22163 c = String.fromCharCode(c).toLowerCase();
22177 this.cleanUpPaste.defer(100, this);
22185 e.preventDefault();
22193 fixKeys : function(){ // load time branching for fastest keydown performance
22195 return function(e){
22196 var k = e.getKey(), r;
22199 r = this.doc.selection.createRange();
22202 r.pasteHTML('    ');
22209 r = this.doc.selection.createRange();
22211 var target = r.parentElement();
22212 if(!target || target.tagName.toLowerCase() != 'li'){
22214 r.pasteHTML('<br />');
22220 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22221 this.cleanUpPaste.defer(100, this);
22227 }else if(Roo.isOpera){
22228 return function(e){
22229 var k = e.getKey();
22233 this.execCmd('InsertHTML','    ');
22236 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22237 this.cleanUpPaste.defer(100, this);
22242 }else if(Roo.isSafari){
22243 return function(e){
22244 var k = e.getKey();
22248 this.execCmd('InsertText','\t');
22252 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22253 this.cleanUpPaste.defer(100, this);
22261 getAllAncestors: function()
22263 var p = this.getSelectedNode();
22266 a.push(p); // push blank onto stack..
22267 p = this.getParentElement();
22271 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22275 a.push(this.doc.body);
22279 lastSelNode : false,
22282 getSelection : function()
22284 this.assignDocWin();
22285 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22288 getSelectedNode: function()
22290 // this may only work on Gecko!!!
22292 // should we cache this!!!!
22297 var range = this.createRange(this.getSelection()).cloneRange();
22300 var parent = range.parentElement();
22302 var testRange = range.duplicate();
22303 testRange.moveToElementText(parent);
22304 if (testRange.inRange(range)) {
22307 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22310 parent = parent.parentElement;
22315 // is ancestor a text element.
22316 var ac = range.commonAncestorContainer;
22317 if (ac.nodeType == 3) {
22318 ac = ac.parentNode;
22321 var ar = ac.childNodes;
22324 var other_nodes = [];
22325 var has_other_nodes = false;
22326 for (var i=0;i<ar.length;i++) {
22327 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22330 // fullly contained node.
22332 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22337 // probably selected..
22338 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22339 other_nodes.push(ar[i]);
22343 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22348 has_other_nodes = true;
22350 if (!nodes.length && other_nodes.length) {
22351 nodes= other_nodes;
22353 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22359 createRange: function(sel)
22361 // this has strange effects when using with
22362 // top toolbar - not sure if it's a great idea.
22363 //this.editor.contentWindow.focus();
22364 if (typeof sel != "undefined") {
22366 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22368 return this.doc.createRange();
22371 return this.doc.createRange();
22374 getParentElement: function()
22377 this.assignDocWin();
22378 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22380 var range = this.createRange(sel);
22383 var p = range.commonAncestorContainer;
22384 while (p.nodeType == 3) { // text node
22395 * Range intersection.. the hard stuff...
22399 * [ -- selected range --- ]
22403 * if end is before start or hits it. fail.
22404 * if start is after end or hits it fail.
22406 * if either hits (but other is outside. - then it's not
22412 // @see http://www.thismuchiknow.co.uk/?p=64.
22413 rangeIntersectsNode : function(range, node)
22415 var nodeRange = node.ownerDocument.createRange();
22417 nodeRange.selectNode(node);
22419 nodeRange.selectNodeContents(node);
22422 var rangeStartRange = range.cloneRange();
22423 rangeStartRange.collapse(true);
22425 var rangeEndRange = range.cloneRange();
22426 rangeEndRange.collapse(false);
22428 var nodeStartRange = nodeRange.cloneRange();
22429 nodeStartRange.collapse(true);
22431 var nodeEndRange = nodeRange.cloneRange();
22432 nodeEndRange.collapse(false);
22434 return rangeStartRange.compareBoundaryPoints(
22435 Range.START_TO_START, nodeEndRange) == -1 &&
22436 rangeEndRange.compareBoundaryPoints(
22437 Range.START_TO_START, nodeStartRange) == 1;
22441 rangeCompareNode : function(range, node)
22443 var nodeRange = node.ownerDocument.createRange();
22445 nodeRange.selectNode(node);
22447 nodeRange.selectNodeContents(node);
22451 range.collapse(true);
22453 nodeRange.collapse(true);
22455 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22456 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22458 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22460 var nodeIsBefore = ss == 1;
22461 var nodeIsAfter = ee == -1;
22463 if (nodeIsBefore && nodeIsAfter) {
22466 if (!nodeIsBefore && nodeIsAfter) {
22467 return 1; //right trailed.
22470 if (nodeIsBefore && !nodeIsAfter) {
22471 return 2; // left trailed.
22477 // private? - in a new class?
22478 cleanUpPaste : function()
22480 // cleans up the whole document..
22481 Roo.log('cleanuppaste');
22483 this.cleanUpChildren(this.doc.body);
22484 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22485 if (clean != this.doc.body.innerHTML) {
22486 this.doc.body.innerHTML = clean;
22491 cleanWordChars : function(input) {// change the chars to hex code
22492 var he = Roo.HtmlEditorCore;
22494 var output = input;
22495 Roo.each(he.swapCodes, function(sw) {
22496 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22498 output = output.replace(swapper, sw[1]);
22505 cleanUpChildren : function (n)
22507 if (!n.childNodes.length) {
22510 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22511 this.cleanUpChild(n.childNodes[i]);
22518 cleanUpChild : function (node)
22521 //console.log(node);
22522 if (node.nodeName == "#text") {
22523 // clean up silly Windows -- stuff?
22526 if (node.nodeName == "#comment") {
22527 node.parentNode.removeChild(node);
22528 // clean up silly Windows -- stuff?
22531 var lcname = node.tagName.toLowerCase();
22532 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22533 // whitelist of tags..
22535 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22537 node.parentNode.removeChild(node);
22542 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22544 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22545 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22547 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22548 // remove_keep_children = true;
22551 if (remove_keep_children) {
22552 this.cleanUpChildren(node);
22553 // inserts everything just before this node...
22554 while (node.childNodes.length) {
22555 var cn = node.childNodes[0];
22556 node.removeChild(cn);
22557 node.parentNode.insertBefore(cn, node);
22559 node.parentNode.removeChild(node);
22563 if (!node.attributes || !node.attributes.length) {
22564 this.cleanUpChildren(node);
22568 function cleanAttr(n,v)
22571 if (v.match(/^\./) || v.match(/^\//)) {
22574 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22577 if (v.match(/^#/)) {
22580 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22581 node.removeAttribute(n);
22585 var cwhite = this.cwhite;
22586 var cblack = this.cblack;
22588 function cleanStyle(n,v)
22590 if (v.match(/expression/)) { //XSS?? should we even bother..
22591 node.removeAttribute(n);
22595 var parts = v.split(/;/);
22598 Roo.each(parts, function(p) {
22599 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22603 var l = p.split(':').shift().replace(/\s+/g,'');
22604 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22606 if ( cwhite.length && cblack.indexOf(l) > -1) {
22607 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22608 //node.removeAttribute(n);
22612 // only allow 'c whitelisted system attributes'
22613 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22614 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22615 //node.removeAttribute(n);
22625 if (clean.length) {
22626 node.setAttribute(n, clean.join(';'));
22628 node.removeAttribute(n);
22634 for (var i = node.attributes.length-1; i > -1 ; i--) {
22635 var a = node.attributes[i];
22638 if (a.name.toLowerCase().substr(0,2)=='on') {
22639 node.removeAttribute(a.name);
22642 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22643 node.removeAttribute(a.name);
22646 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22647 cleanAttr(a.name,a.value); // fixme..
22650 if (a.name == 'style') {
22651 cleanStyle(a.name,a.value);
22654 /// clean up MS crap..
22655 // tecnically this should be a list of valid class'es..
22658 if (a.name == 'class') {
22659 if (a.value.match(/^Mso/)) {
22660 node.className = '';
22663 if (a.value.match(/^body$/)) {
22664 node.className = '';
22675 this.cleanUpChildren(node);
22681 * Clean up MS wordisms...
22683 cleanWord : function(node)
22688 this.cleanWord(this.doc.body);
22691 if (node.nodeName == "#text") {
22692 // clean up silly Windows -- stuff?
22695 if (node.nodeName == "#comment") {
22696 node.parentNode.removeChild(node);
22697 // clean up silly Windows -- stuff?
22701 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22702 node.parentNode.removeChild(node);
22706 // remove - but keep children..
22707 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22708 while (node.childNodes.length) {
22709 var cn = node.childNodes[0];
22710 node.removeChild(cn);
22711 node.parentNode.insertBefore(cn, node);
22713 node.parentNode.removeChild(node);
22714 this.iterateChildren(node, this.cleanWord);
22718 if (node.className.length) {
22720 var cn = node.className.split(/\W+/);
22722 Roo.each(cn, function(cls) {
22723 if (cls.match(/Mso[a-zA-Z]+/)) {
22728 node.className = cna.length ? cna.join(' ') : '';
22730 node.removeAttribute("class");
22734 if (node.hasAttribute("lang")) {
22735 node.removeAttribute("lang");
22738 if (node.hasAttribute("style")) {
22740 var styles = node.getAttribute("style").split(";");
22742 Roo.each(styles, function(s) {
22743 if (!s.match(/:/)) {
22746 var kv = s.split(":");
22747 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22750 // what ever is left... we allow.
22753 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22754 if (!nstyle.length) {
22755 node.removeAttribute('style');
22758 this.iterateChildren(node, this.cleanWord);
22764 * iterateChildren of a Node, calling fn each time, using this as the scole..
22765 * @param {DomNode} node node to iterate children of.
22766 * @param {Function} fn method of this class to call on each item.
22768 iterateChildren : function(node, fn)
22770 if (!node.childNodes.length) {
22773 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22774 fn.call(this, node.childNodes[i])
22780 * cleanTableWidths.
22782 * Quite often pasting from word etc.. results in tables with column and widths.
22783 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22786 cleanTableWidths : function(node)
22791 this.cleanTableWidths(this.doc.body);
22796 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22799 Roo.log(node.tagName);
22800 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22801 this.iterateChildren(node, this.cleanTableWidths);
22804 if (node.hasAttribute('width')) {
22805 node.removeAttribute('width');
22809 if (node.hasAttribute("style")) {
22812 var styles = node.getAttribute("style").split(";");
22814 Roo.each(styles, function(s) {
22815 if (!s.match(/:/)) {
22818 var kv = s.split(":");
22819 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22822 // what ever is left... we allow.
22825 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22826 if (!nstyle.length) {
22827 node.removeAttribute('style');
22831 this.iterateChildren(node, this.cleanTableWidths);
22839 domToHTML : function(currentElement, depth, nopadtext) {
22841 depth = depth || 0;
22842 nopadtext = nopadtext || false;
22844 if (!currentElement) {
22845 return this.domToHTML(this.doc.body);
22848 //Roo.log(currentElement);
22850 var allText = false;
22851 var nodeName = currentElement.nodeName;
22852 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22854 if (nodeName == '#text') {
22856 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22861 if (nodeName != 'BODY') {
22864 // Prints the node tagName, such as <A>, <IMG>, etc
22867 for(i = 0; i < currentElement.attributes.length;i++) {
22869 var aname = currentElement.attributes.item(i).name;
22870 if (!currentElement.attributes.item(i).value.length) {
22873 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22876 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22885 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22888 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22893 // Traverse the tree
22895 var currentElementChild = currentElement.childNodes.item(i);
22896 var allText = true;
22897 var innerHTML = '';
22899 while (currentElementChild) {
22900 // Formatting code (indent the tree so it looks nice on the screen)
22901 var nopad = nopadtext;
22902 if (lastnode == 'SPAN') {
22906 if (currentElementChild.nodeName == '#text') {
22907 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22908 toadd = nopadtext ? toadd : toadd.trim();
22909 if (!nopad && toadd.length > 80) {
22910 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22912 innerHTML += toadd;
22915 currentElementChild = currentElement.childNodes.item(i);
22921 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22923 // Recursively traverse the tree structure of the child node
22924 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22925 lastnode = currentElementChild.nodeName;
22927 currentElementChild=currentElement.childNodes.item(i);
22933 // The remaining code is mostly for formatting the tree
22934 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22939 ret+= "</"+tagName+">";
22945 applyBlacklists : function()
22947 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22948 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22952 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22953 if (b.indexOf(tag) > -1) {
22956 this.white.push(tag);
22960 Roo.each(w, function(tag) {
22961 if (b.indexOf(tag) > -1) {
22964 if (this.white.indexOf(tag) > -1) {
22967 this.white.push(tag);
22972 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22973 if (w.indexOf(tag) > -1) {
22976 this.black.push(tag);
22980 Roo.each(b, function(tag) {
22981 if (w.indexOf(tag) > -1) {
22984 if (this.black.indexOf(tag) > -1) {
22987 this.black.push(tag);
22992 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22993 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22997 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22998 if (b.indexOf(tag) > -1) {
23001 this.cwhite.push(tag);
23005 Roo.each(w, function(tag) {
23006 if (b.indexOf(tag) > -1) {
23009 if (this.cwhite.indexOf(tag) > -1) {
23012 this.cwhite.push(tag);
23017 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23018 if (w.indexOf(tag) > -1) {
23021 this.cblack.push(tag);
23025 Roo.each(b, function(tag) {
23026 if (w.indexOf(tag) > -1) {
23029 if (this.cblack.indexOf(tag) > -1) {
23032 this.cblack.push(tag);
23037 setStylesheets : function(stylesheets)
23039 if(typeof(stylesheets) == 'string'){
23040 Roo.get(this.iframe.contentDocument.head).createChild({
23042 rel : 'stylesheet',
23051 Roo.each(stylesheets, function(s) {
23056 Roo.get(_this.iframe.contentDocument.head).createChild({
23058 rel : 'stylesheet',
23067 removeStylesheets : function()
23071 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23076 setStyle : function(style)
23078 Roo.get(this.iframe.contentDocument.head).createChild({
23087 // hide stuff that is not compatible
23101 * @event specialkey
23105 * @cfg {String} fieldClass @hide
23108 * @cfg {String} focusClass @hide
23111 * @cfg {String} autoCreate @hide
23114 * @cfg {String} inputType @hide
23117 * @cfg {String} invalidClass @hide
23120 * @cfg {String} invalidText @hide
23123 * @cfg {String} msgFx @hide
23126 * @cfg {String} validateOnBlur @hide
23130 Roo.HtmlEditorCore.white = [
23131 'area', 'br', 'img', 'input', 'hr', 'wbr',
23133 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23134 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23135 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23136 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23137 'table', 'ul', 'xmp',
23139 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23142 'dir', 'menu', 'ol', 'ul', 'dl',
23148 Roo.HtmlEditorCore.black = [
23149 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23151 'base', 'basefont', 'bgsound', 'blink', 'body',
23152 'frame', 'frameset', 'head', 'html', 'ilayer',
23153 'iframe', 'layer', 'link', 'meta', 'object',
23154 'script', 'style' ,'title', 'xml' // clean later..
23156 Roo.HtmlEditorCore.clean = [
23157 'script', 'style', 'title', 'xml'
23159 Roo.HtmlEditorCore.remove = [
23164 Roo.HtmlEditorCore.ablack = [
23168 Roo.HtmlEditorCore.aclean = [
23169 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23173 Roo.HtmlEditorCore.pwhite= [
23174 'http', 'https', 'mailto'
23177 // white listed style attributes.
23178 Roo.HtmlEditorCore.cwhite= [
23179 // 'text-align', /// default is to allow most things..
23185 // black listed style attributes.
23186 Roo.HtmlEditorCore.cblack= [
23187 // 'font-size' -- this can be set by the project
23191 Roo.HtmlEditorCore.swapCodes =[
23210 * @class Roo.bootstrap.HtmlEditor
23211 * @extends Roo.bootstrap.TextArea
23212 * Bootstrap HtmlEditor class
23215 * Create a new HtmlEditor
23216 * @param {Object} config The config object
23219 Roo.bootstrap.HtmlEditor = function(config){
23220 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23221 if (!this.toolbars) {
23222 this.toolbars = [];
23225 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23228 * @event initialize
23229 * Fires when the editor is fully initialized (including the iframe)
23230 * @param {HtmlEditor} this
23235 * Fires when the editor is first receives the focus. Any insertion must wait
23236 * until after this event.
23237 * @param {HtmlEditor} this
23241 * @event beforesync
23242 * Fires before the textarea is updated with content from the editor iframe. Return false
23243 * to cancel the sync.
23244 * @param {HtmlEditor} this
23245 * @param {String} html
23249 * @event beforepush
23250 * Fires before the iframe editor is updated with content from the textarea. Return false
23251 * to cancel the push.
23252 * @param {HtmlEditor} this
23253 * @param {String} html
23258 * Fires when the textarea is updated with content from the editor iframe.
23259 * @param {HtmlEditor} this
23260 * @param {String} html
23265 * Fires when the iframe editor is updated with content from the textarea.
23266 * @param {HtmlEditor} this
23267 * @param {String} html
23271 * @event editmodechange
23272 * Fires when the editor switches edit modes
23273 * @param {HtmlEditor} this
23274 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23276 editmodechange: true,
23278 * @event editorevent
23279 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23280 * @param {HtmlEditor} this
23284 * @event firstfocus
23285 * Fires when on first focus - needed by toolbars..
23286 * @param {HtmlEditor} this
23291 * Auto save the htmlEditor value as a file into Events
23292 * @param {HtmlEditor} this
23296 * @event savedpreview
23297 * preview the saved version of htmlEditor
23298 * @param {HtmlEditor} this
23305 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23309 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23314 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23319 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23324 * @cfg {Number} height (in pixels)
23328 * @cfg {Number} width (in pixels)
23333 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23336 stylesheets: false,
23341 // private properties
23342 validationEvent : false,
23344 initialized : false,
23347 onFocus : Roo.emptyFn,
23349 hideMode:'offsets',
23351 tbContainer : false,
23355 toolbarContainer :function() {
23356 return this.wrap.select('.x-html-editor-tb',true).first();
23360 * Protected method that will not generally be called directly. It
23361 * is called when the editor creates its toolbar. Override this method if you need to
23362 * add custom toolbar buttons.
23363 * @param {HtmlEditor} editor
23365 createToolbar : function(){
23366 Roo.log('renewing');
23367 Roo.log("create toolbars");
23369 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23370 this.toolbars[0].render(this.toolbarContainer());
23374 // if (!editor.toolbars || !editor.toolbars.length) {
23375 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23378 // for (var i =0 ; i < editor.toolbars.length;i++) {
23379 // editor.toolbars[i] = Roo.factory(
23380 // typeof(editor.toolbars[i]) == 'string' ?
23381 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23382 // Roo.bootstrap.HtmlEditor);
23383 // editor.toolbars[i].init(editor);
23389 onRender : function(ct, position)
23391 // Roo.log("Call onRender: " + this.xtype);
23393 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23395 this.wrap = this.inputEl().wrap({
23396 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23399 this.editorcore.onRender(ct, position);
23401 if (this.resizable) {
23402 this.resizeEl = new Roo.Resizable(this.wrap, {
23406 minHeight : this.height,
23407 height: this.height,
23408 handles : this.resizable,
23411 resize : function(r, w, h) {
23412 _t.onResize(w,h); // -something
23418 this.createToolbar(this);
23421 if(!this.width && this.resizable){
23422 this.setSize(this.wrap.getSize());
23424 if (this.resizeEl) {
23425 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23426 // should trigger onReize..
23432 onResize : function(w, h)
23434 Roo.log('resize: ' +w + ',' + h );
23435 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23439 if(this.inputEl() ){
23440 if(typeof w == 'number'){
23441 var aw = w - this.wrap.getFrameWidth('lr');
23442 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23445 if(typeof h == 'number'){
23446 var tbh = -11; // fixme it needs to tool bar size!
23447 for (var i =0; i < this.toolbars.length;i++) {
23448 // fixme - ask toolbars for heights?
23449 tbh += this.toolbars[i].el.getHeight();
23450 //if (this.toolbars[i].footer) {
23451 // tbh += this.toolbars[i].footer.el.getHeight();
23459 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23460 ah -= 5; // knock a few pixes off for look..
23461 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23465 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23466 this.editorcore.onResize(ew,eh);
23471 * Toggles the editor between standard and source edit mode.
23472 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23474 toggleSourceEdit : function(sourceEditMode)
23476 this.editorcore.toggleSourceEdit(sourceEditMode);
23478 if(this.editorcore.sourceEditMode){
23479 Roo.log('editor - showing textarea');
23482 // Roo.log(this.syncValue());
23484 this.inputEl().removeClass(['hide', 'x-hidden']);
23485 this.inputEl().dom.removeAttribute('tabIndex');
23486 this.inputEl().focus();
23488 Roo.log('editor - hiding textarea');
23490 // Roo.log(this.pushValue());
23493 this.inputEl().addClass(['hide', 'x-hidden']);
23494 this.inputEl().dom.setAttribute('tabIndex', -1);
23495 //this.deferFocus();
23498 if(this.resizable){
23499 this.setSize(this.wrap.getSize());
23502 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23505 // private (for BoxComponent)
23506 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23508 // private (for BoxComponent)
23509 getResizeEl : function(){
23513 // private (for BoxComponent)
23514 getPositionEl : function(){
23519 initEvents : function(){
23520 this.originalValue = this.getValue();
23524 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23527 // markInvalid : Roo.emptyFn,
23529 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23532 // clearInvalid : Roo.emptyFn,
23534 setValue : function(v){
23535 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23536 this.editorcore.pushValue();
23541 deferFocus : function(){
23542 this.focus.defer(10, this);
23546 focus : function(){
23547 this.editorcore.focus();
23553 onDestroy : function(){
23559 for (var i =0; i < this.toolbars.length;i++) {
23560 // fixme - ask toolbars for heights?
23561 this.toolbars[i].onDestroy();
23564 this.wrap.dom.innerHTML = '';
23565 this.wrap.remove();
23570 onFirstFocus : function(){
23571 //Roo.log("onFirstFocus");
23572 this.editorcore.onFirstFocus();
23573 for (var i =0; i < this.toolbars.length;i++) {
23574 this.toolbars[i].onFirstFocus();
23580 syncValue : function()
23582 this.editorcore.syncValue();
23585 pushValue : function()
23587 this.editorcore.pushValue();
23591 // hide stuff that is not compatible
23605 * @event specialkey
23609 * @cfg {String} fieldClass @hide
23612 * @cfg {String} focusClass @hide
23615 * @cfg {String} autoCreate @hide
23618 * @cfg {String} inputType @hide
23621 * @cfg {String} invalidClass @hide
23624 * @cfg {String} invalidText @hide
23627 * @cfg {String} msgFx @hide
23630 * @cfg {String} validateOnBlur @hide
23639 Roo.namespace('Roo.bootstrap.htmleditor');
23641 * @class Roo.bootstrap.HtmlEditorToolbar1
23646 new Roo.bootstrap.HtmlEditor({
23649 new Roo.bootstrap.HtmlEditorToolbar1({
23650 disable : { fonts: 1 , format: 1, ..., ... , ...],
23656 * @cfg {Object} disable List of elements to disable..
23657 * @cfg {Array} btns List of additional buttons.
23661 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23664 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23667 Roo.apply(this, config);
23669 // default disabled, based on 'good practice'..
23670 this.disable = this.disable || {};
23671 Roo.applyIf(this.disable, {
23674 specialElements : true
23676 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23678 this.editor = config.editor;
23679 this.editorcore = config.editor.editorcore;
23681 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23683 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23684 // dont call parent... till later.
23686 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23691 editorcore : false,
23696 "h1","h2","h3","h4","h5","h6",
23698 "abbr", "acronym", "address", "cite", "samp", "var",
23702 onRender : function(ct, position)
23704 // Roo.log("Call onRender: " + this.xtype);
23706 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23708 this.el.dom.style.marginBottom = '0';
23710 var editorcore = this.editorcore;
23711 var editor= this.editor;
23714 var btn = function(id,cmd , toggle, handler, html){
23716 var event = toggle ? 'toggle' : 'click';
23721 xns: Roo.bootstrap,
23724 enableToggle:toggle !== false,
23726 pressed : toggle ? false : null,
23729 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23730 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23736 // var cb_box = function...
23741 xns: Roo.bootstrap,
23742 glyphicon : 'font',
23746 xns: Roo.bootstrap,
23750 Roo.each(this.formats, function(f) {
23751 style.menu.items.push({
23753 xns: Roo.bootstrap,
23754 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23759 editorcore.insertTag(this.tagname);
23766 children.push(style);
23768 btn('bold',false,true);
23769 btn('italic',false,true);
23770 btn('align-left', 'justifyleft',true);
23771 btn('align-center', 'justifycenter',true);
23772 btn('align-right' , 'justifyright',true);
23773 btn('link', false, false, function(btn) {
23774 //Roo.log("create link?");
23775 var url = prompt(this.createLinkText, this.defaultLinkValue);
23776 if(url && url != 'http:/'+'/'){
23777 this.editorcore.relayCmd('createlink', url);
23780 btn('list','insertunorderedlist',true);
23781 btn('pencil', false,true, function(btn){
23783 this.toggleSourceEdit(btn.pressed);
23786 if (this.editor.btns.length > 0) {
23787 for (var i = 0; i<this.editor.btns.length; i++) {
23788 children.push(this.editor.btns[i]);
23796 xns: Roo.bootstrap,
23801 xns: Roo.bootstrap,
23806 cog.menu.items.push({
23808 xns: Roo.bootstrap,
23809 html : Clean styles,
23814 editorcore.insertTag(this.tagname);
23823 this.xtype = 'NavSimplebar';
23825 for(var i=0;i< children.length;i++) {
23827 this.buttons.add(this.addxtypeChild(children[i]));
23831 editor.on('editorevent', this.updateToolbar, this);
23833 onBtnClick : function(id)
23835 this.editorcore.relayCmd(id);
23836 this.editorcore.focus();
23840 * Protected method that will not generally be called directly. It triggers
23841 * a toolbar update by reading the markup state of the current selection in the editor.
23843 updateToolbar: function(){
23845 if(!this.editorcore.activated){
23846 this.editor.onFirstFocus(); // is this neeed?
23850 var btns = this.buttons;
23851 var doc = this.editorcore.doc;
23852 btns.get('bold').setActive(doc.queryCommandState('bold'));
23853 btns.get('italic').setActive(doc.queryCommandState('italic'));
23854 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23856 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23857 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23858 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23860 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23861 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23864 var ans = this.editorcore.getAllAncestors();
23865 if (this.formatCombo) {
23868 var store = this.formatCombo.store;
23869 this.formatCombo.setValue("");
23870 for (var i =0; i < ans.length;i++) {
23871 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23873 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23881 // hides menus... - so this cant be on a menu...
23882 Roo.bootstrap.MenuMgr.hideAll();
23884 Roo.bootstrap.MenuMgr.hideAll();
23885 //this.editorsyncValue();
23887 onFirstFocus: function() {
23888 this.buttons.each(function(item){
23892 toggleSourceEdit : function(sourceEditMode){
23895 if(sourceEditMode){
23896 Roo.log("disabling buttons");
23897 this.buttons.each( function(item){
23898 if(item.cmd != 'pencil'){
23904 Roo.log("enabling buttons");
23905 if(this.editorcore.initialized){
23906 this.buttons.each( function(item){
23912 Roo.log("calling toggole on editor");
23913 // tell the editor that it's been pressed..
23914 this.editor.toggleSourceEdit(sourceEditMode);
23924 * @class Roo.bootstrap.Table.AbstractSelectionModel
23925 * @extends Roo.util.Observable
23926 * Abstract base class for grid SelectionModels. It provides the interface that should be
23927 * implemented by descendant classes. This class should not be directly instantiated.
23930 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23931 this.locked = false;
23932 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23936 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23937 /** @ignore Called by the grid automatically. Do not call directly. */
23938 init : function(grid){
23944 * Locks the selections.
23947 this.locked = true;
23951 * Unlocks the selections.
23953 unlock : function(){
23954 this.locked = false;
23958 * Returns true if the selections are locked.
23959 * @return {Boolean}
23961 isLocked : function(){
23962 return this.locked;
23966 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23967 * @class Roo.bootstrap.Table.RowSelectionModel
23968 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23969 * It supports multiple selections and keyboard selection/navigation.
23971 * @param {Object} config
23974 Roo.bootstrap.Table.RowSelectionModel = function(config){
23975 Roo.apply(this, config);
23976 this.selections = new Roo.util.MixedCollection(false, function(o){
23981 this.lastActive = false;
23985 * @event selectionchange
23986 * Fires when the selection changes
23987 * @param {SelectionModel} this
23989 "selectionchange" : true,
23991 * @event afterselectionchange
23992 * Fires after the selection changes (eg. by key press or clicking)
23993 * @param {SelectionModel} this
23995 "afterselectionchange" : true,
23997 * @event beforerowselect
23998 * Fires when a row is selected being selected, return false to cancel.
23999 * @param {SelectionModel} this
24000 * @param {Number} rowIndex The selected index
24001 * @param {Boolean} keepExisting False if other selections will be cleared
24003 "beforerowselect" : true,
24006 * Fires when a row is selected.
24007 * @param {SelectionModel} this
24008 * @param {Number} rowIndex The selected index
24009 * @param {Roo.data.Record} r The record
24011 "rowselect" : true,
24013 * @event rowdeselect
24014 * Fires when a row is deselected.
24015 * @param {SelectionModel} this
24016 * @param {Number} rowIndex The selected index
24018 "rowdeselect" : true
24020 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24021 this.locked = false;
24024 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24026 * @cfg {Boolean} singleSelect
24027 * True to allow selection of only one row at a time (defaults to false)
24029 singleSelect : false,
24032 initEvents : function()
24035 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24036 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24037 //}else{ // allow click to work like normal
24038 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24040 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24041 this.grid.on("rowclick", this.handleMouseDown, this);
24043 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24044 "up" : function(e){
24046 this.selectPrevious(e.shiftKey);
24047 }else if(this.last !== false && this.lastActive !== false){
24048 var last = this.last;
24049 this.selectRange(this.last, this.lastActive-1);
24050 this.grid.getView().focusRow(this.lastActive);
24051 if(last !== false){
24055 this.selectFirstRow();
24057 this.fireEvent("afterselectionchange", this);
24059 "down" : function(e){
24061 this.selectNext(e.shiftKey);
24062 }else if(this.last !== false && this.lastActive !== false){
24063 var last = this.last;
24064 this.selectRange(this.last, this.lastActive+1);
24065 this.grid.getView().focusRow(this.lastActive);
24066 if(last !== false){
24070 this.selectFirstRow();
24072 this.fireEvent("afterselectionchange", this);
24076 this.grid.store.on('load', function(){
24077 this.selections.clear();
24080 var view = this.grid.view;
24081 view.on("refresh", this.onRefresh, this);
24082 view.on("rowupdated", this.onRowUpdated, this);
24083 view.on("rowremoved", this.onRemove, this);
24088 onRefresh : function()
24090 var ds = this.grid.store, i, v = this.grid.view;
24091 var s = this.selections;
24092 s.each(function(r){
24093 if((i = ds.indexOfId(r.id)) != -1){
24102 onRemove : function(v, index, r){
24103 this.selections.remove(r);
24107 onRowUpdated : function(v, index, r){
24108 if(this.isSelected(r)){
24109 v.onRowSelect(index);
24115 * @param {Array} records The records to select
24116 * @param {Boolean} keepExisting (optional) True to keep existing selections
24118 selectRecords : function(records, keepExisting)
24121 this.clearSelections();
24123 var ds = this.grid.store;
24124 for(var i = 0, len = records.length; i < len; i++){
24125 this.selectRow(ds.indexOf(records[i]), true);
24130 * Gets the number of selected rows.
24133 getCount : function(){
24134 return this.selections.length;
24138 * Selects the first row in the grid.
24140 selectFirstRow : function(){
24145 * Select the last row.
24146 * @param {Boolean} keepExisting (optional) True to keep existing selections
24148 selectLastRow : function(keepExisting){
24149 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24150 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24154 * Selects the row immediately following the last selected row.
24155 * @param {Boolean} keepExisting (optional) True to keep existing selections
24157 selectNext : function(keepExisting)
24159 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24160 this.selectRow(this.last+1, keepExisting);
24161 this.grid.getView().focusRow(this.last);
24166 * Selects the row that precedes the last selected row.
24167 * @param {Boolean} keepExisting (optional) True to keep existing selections
24169 selectPrevious : function(keepExisting){
24171 this.selectRow(this.last-1, keepExisting);
24172 this.grid.getView().focusRow(this.last);
24177 * Returns the selected records
24178 * @return {Array} Array of selected records
24180 getSelections : function(){
24181 return [].concat(this.selections.items);
24185 * Returns the first selected record.
24188 getSelected : function(){
24189 return this.selections.itemAt(0);
24194 * Clears all selections.
24196 clearSelections : function(fast)
24202 var ds = this.grid.store;
24203 var s = this.selections;
24204 s.each(function(r){
24205 this.deselectRow(ds.indexOfId(r.id));
24209 this.selections.clear();
24216 * Selects all rows.
24218 selectAll : function(){
24222 this.selections.clear();
24223 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24224 this.selectRow(i, true);
24229 * Returns True if there is a selection.
24230 * @return {Boolean}
24232 hasSelection : function(){
24233 return this.selections.length > 0;
24237 * Returns True if the specified row is selected.
24238 * @param {Number/Record} record The record or index of the record to check
24239 * @return {Boolean}
24241 isSelected : function(index){
24242 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24243 return (r && this.selections.key(r.id) ? true : false);
24247 * Returns True if the specified record id is selected.
24248 * @param {String} id The id of record to check
24249 * @return {Boolean}
24251 isIdSelected : function(id){
24252 return (this.selections.key(id) ? true : false);
24257 handleMouseDBClick : function(e, t){
24261 handleMouseDown : function(e, t)
24263 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24264 if(this.isLocked() || rowIndex < 0 ){
24267 if(e.shiftKey && this.last !== false){
24268 var last = this.last;
24269 this.selectRange(last, rowIndex, e.ctrlKey);
24270 this.last = last; // reset the last
24274 var isSelected = this.isSelected(rowIndex);
24275 //Roo.log("select row:" + rowIndex);
24277 this.deselectRow(rowIndex);
24279 this.selectRow(rowIndex, true);
24283 if(e.button !== 0 && isSelected){
24284 alert('rowIndex 2: ' + rowIndex);
24285 view.focusRow(rowIndex);
24286 }else if(e.ctrlKey && isSelected){
24287 this.deselectRow(rowIndex);
24288 }else if(!isSelected){
24289 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24290 view.focusRow(rowIndex);
24294 this.fireEvent("afterselectionchange", this);
24297 handleDragableRowClick : function(grid, rowIndex, e)
24299 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24300 this.selectRow(rowIndex, false);
24301 grid.view.focusRow(rowIndex);
24302 this.fireEvent("afterselectionchange", this);
24307 * Selects multiple rows.
24308 * @param {Array} rows Array of the indexes of the row to select
24309 * @param {Boolean} keepExisting (optional) True to keep existing selections
24311 selectRows : function(rows, keepExisting){
24313 this.clearSelections();
24315 for(var i = 0, len = rows.length; i < len; i++){
24316 this.selectRow(rows[i], true);
24321 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24322 * @param {Number} startRow The index of the first row in the range
24323 * @param {Number} endRow The index of the last row in the range
24324 * @param {Boolean} keepExisting (optional) True to retain existing selections
24326 selectRange : function(startRow, endRow, keepExisting){
24331 this.clearSelections();
24333 if(startRow <= endRow){
24334 for(var i = startRow; i <= endRow; i++){
24335 this.selectRow(i, true);
24338 for(var i = startRow; i >= endRow; i--){
24339 this.selectRow(i, true);
24345 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24346 * @param {Number} startRow The index of the first row in the range
24347 * @param {Number} endRow The index of the last row in the range
24349 deselectRange : function(startRow, endRow, preventViewNotify){
24353 for(var i = startRow; i <= endRow; i++){
24354 this.deselectRow(i, preventViewNotify);
24360 * @param {Number} row The index of the row to select
24361 * @param {Boolean} keepExisting (optional) True to keep existing selections
24363 selectRow : function(index, keepExisting, preventViewNotify)
24365 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24368 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24369 if(!keepExisting || this.singleSelect){
24370 this.clearSelections();
24373 var r = this.grid.store.getAt(index);
24374 //console.log('selectRow - record id :' + r.id);
24376 this.selections.add(r);
24377 this.last = this.lastActive = index;
24378 if(!preventViewNotify){
24379 var proxy = new Roo.Element(
24380 this.grid.getRowDom(index)
24382 proxy.addClass('bg-info info');
24384 this.fireEvent("rowselect", this, index, r);
24385 this.fireEvent("selectionchange", this);
24391 * @param {Number} row The index of the row to deselect
24393 deselectRow : function(index, preventViewNotify)
24398 if(this.last == index){
24401 if(this.lastActive == index){
24402 this.lastActive = false;
24405 var r = this.grid.store.getAt(index);
24410 this.selections.remove(r);
24411 //.console.log('deselectRow - record id :' + r.id);
24412 if(!preventViewNotify){
24414 var proxy = new Roo.Element(
24415 this.grid.getRowDom(index)
24417 proxy.removeClass('bg-info info');
24419 this.fireEvent("rowdeselect", this, index);
24420 this.fireEvent("selectionchange", this);
24424 restoreLast : function(){
24426 this.last = this._last;
24431 acceptsNav : function(row, col, cm){
24432 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24436 onEditorKey : function(field, e){
24437 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24442 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24444 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24446 }else if(k == e.ENTER && !e.ctrlKey){
24450 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24452 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24454 }else if(k == e.ESC){
24458 g.startEditing(newCell[0], newCell[1]);
24464 * Ext JS Library 1.1.1
24465 * Copyright(c) 2006-2007, Ext JS, LLC.
24467 * Originally Released Under LGPL - original licence link has changed is not relivant.
24470 * <script type="text/javascript">
24474 * @class Roo.bootstrap.PagingToolbar
24475 * @extends Roo.bootstrap.NavSimplebar
24476 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24478 * Create a new PagingToolbar
24479 * @param {Object} config The config object
24480 * @param {Roo.data.Store} store
24482 Roo.bootstrap.PagingToolbar = function(config)
24484 // old args format still supported... - xtype is prefered..
24485 // created from xtype...
24487 this.ds = config.dataSource;
24489 if (config.store && !this.ds) {
24490 this.store= Roo.factory(config.store, Roo.data);
24491 this.ds = this.store;
24492 this.ds.xmodule = this.xmodule || false;
24495 this.toolbarItems = [];
24496 if (config.items) {
24497 this.toolbarItems = config.items;
24500 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24505 this.bind(this.ds);
24508 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24512 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24514 * @cfg {Roo.data.Store} dataSource
24515 * The underlying data store providing the paged data
24518 * @cfg {String/HTMLElement/Element} container
24519 * container The id or element that will contain the toolbar
24522 * @cfg {Boolean} displayInfo
24523 * True to display the displayMsg (defaults to false)
24526 * @cfg {Number} pageSize
24527 * The number of records to display per page (defaults to 20)
24531 * @cfg {String} displayMsg
24532 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24534 displayMsg : 'Displaying {0} - {1} of {2}',
24536 * @cfg {String} emptyMsg
24537 * The message to display when no records are found (defaults to "No data to display")
24539 emptyMsg : 'No data to display',
24541 * Customizable piece of the default paging text (defaults to "Page")
24544 beforePageText : "Page",
24546 * Customizable piece of the default paging text (defaults to "of %0")
24549 afterPageText : "of {0}",
24551 * Customizable piece of the default paging text (defaults to "First Page")
24554 firstText : "First Page",
24556 * Customizable piece of the default paging text (defaults to "Previous Page")
24559 prevText : "Previous Page",
24561 * Customizable piece of the default paging text (defaults to "Next Page")
24564 nextText : "Next Page",
24566 * Customizable piece of the default paging text (defaults to "Last Page")
24569 lastText : "Last Page",
24571 * Customizable piece of the default paging text (defaults to "Refresh")
24574 refreshText : "Refresh",
24578 onRender : function(ct, position)
24580 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24581 this.navgroup.parentId = this.id;
24582 this.navgroup.onRender(this.el, null);
24583 // add the buttons to the navgroup
24585 if(this.displayInfo){
24586 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24587 this.displayEl = this.el.select('.x-paging-info', true).first();
24588 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24589 // this.displayEl = navel.el.select('span',true).first();
24595 Roo.each(_this.buttons, function(e){ // this might need to use render????
24596 Roo.factory(e).render(_this.el);
24600 Roo.each(_this.toolbarItems, function(e) {
24601 _this.navgroup.addItem(e);
24605 this.first = this.navgroup.addItem({
24606 tooltip: this.firstText,
24608 icon : 'fa fa-backward',
24610 preventDefault: true,
24611 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24614 this.prev = this.navgroup.addItem({
24615 tooltip: this.prevText,
24617 icon : 'fa fa-step-backward',
24619 preventDefault: true,
24620 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24622 //this.addSeparator();
24625 var field = this.navgroup.addItem( {
24627 cls : 'x-paging-position',
24629 html : this.beforePageText +
24630 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24631 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24634 this.field = field.el.select('input', true).first();
24635 this.field.on("keydown", this.onPagingKeydown, this);
24636 this.field.on("focus", function(){this.dom.select();});
24639 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24640 //this.field.setHeight(18);
24641 //this.addSeparator();
24642 this.next = this.navgroup.addItem({
24643 tooltip: this.nextText,
24645 html : ' <i class="fa fa-step-forward">',
24647 preventDefault: true,
24648 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24650 this.last = this.navgroup.addItem({
24651 tooltip: this.lastText,
24652 icon : 'fa fa-forward',
24655 preventDefault: true,
24656 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24658 //this.addSeparator();
24659 this.loading = this.navgroup.addItem({
24660 tooltip: this.refreshText,
24661 icon: 'fa fa-refresh',
24662 preventDefault: true,
24663 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24669 updateInfo : function(){
24670 if(this.displayEl){
24671 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24672 var msg = count == 0 ?
24676 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24678 this.displayEl.update(msg);
24683 onLoad : function(ds, r, o)
24685 this.cursor = o.params.start ? o.params.start : 0;
24687 var d = this.getPageData(),
24692 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24693 this.field.dom.value = ap;
24694 this.first.setDisabled(ap == 1);
24695 this.prev.setDisabled(ap == 1);
24696 this.next.setDisabled(ap == ps);
24697 this.last.setDisabled(ap == ps);
24698 this.loading.enable();
24703 getPageData : function(){
24704 var total = this.ds.getTotalCount();
24707 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24708 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24713 onLoadError : function(){
24714 this.loading.enable();
24718 onPagingKeydown : function(e){
24719 var k = e.getKey();
24720 var d = this.getPageData();
24722 var v = this.field.dom.value, pageNum;
24723 if(!v || isNaN(pageNum = parseInt(v, 10))){
24724 this.field.dom.value = d.activePage;
24727 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24728 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24731 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))
24733 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24734 this.field.dom.value = pageNum;
24735 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24738 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24740 var v = this.field.dom.value, pageNum;
24741 var increment = (e.shiftKey) ? 10 : 1;
24742 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24745 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24746 this.field.dom.value = d.activePage;
24749 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24751 this.field.dom.value = parseInt(v, 10) + increment;
24752 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24753 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24760 beforeLoad : function(){
24762 this.loading.disable();
24767 onClick : function(which){
24776 ds.load({params:{start: 0, limit: this.pageSize}});
24779 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24782 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24785 var total = ds.getTotalCount();
24786 var extra = total % this.pageSize;
24787 var lastStart = extra ? (total - extra) : total-this.pageSize;
24788 ds.load({params:{start: lastStart, limit: this.pageSize}});
24791 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24797 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24798 * @param {Roo.data.Store} store The data store to unbind
24800 unbind : function(ds){
24801 ds.un("beforeload", this.beforeLoad, this);
24802 ds.un("load", this.onLoad, this);
24803 ds.un("loadexception", this.onLoadError, this);
24804 ds.un("remove", this.updateInfo, this);
24805 ds.un("add", this.updateInfo, this);
24806 this.ds = undefined;
24810 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24811 * @param {Roo.data.Store} store The data store to bind
24813 bind : function(ds){
24814 ds.on("beforeload", this.beforeLoad, this);
24815 ds.on("load", this.onLoad, this);
24816 ds.on("loadexception", this.onLoadError, this);
24817 ds.on("remove", this.updateInfo, this);
24818 ds.on("add", this.updateInfo, this);
24829 * @class Roo.bootstrap.MessageBar
24830 * @extends Roo.bootstrap.Component
24831 * Bootstrap MessageBar class
24832 * @cfg {String} html contents of the MessageBar
24833 * @cfg {String} weight (info | success | warning | danger) default info
24834 * @cfg {String} beforeClass insert the bar before the given class
24835 * @cfg {Boolean} closable (true | false) default false
24836 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24839 * Create a new Element
24840 * @param {Object} config The config object
24843 Roo.bootstrap.MessageBar = function(config){
24844 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24847 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24853 beforeClass: 'bootstrap-sticky-wrap',
24855 getAutoCreate : function(){
24859 cls: 'alert alert-dismissable alert-' + this.weight,
24864 html: this.html || ''
24870 cfg.cls += ' alert-messages-fixed';
24884 onRender : function(ct, position)
24886 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24889 var cfg = Roo.apply({}, this.getAutoCreate());
24893 cfg.cls += ' ' + this.cls;
24896 cfg.style = this.style;
24898 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24900 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24903 this.el.select('>button.close').on('click', this.hide, this);
24909 if (!this.rendered) {
24915 this.fireEvent('show', this);
24921 if (!this.rendered) {
24927 this.fireEvent('hide', this);
24930 update : function()
24932 // var e = this.el.dom.firstChild;
24934 // if(this.closable){
24935 // e = e.nextSibling;
24938 // e.data = this.html || '';
24940 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24956 * @class Roo.bootstrap.Graph
24957 * @extends Roo.bootstrap.Component
24958 * Bootstrap Graph class
24962 @cfg {String} graphtype bar | vbar | pie
24963 @cfg {number} g_x coodinator | centre x (pie)
24964 @cfg {number} g_y coodinator | centre y (pie)
24965 @cfg {number} g_r radius (pie)
24966 @cfg {number} g_height height of the chart (respected by all elements in the set)
24967 @cfg {number} g_width width of the chart (respected by all elements in the set)
24968 @cfg {Object} title The title of the chart
24971 -opts (object) options for the chart
24973 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24974 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24976 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.
24977 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24979 o stretch (boolean)
24981 -opts (object) options for the pie
24984 o startAngle (number)
24985 o endAngle (number)
24989 * Create a new Input
24990 * @param {Object} config The config object
24993 Roo.bootstrap.Graph = function(config){
24994 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25000 * The img click event for the img.
25001 * @param {Roo.EventObject} e
25007 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25018 //g_colors: this.colors,
25025 getAutoCreate : function(){
25036 onRender : function(ct,position){
25039 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25041 if (typeof(Raphael) == 'undefined') {
25042 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25046 this.raphael = Raphael(this.el.dom);
25048 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25049 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25050 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25051 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25053 r.text(160, 10, "Single Series Chart").attr(txtattr);
25054 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25055 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25056 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25058 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25059 r.barchart(330, 10, 300, 220, data1);
25060 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25061 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25064 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25065 // r.barchart(30, 30, 560, 250, xdata, {
25066 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25067 // axis : "0 0 1 1",
25068 // axisxlabels : xdata
25069 // //yvalues : cols,
25072 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25074 // this.load(null,xdata,{
25075 // axis : "0 0 1 1",
25076 // axisxlabels : xdata
25081 load : function(graphtype,xdata,opts)
25083 this.raphael.clear();
25085 graphtype = this.graphtype;
25090 var r = this.raphael,
25091 fin = function () {
25092 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25094 fout = function () {
25095 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25097 pfin = function() {
25098 this.sector.stop();
25099 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25102 this.label[0].stop();
25103 this.label[0].attr({ r: 7.5 });
25104 this.label[1].attr({ "font-weight": 800 });
25107 pfout = function() {
25108 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25111 this.label[0].animate({ r: 5 }, 500, "bounce");
25112 this.label[1].attr({ "font-weight": 400 });
25118 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25121 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25124 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25125 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25127 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25134 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25139 setTitle: function(o)
25144 initEvents: function() {
25147 this.el.on('click', this.onClick, this);
25151 onClick : function(e)
25153 Roo.log('img onclick');
25154 this.fireEvent('click', this, e);
25166 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25169 * @class Roo.bootstrap.dash.NumberBox
25170 * @extends Roo.bootstrap.Component
25171 * Bootstrap NumberBox class
25172 * @cfg {String} headline Box headline
25173 * @cfg {String} content Box content
25174 * @cfg {String} icon Box icon
25175 * @cfg {String} footer Footer text
25176 * @cfg {String} fhref Footer href
25179 * Create a new NumberBox
25180 * @param {Object} config The config object
25184 Roo.bootstrap.dash.NumberBox = function(config){
25185 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25189 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25198 getAutoCreate : function(){
25202 cls : 'small-box ',
25210 cls : 'roo-headline',
25211 html : this.headline
25215 cls : 'roo-content',
25216 html : this.content
25230 cls : 'ion ' + this.icon
25239 cls : 'small-box-footer',
25240 href : this.fhref || '#',
25244 cfg.cn.push(footer);
25251 onRender : function(ct,position){
25252 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25259 setHeadline: function (value)
25261 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25264 setFooter: function (value, href)
25266 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25269 this.el.select('a.small-box-footer',true).first().attr('href', href);
25274 setContent: function (value)
25276 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25279 initEvents: function()
25293 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25296 * @class Roo.bootstrap.dash.TabBox
25297 * @extends Roo.bootstrap.Component
25298 * Bootstrap TabBox class
25299 * @cfg {String} title Title of the TabBox
25300 * @cfg {String} icon Icon of the TabBox
25301 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25302 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25305 * Create a new TabBox
25306 * @param {Object} config The config object
25310 Roo.bootstrap.dash.TabBox = function(config){
25311 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25316 * When a pane is added
25317 * @param {Roo.bootstrap.dash.TabPane} pane
25321 * @event activatepane
25322 * When a pane is activated
25323 * @param {Roo.bootstrap.dash.TabPane} pane
25325 "activatepane" : true
25333 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25338 tabScrollable : false,
25340 getChildContainer : function()
25342 return this.el.select('.tab-content', true).first();
25345 getAutoCreate : function(){
25349 cls: 'pull-left header',
25357 cls: 'fa ' + this.icon
25363 cls: 'nav nav-tabs pull-right',
25369 if(this.tabScrollable){
25376 cls: 'nav nav-tabs pull-right',
25387 cls: 'nav-tabs-custom',
25392 cls: 'tab-content no-padding',
25400 initEvents : function()
25402 //Roo.log('add add pane handler');
25403 this.on('addpane', this.onAddPane, this);
25406 * Updates the box title
25407 * @param {String} html to set the title to.
25409 setTitle : function(value)
25411 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25413 onAddPane : function(pane)
25415 this.panes.push(pane);
25416 //Roo.log('addpane');
25418 // tabs are rendere left to right..
25419 if(!this.showtabs){
25423 var ctr = this.el.select('.nav-tabs', true).first();
25426 var existing = ctr.select('.nav-tab',true);
25427 var qty = existing.getCount();;
25430 var tab = ctr.createChild({
25432 cls : 'nav-tab' + (qty ? '' : ' active'),
25440 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25443 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25445 pane.el.addClass('active');
25450 onTabClick : function(ev,un,ob,pane)
25452 //Roo.log('tab - prev default');
25453 ev.preventDefault();
25456 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25457 pane.tab.addClass('active');
25458 //Roo.log(pane.title);
25459 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25460 // technically we should have a deactivate event.. but maybe add later.
25461 // and it should not de-activate the selected tab...
25462 this.fireEvent('activatepane', pane);
25463 pane.el.addClass('active');
25464 pane.fireEvent('activate');
25469 getActivePane : function()
25472 Roo.each(this.panes, function(p) {
25473 if(p.el.hasClass('active')){
25494 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25496 * @class Roo.bootstrap.TabPane
25497 * @extends Roo.bootstrap.Component
25498 * Bootstrap TabPane class
25499 * @cfg {Boolean} active (false | true) Default false
25500 * @cfg {String} title title of panel
25504 * Create a new TabPane
25505 * @param {Object} config The config object
25508 Roo.bootstrap.dash.TabPane = function(config){
25509 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25515 * When a pane is activated
25516 * @param {Roo.bootstrap.dash.TabPane} pane
25523 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25528 // the tabBox that this is attached to.
25531 getAutoCreate : function()
25539 cfg.cls += ' active';
25544 initEvents : function()
25546 //Roo.log('trigger add pane handler');
25547 this.parent().fireEvent('addpane', this)
25551 * Updates the tab title
25552 * @param {String} html to set the title to.
25554 setTitle: function(str)
25560 this.tab.select('a', true).first().dom.innerHTML = str;
25577 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25580 * @class Roo.bootstrap.menu.Menu
25581 * @extends Roo.bootstrap.Component
25582 * Bootstrap Menu class - container for Menu
25583 * @cfg {String} html Text of the menu
25584 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25585 * @cfg {String} icon Font awesome icon
25586 * @cfg {String} pos Menu align to (top | bottom) default bottom
25590 * Create a new Menu
25591 * @param {Object} config The config object
25595 Roo.bootstrap.menu.Menu = function(config){
25596 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25600 * @event beforeshow
25601 * Fires before this menu is displayed
25602 * @param {Roo.bootstrap.menu.Menu} this
25606 * @event beforehide
25607 * Fires before this menu is hidden
25608 * @param {Roo.bootstrap.menu.Menu} this
25613 * Fires after this menu is displayed
25614 * @param {Roo.bootstrap.menu.Menu} this
25619 * Fires after this menu is hidden
25620 * @param {Roo.bootstrap.menu.Menu} this
25625 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25626 * @param {Roo.bootstrap.menu.Menu} this
25627 * @param {Roo.EventObject} e
25634 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25638 weight : 'default',
25643 getChildContainer : function() {
25644 if(this.isSubMenu){
25648 return this.el.select('ul.dropdown-menu', true).first();
25651 getAutoCreate : function()
25656 cls : 'roo-menu-text',
25664 cls : 'fa ' + this.icon
25675 cls : 'dropdown-button btn btn-' + this.weight,
25680 cls : 'dropdown-toggle btn btn-' + this.weight,
25690 cls : 'dropdown-menu'
25696 if(this.pos == 'top'){
25697 cfg.cls += ' dropup';
25700 if(this.isSubMenu){
25703 cls : 'dropdown-menu'
25710 onRender : function(ct, position)
25712 this.isSubMenu = ct.hasClass('dropdown-submenu');
25714 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25717 initEvents : function()
25719 if(this.isSubMenu){
25723 this.hidden = true;
25725 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25726 this.triggerEl.on('click', this.onTriggerPress, this);
25728 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25729 this.buttonEl.on('click', this.onClick, this);
25735 if(this.isSubMenu){
25739 return this.el.select('ul.dropdown-menu', true).first();
25742 onClick : function(e)
25744 this.fireEvent("click", this, e);
25747 onTriggerPress : function(e)
25749 if (this.isVisible()) {
25756 isVisible : function(){
25757 return !this.hidden;
25762 this.fireEvent("beforeshow", this);
25764 this.hidden = false;
25765 this.el.addClass('open');
25767 Roo.get(document).on("mouseup", this.onMouseUp, this);
25769 this.fireEvent("show", this);
25776 this.fireEvent("beforehide", this);
25778 this.hidden = true;
25779 this.el.removeClass('open');
25781 Roo.get(document).un("mouseup", this.onMouseUp);
25783 this.fireEvent("hide", this);
25786 onMouseUp : function()
25800 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25803 * @class Roo.bootstrap.menu.Item
25804 * @extends Roo.bootstrap.Component
25805 * Bootstrap MenuItem class
25806 * @cfg {Boolean} submenu (true | false) default false
25807 * @cfg {String} html text of the item
25808 * @cfg {String} href the link
25809 * @cfg {Boolean} disable (true | false) default false
25810 * @cfg {Boolean} preventDefault (true | false) default true
25811 * @cfg {String} icon Font awesome icon
25812 * @cfg {String} pos Submenu align to (left | right) default right
25816 * Create a new Item
25817 * @param {Object} config The config object
25821 Roo.bootstrap.menu.Item = function(config){
25822 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25826 * Fires when the mouse is hovering over this menu
25827 * @param {Roo.bootstrap.menu.Item} this
25828 * @param {Roo.EventObject} e
25833 * Fires when the mouse exits this menu
25834 * @param {Roo.bootstrap.menu.Item} this
25835 * @param {Roo.EventObject} e
25841 * The raw click event for the entire grid.
25842 * @param {Roo.EventObject} e
25848 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25853 preventDefault: true,
25858 getAutoCreate : function()
25863 cls : 'roo-menu-item-text',
25871 cls : 'fa ' + this.icon
25880 href : this.href || '#',
25887 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25891 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25893 if(this.pos == 'left'){
25894 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25901 initEvents : function()
25903 this.el.on('mouseover', this.onMouseOver, this);
25904 this.el.on('mouseout', this.onMouseOut, this);
25906 this.el.select('a', true).first().on('click', this.onClick, this);
25910 onClick : function(e)
25912 if(this.preventDefault){
25913 e.preventDefault();
25916 this.fireEvent("click", this, e);
25919 onMouseOver : function(e)
25921 if(this.submenu && this.pos == 'left'){
25922 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25925 this.fireEvent("mouseover", this, e);
25928 onMouseOut : function(e)
25930 this.fireEvent("mouseout", this, e);
25942 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25945 * @class Roo.bootstrap.menu.Separator
25946 * @extends Roo.bootstrap.Component
25947 * Bootstrap Separator class
25950 * Create a new Separator
25951 * @param {Object} config The config object
25955 Roo.bootstrap.menu.Separator = function(config){
25956 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25959 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25961 getAutoCreate : function(){
25982 * @class Roo.bootstrap.Tooltip
25983 * Bootstrap Tooltip class
25984 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25985 * to determine which dom element triggers the tooltip.
25987 * It needs to add support for additional attributes like tooltip-position
25990 * Create a new Toolti
25991 * @param {Object} config The config object
25994 Roo.bootstrap.Tooltip = function(config){
25995 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25997 this.alignment = Roo.bootstrap.Tooltip.alignment;
25999 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26000 this.alignment = config.alignment;
26005 Roo.apply(Roo.bootstrap.Tooltip, {
26007 * @function init initialize tooltip monitoring.
26011 currentTip : false,
26012 currentRegion : false,
26018 Roo.get(document).on('mouseover', this.enter ,this);
26019 Roo.get(document).on('mouseout', this.leave, this);
26022 this.currentTip = new Roo.bootstrap.Tooltip();
26025 enter : function(ev)
26027 var dom = ev.getTarget();
26029 //Roo.log(['enter',dom]);
26030 var el = Roo.fly(dom);
26031 if (this.currentEl) {
26033 //Roo.log(this.currentEl);
26034 //Roo.log(this.currentEl.contains(dom));
26035 if (this.currentEl == el) {
26038 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26044 if (this.currentTip.el) {
26045 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26049 if(!el || el.dom == document){
26055 // you can not look for children, as if el is the body.. then everythign is the child..
26056 if (!el.attr('tooltip')) { //
26057 if (!el.select("[tooltip]").elements.length) {
26060 // is the mouse over this child...?
26061 bindEl = el.select("[tooltip]").first();
26062 var xy = ev.getXY();
26063 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26064 //Roo.log("not in region.");
26067 //Roo.log("child element over..");
26070 this.currentEl = bindEl;
26071 this.currentTip.bind(bindEl);
26072 this.currentRegion = Roo.lib.Region.getRegion(dom);
26073 this.currentTip.enter();
26076 leave : function(ev)
26078 var dom = ev.getTarget();
26079 //Roo.log(['leave',dom]);
26080 if (!this.currentEl) {
26085 if (dom != this.currentEl.dom) {
26088 var xy = ev.getXY();
26089 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26092 // only activate leave if mouse cursor is outside... bounding box..
26097 if (this.currentTip) {
26098 this.currentTip.leave();
26100 //Roo.log('clear currentEl');
26101 this.currentEl = false;
26106 'left' : ['r-l', [-2,0], 'right'],
26107 'right' : ['l-r', [2,0], 'left'],
26108 'bottom' : ['t-b', [0,2], 'top'],
26109 'top' : [ 'b-t', [0,-2], 'bottom']
26115 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26120 delay : null, // can be { show : 300 , hide: 500}
26124 hoverState : null, //???
26126 placement : 'bottom',
26130 getAutoCreate : function(){
26137 cls : 'tooltip-arrow'
26140 cls : 'tooltip-inner'
26147 bind : function(el)
26153 enter : function () {
26155 if (this.timeout != null) {
26156 clearTimeout(this.timeout);
26159 this.hoverState = 'in';
26160 //Roo.log("enter - show");
26161 if (!this.delay || !this.delay.show) {
26166 this.timeout = setTimeout(function () {
26167 if (_t.hoverState == 'in') {
26170 }, this.delay.show);
26174 clearTimeout(this.timeout);
26176 this.hoverState = 'out';
26177 if (!this.delay || !this.delay.hide) {
26183 this.timeout = setTimeout(function () {
26184 //Roo.log("leave - timeout");
26186 if (_t.hoverState == 'out') {
26188 Roo.bootstrap.Tooltip.currentEl = false;
26193 show : function (msg)
26196 this.render(document.body);
26199 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26201 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26203 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26205 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26207 var placement = typeof this.placement == 'function' ?
26208 this.placement.call(this, this.el, on_el) :
26211 var autoToken = /\s?auto?\s?/i;
26212 var autoPlace = autoToken.test(placement);
26214 placement = placement.replace(autoToken, '') || 'top';
26218 //this.el.setXY([0,0]);
26220 //this.el.dom.style.display='block';
26222 //this.el.appendTo(on_el);
26224 var p = this.getPosition();
26225 var box = this.el.getBox();
26231 var align = this.alignment[placement];
26233 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26235 if(placement == 'top' || placement == 'bottom'){
26237 placement = 'right';
26240 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26241 placement = 'left';
26244 var scroll = Roo.select('body', true).first().getScroll();
26246 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26250 align = this.alignment[placement];
26253 this.el.alignTo(this.bindEl, align[0],align[1]);
26254 //var arrow = this.el.select('.arrow',true).first();
26255 //arrow.set(align[2],
26257 this.el.addClass(placement);
26259 this.el.addClass('in fade');
26261 this.hoverState = null;
26263 if (this.el.hasClass('fade')) {
26274 //this.el.setXY([0,0]);
26275 this.el.removeClass('in');
26291 * @class Roo.bootstrap.LocationPicker
26292 * @extends Roo.bootstrap.Component
26293 * Bootstrap LocationPicker class
26294 * @cfg {Number} latitude Position when init default 0
26295 * @cfg {Number} longitude Position when init default 0
26296 * @cfg {Number} zoom default 15
26297 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26298 * @cfg {Boolean} mapTypeControl default false
26299 * @cfg {Boolean} disableDoubleClickZoom default false
26300 * @cfg {Boolean} scrollwheel default true
26301 * @cfg {Boolean} streetViewControl default false
26302 * @cfg {Number} radius default 0
26303 * @cfg {String} locationName
26304 * @cfg {Boolean} draggable default true
26305 * @cfg {Boolean} enableAutocomplete default false
26306 * @cfg {Boolean} enableReverseGeocode default true
26307 * @cfg {String} markerTitle
26310 * Create a new LocationPicker
26311 * @param {Object} config The config object
26315 Roo.bootstrap.LocationPicker = function(config){
26317 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26322 * Fires when the picker initialized.
26323 * @param {Roo.bootstrap.LocationPicker} this
26324 * @param {Google Location} location
26328 * @event positionchanged
26329 * Fires when the picker position changed.
26330 * @param {Roo.bootstrap.LocationPicker} this
26331 * @param {Google Location} location
26333 positionchanged : true,
26336 * Fires when the map resize.
26337 * @param {Roo.bootstrap.LocationPicker} this
26342 * Fires when the map show.
26343 * @param {Roo.bootstrap.LocationPicker} this
26348 * Fires when the map hide.
26349 * @param {Roo.bootstrap.LocationPicker} this
26354 * Fires when click the map.
26355 * @param {Roo.bootstrap.LocationPicker} this
26356 * @param {Map event} e
26360 * @event mapRightClick
26361 * Fires when right click the map.
26362 * @param {Roo.bootstrap.LocationPicker} this
26363 * @param {Map event} e
26365 mapRightClick : true,
26367 * @event markerClick
26368 * Fires when click the marker.
26369 * @param {Roo.bootstrap.LocationPicker} this
26370 * @param {Map event} e
26372 markerClick : true,
26374 * @event markerRightClick
26375 * Fires when right click the marker.
26376 * @param {Roo.bootstrap.LocationPicker} this
26377 * @param {Map event} e
26379 markerRightClick : true,
26381 * @event OverlayViewDraw
26382 * Fires when OverlayView Draw
26383 * @param {Roo.bootstrap.LocationPicker} this
26385 OverlayViewDraw : true,
26387 * @event OverlayViewOnAdd
26388 * Fires when OverlayView Draw
26389 * @param {Roo.bootstrap.LocationPicker} this
26391 OverlayViewOnAdd : true,
26393 * @event OverlayViewOnRemove
26394 * Fires when OverlayView Draw
26395 * @param {Roo.bootstrap.LocationPicker} this
26397 OverlayViewOnRemove : true,
26399 * @event OverlayViewShow
26400 * Fires when OverlayView Draw
26401 * @param {Roo.bootstrap.LocationPicker} this
26402 * @param {Pixel} cpx
26404 OverlayViewShow : true,
26406 * @event OverlayViewHide
26407 * Fires when OverlayView Draw
26408 * @param {Roo.bootstrap.LocationPicker} this
26410 OverlayViewHide : true,
26412 * @event loadexception
26413 * Fires when load google lib failed.
26414 * @param {Roo.bootstrap.LocationPicker} this
26416 loadexception : true
26421 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26423 gMapContext: false,
26429 mapTypeControl: false,
26430 disableDoubleClickZoom: false,
26432 streetViewControl: false,
26436 enableAutocomplete: false,
26437 enableReverseGeocode: true,
26440 getAutoCreate: function()
26445 cls: 'roo-location-picker'
26451 initEvents: function(ct, position)
26453 if(!this.el.getWidth() || this.isApplied()){
26457 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26462 initial: function()
26464 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26465 this.fireEvent('loadexception', this);
26469 if(!this.mapTypeId){
26470 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26473 this.gMapContext = this.GMapContext();
26475 this.initOverlayView();
26477 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26481 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26482 _this.setPosition(_this.gMapContext.marker.position);
26485 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26486 _this.fireEvent('mapClick', this, event);
26490 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26491 _this.fireEvent('mapRightClick', this, event);
26495 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26496 _this.fireEvent('markerClick', this, event);
26500 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26501 _this.fireEvent('markerRightClick', this, event);
26505 this.setPosition(this.gMapContext.location);
26507 this.fireEvent('initial', this, this.gMapContext.location);
26510 initOverlayView: function()
26514 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26518 _this.fireEvent('OverlayViewDraw', _this);
26523 _this.fireEvent('OverlayViewOnAdd', _this);
26526 onRemove: function()
26528 _this.fireEvent('OverlayViewOnRemove', _this);
26531 show: function(cpx)
26533 _this.fireEvent('OverlayViewShow', _this, cpx);
26538 _this.fireEvent('OverlayViewHide', _this);
26544 fromLatLngToContainerPixel: function(event)
26546 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26549 isApplied: function()
26551 return this.getGmapContext() == false ? false : true;
26554 getGmapContext: function()
26556 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26559 GMapContext: function()
26561 var position = new google.maps.LatLng(this.latitude, this.longitude);
26563 var _map = new google.maps.Map(this.el.dom, {
26566 mapTypeId: this.mapTypeId,
26567 mapTypeControl: this.mapTypeControl,
26568 disableDoubleClickZoom: this.disableDoubleClickZoom,
26569 scrollwheel: this.scrollwheel,
26570 streetViewControl: this.streetViewControl,
26571 locationName: this.locationName,
26572 draggable: this.draggable,
26573 enableAutocomplete: this.enableAutocomplete,
26574 enableReverseGeocode: this.enableReverseGeocode
26577 var _marker = new google.maps.Marker({
26578 position: position,
26580 title: this.markerTitle,
26581 draggable: this.draggable
26588 location: position,
26589 radius: this.radius,
26590 locationName: this.locationName,
26591 addressComponents: {
26592 formatted_address: null,
26593 addressLine1: null,
26594 addressLine2: null,
26596 streetNumber: null,
26600 stateOrProvince: null
26603 domContainer: this.el.dom,
26604 geodecoder: new google.maps.Geocoder()
26608 drawCircle: function(center, radius, options)
26610 if (this.gMapContext.circle != null) {
26611 this.gMapContext.circle.setMap(null);
26615 options = Roo.apply({}, options, {
26616 strokeColor: "#0000FF",
26617 strokeOpacity: .35,
26619 fillColor: "#0000FF",
26623 options.map = this.gMapContext.map;
26624 options.radius = radius;
26625 options.center = center;
26626 this.gMapContext.circle = new google.maps.Circle(options);
26627 return this.gMapContext.circle;
26633 setPosition: function(location)
26635 this.gMapContext.location = location;
26636 this.gMapContext.marker.setPosition(location);
26637 this.gMapContext.map.panTo(location);
26638 this.drawCircle(location, this.gMapContext.radius, {});
26642 if (this.gMapContext.settings.enableReverseGeocode) {
26643 this.gMapContext.geodecoder.geocode({
26644 latLng: this.gMapContext.location
26645 }, function(results, status) {
26647 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26648 _this.gMapContext.locationName = results[0].formatted_address;
26649 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26651 _this.fireEvent('positionchanged', this, location);
26658 this.fireEvent('positionchanged', this, location);
26663 google.maps.event.trigger(this.gMapContext.map, "resize");
26665 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26667 this.fireEvent('resize', this);
26670 setPositionByLatLng: function(latitude, longitude)
26672 this.setPosition(new google.maps.LatLng(latitude, longitude));
26675 getCurrentPosition: function()
26678 latitude: this.gMapContext.location.lat(),
26679 longitude: this.gMapContext.location.lng()
26683 getAddressName: function()
26685 return this.gMapContext.locationName;
26688 getAddressComponents: function()
26690 return this.gMapContext.addressComponents;
26693 address_component_from_google_geocode: function(address_components)
26697 for (var i = 0; i < address_components.length; i++) {
26698 var component = address_components[i];
26699 if (component.types.indexOf("postal_code") >= 0) {
26700 result.postalCode = component.short_name;
26701 } else if (component.types.indexOf("street_number") >= 0) {
26702 result.streetNumber = component.short_name;
26703 } else if (component.types.indexOf("route") >= 0) {
26704 result.streetName = component.short_name;
26705 } else if (component.types.indexOf("neighborhood") >= 0) {
26706 result.city = component.short_name;
26707 } else if (component.types.indexOf("locality") >= 0) {
26708 result.city = component.short_name;
26709 } else if (component.types.indexOf("sublocality") >= 0) {
26710 result.district = component.short_name;
26711 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26712 result.stateOrProvince = component.short_name;
26713 } else if (component.types.indexOf("country") >= 0) {
26714 result.country = component.short_name;
26718 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26719 result.addressLine2 = "";
26723 setZoomLevel: function(zoom)
26725 this.gMapContext.map.setZoom(zoom);
26738 this.fireEvent('show', this);
26749 this.fireEvent('hide', this);
26754 Roo.apply(Roo.bootstrap.LocationPicker, {
26756 OverlayView : function(map, options)
26758 options = options || {};
26772 * @class Roo.bootstrap.Alert
26773 * @extends Roo.bootstrap.Component
26774 * Bootstrap Alert class
26775 * @cfg {String} title The title of alert
26776 * @cfg {String} html The content of alert
26777 * @cfg {String} weight ( success | info | warning | danger )
26778 * @cfg {String} faicon font-awesomeicon
26781 * Create a new alert
26782 * @param {Object} config The config object
26786 Roo.bootstrap.Alert = function(config){
26787 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26791 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26798 getAutoCreate : function()
26807 cls : 'roo-alert-icon'
26812 cls : 'roo-alert-title',
26817 cls : 'roo-alert-text',
26824 cfg.cn[0].cls += ' fa ' + this.faicon;
26828 cfg.cls += ' alert-' + this.weight;
26834 initEvents: function()
26836 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26839 setTitle : function(str)
26841 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26844 setText : function(str)
26846 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26849 setWeight : function(weight)
26852 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26855 this.weight = weight;
26857 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26860 setIcon : function(icon)
26863 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26866 this.faicon = icon;
26868 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26889 * @class Roo.bootstrap.UploadCropbox
26890 * @extends Roo.bootstrap.Component
26891 * Bootstrap UploadCropbox class
26892 * @cfg {String} emptyText show when image has been loaded
26893 * @cfg {String} rotateNotify show when image too small to rotate
26894 * @cfg {Number} errorTimeout default 3000
26895 * @cfg {Number} minWidth default 300
26896 * @cfg {Number} minHeight default 300
26897 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26898 * @cfg {Boolean} isDocument (true|false) default false
26899 * @cfg {String} url action url
26900 * @cfg {String} paramName default 'imageUpload'
26901 * @cfg {String} method default POST
26902 * @cfg {Boolean} loadMask (true|false) default true
26903 * @cfg {Boolean} loadingText default 'Loading...'
26906 * Create a new UploadCropbox
26907 * @param {Object} config The config object
26910 Roo.bootstrap.UploadCropbox = function(config){
26911 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26915 * @event beforeselectfile
26916 * Fire before select file
26917 * @param {Roo.bootstrap.UploadCropbox} this
26919 "beforeselectfile" : true,
26922 * Fire after initEvent
26923 * @param {Roo.bootstrap.UploadCropbox} this
26928 * Fire after initEvent
26929 * @param {Roo.bootstrap.UploadCropbox} this
26930 * @param {String} data
26935 * Fire when preparing the file data
26936 * @param {Roo.bootstrap.UploadCropbox} this
26937 * @param {Object} file
26942 * Fire when get exception
26943 * @param {Roo.bootstrap.UploadCropbox} this
26944 * @param {XMLHttpRequest} xhr
26946 "exception" : true,
26948 * @event beforeloadcanvas
26949 * Fire before load the canvas
26950 * @param {Roo.bootstrap.UploadCropbox} this
26951 * @param {String} src
26953 "beforeloadcanvas" : true,
26956 * Fire when trash image
26957 * @param {Roo.bootstrap.UploadCropbox} this
26962 * Fire when download the image
26963 * @param {Roo.bootstrap.UploadCropbox} this
26967 * @event footerbuttonclick
26968 * Fire when footerbuttonclick
26969 * @param {Roo.bootstrap.UploadCropbox} this
26970 * @param {String} type
26972 "footerbuttonclick" : true,
26976 * @param {Roo.bootstrap.UploadCropbox} this
26981 * Fire when rotate the image
26982 * @param {Roo.bootstrap.UploadCropbox} this
26983 * @param {String} pos
26988 * Fire when inspect the file
26989 * @param {Roo.bootstrap.UploadCropbox} this
26990 * @param {Object} file
26995 * Fire when xhr upload the file
26996 * @param {Roo.bootstrap.UploadCropbox} this
26997 * @param {Object} data
27002 * Fire when arrange the file data
27003 * @param {Roo.bootstrap.UploadCropbox} this
27004 * @param {Object} formData
27009 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27012 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27014 emptyText : 'Click to upload image',
27015 rotateNotify : 'Image is too small to rotate',
27016 errorTimeout : 3000,
27030 cropType : 'image/jpeg',
27032 canvasLoaded : false,
27033 isDocument : false,
27035 paramName : 'imageUpload',
27037 loadingText : 'Loading...',
27040 getAutoCreate : function()
27044 cls : 'roo-upload-cropbox',
27048 cls : 'roo-upload-cropbox-selector',
27053 cls : 'roo-upload-cropbox-body',
27054 style : 'cursor:pointer',
27058 cls : 'roo-upload-cropbox-preview'
27062 cls : 'roo-upload-cropbox-thumb'
27066 cls : 'roo-upload-cropbox-empty-notify',
27067 html : this.emptyText
27071 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27072 html : this.rotateNotify
27078 cls : 'roo-upload-cropbox-footer',
27081 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27091 onRender : function(ct, position)
27093 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27095 if (this.buttons.length) {
27097 Roo.each(this.buttons, function(bb) {
27099 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27101 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27107 this.maskEl = this.el;
27111 initEvents : function()
27113 this.urlAPI = (window.createObjectURL && window) ||
27114 (window.URL && URL.revokeObjectURL && URL) ||
27115 (window.webkitURL && webkitURL);
27117 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27118 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27120 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27121 this.selectorEl.hide();
27123 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27124 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27126 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27127 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27128 this.thumbEl.hide();
27130 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27131 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27133 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27134 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27135 this.errorEl.hide();
27137 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27138 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27139 this.footerEl.hide();
27141 this.setThumbBoxSize();
27147 this.fireEvent('initial', this);
27154 window.addEventListener("resize", function() { _this.resize(); } );
27156 this.bodyEl.on('click', this.beforeSelectFile, this);
27159 this.bodyEl.on('touchstart', this.onTouchStart, this);
27160 this.bodyEl.on('touchmove', this.onTouchMove, this);
27161 this.bodyEl.on('touchend', this.onTouchEnd, this);
27165 this.bodyEl.on('mousedown', this.onMouseDown, this);
27166 this.bodyEl.on('mousemove', this.onMouseMove, this);
27167 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27168 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27169 Roo.get(document).on('mouseup', this.onMouseUp, this);
27172 this.selectorEl.on('change', this.onFileSelected, this);
27178 this.baseScale = 1;
27180 this.baseRotate = 1;
27181 this.dragable = false;
27182 this.pinching = false;
27185 this.cropData = false;
27186 this.notifyEl.dom.innerHTML = this.emptyText;
27188 this.selectorEl.dom.value = '';
27192 resize : function()
27194 if(this.fireEvent('resize', this) != false){
27195 this.setThumbBoxPosition();
27196 this.setCanvasPosition();
27200 onFooterButtonClick : function(e, el, o, type)
27203 case 'rotate-left' :
27204 this.onRotateLeft(e);
27206 case 'rotate-right' :
27207 this.onRotateRight(e);
27210 this.beforeSelectFile(e);
27225 this.fireEvent('footerbuttonclick', this, type);
27228 beforeSelectFile : function(e)
27230 e.preventDefault();
27232 if(this.fireEvent('beforeselectfile', this) != false){
27233 this.selectorEl.dom.click();
27237 onFileSelected : function(e)
27239 e.preventDefault();
27241 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27245 var file = this.selectorEl.dom.files[0];
27247 if(this.fireEvent('inspect', this, file) != false){
27248 this.prepare(file);
27253 trash : function(e)
27255 this.fireEvent('trash', this);
27258 download : function(e)
27260 this.fireEvent('download', this);
27263 loadCanvas : function(src)
27265 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27269 this.imageEl = document.createElement('img');
27273 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27275 this.imageEl.src = src;
27279 onLoadCanvas : function()
27281 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27282 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27284 this.bodyEl.un('click', this.beforeSelectFile, this);
27286 this.notifyEl.hide();
27287 this.thumbEl.show();
27288 this.footerEl.show();
27290 this.baseRotateLevel();
27292 if(this.isDocument){
27293 this.setThumbBoxSize();
27296 this.setThumbBoxPosition();
27298 this.baseScaleLevel();
27304 this.canvasLoaded = true;
27307 this.maskEl.unmask();
27312 setCanvasPosition : function()
27314 if(!this.canvasEl){
27318 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27319 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27321 this.previewEl.setLeft(pw);
27322 this.previewEl.setTop(ph);
27326 onMouseDown : function(e)
27330 this.dragable = true;
27331 this.pinching = false;
27333 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27334 this.dragable = false;
27338 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27339 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27343 onMouseMove : function(e)
27347 if(!this.canvasLoaded){
27351 if (!this.dragable){
27355 var minX = Math.ceil(this.thumbEl.getLeft(true));
27356 var minY = Math.ceil(this.thumbEl.getTop(true));
27358 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27359 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27361 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27362 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27364 x = x - this.mouseX;
27365 y = y - this.mouseY;
27367 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27368 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27370 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27371 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27373 this.previewEl.setLeft(bgX);
27374 this.previewEl.setTop(bgY);
27376 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27377 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27380 onMouseUp : function(e)
27384 this.dragable = false;
27387 onMouseWheel : function(e)
27391 this.startScale = this.scale;
27393 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27395 if(!this.zoomable()){
27396 this.scale = this.startScale;
27405 zoomable : function()
27407 var minScale = this.thumbEl.getWidth() / this.minWidth;
27409 if(this.minWidth < this.minHeight){
27410 minScale = this.thumbEl.getHeight() / this.minHeight;
27413 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27414 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27418 (this.rotate == 0 || this.rotate == 180) &&
27420 width > this.imageEl.OriginWidth ||
27421 height > this.imageEl.OriginHeight ||
27422 (width < this.minWidth && height < this.minHeight)
27430 (this.rotate == 90 || this.rotate == 270) &&
27432 width > this.imageEl.OriginWidth ||
27433 height > this.imageEl.OriginHeight ||
27434 (width < this.minHeight && height < this.minWidth)
27441 !this.isDocument &&
27442 (this.rotate == 0 || this.rotate == 180) &&
27444 width < this.minWidth ||
27445 width > this.imageEl.OriginWidth ||
27446 height < this.minHeight ||
27447 height > this.imageEl.OriginHeight
27454 !this.isDocument &&
27455 (this.rotate == 90 || this.rotate == 270) &&
27457 width < this.minHeight ||
27458 width > this.imageEl.OriginWidth ||
27459 height < this.minWidth ||
27460 height > this.imageEl.OriginHeight
27470 onRotateLeft : function(e)
27472 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27474 var minScale = this.thumbEl.getWidth() / this.minWidth;
27476 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27477 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27479 this.startScale = this.scale;
27481 while (this.getScaleLevel() < minScale){
27483 this.scale = this.scale + 1;
27485 if(!this.zoomable()){
27490 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27491 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27496 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27503 this.scale = this.startScale;
27505 this.onRotateFail();
27510 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27512 if(this.isDocument){
27513 this.setThumbBoxSize();
27514 this.setThumbBoxPosition();
27515 this.setCanvasPosition();
27520 this.fireEvent('rotate', this, 'left');
27524 onRotateRight : function(e)
27526 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27528 var minScale = this.thumbEl.getWidth() / this.minWidth;
27530 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27531 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27533 this.startScale = this.scale;
27535 while (this.getScaleLevel() < minScale){
27537 this.scale = this.scale + 1;
27539 if(!this.zoomable()){
27544 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27545 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27550 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27557 this.scale = this.startScale;
27559 this.onRotateFail();
27564 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27566 if(this.isDocument){
27567 this.setThumbBoxSize();
27568 this.setThumbBoxPosition();
27569 this.setCanvasPosition();
27574 this.fireEvent('rotate', this, 'right');
27577 onRotateFail : function()
27579 this.errorEl.show(true);
27583 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27588 this.previewEl.dom.innerHTML = '';
27590 var canvasEl = document.createElement("canvas");
27592 var contextEl = canvasEl.getContext("2d");
27594 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27595 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27596 var center = this.imageEl.OriginWidth / 2;
27598 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27599 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27600 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27601 center = this.imageEl.OriginHeight / 2;
27604 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27606 contextEl.translate(center, center);
27607 contextEl.rotate(this.rotate * Math.PI / 180);
27609 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27611 this.canvasEl = document.createElement("canvas");
27613 this.contextEl = this.canvasEl.getContext("2d");
27615 switch (this.rotate) {
27618 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27619 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27621 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27626 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27627 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27629 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27630 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);
27634 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27639 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27640 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27642 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27643 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);
27647 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);
27652 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27653 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27655 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27656 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27660 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);
27667 this.previewEl.appendChild(this.canvasEl);
27669 this.setCanvasPosition();
27674 if(!this.canvasLoaded){
27678 var imageCanvas = document.createElement("canvas");
27680 var imageContext = imageCanvas.getContext("2d");
27682 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27683 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27685 var center = imageCanvas.width / 2;
27687 imageContext.translate(center, center);
27689 imageContext.rotate(this.rotate * Math.PI / 180);
27691 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27693 var canvas = document.createElement("canvas");
27695 var context = canvas.getContext("2d");
27697 canvas.width = this.minWidth;
27698 canvas.height = this.minHeight;
27700 switch (this.rotate) {
27703 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27704 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27706 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27707 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27709 var targetWidth = this.minWidth - 2 * x;
27710 var targetHeight = this.minHeight - 2 * y;
27714 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27715 scale = targetWidth / width;
27718 if(x > 0 && y == 0){
27719 scale = targetHeight / height;
27722 if(x > 0 && y > 0){
27723 scale = targetWidth / width;
27725 if(width < height){
27726 scale = targetHeight / height;
27730 context.scale(scale, scale);
27732 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27733 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27735 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27736 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27738 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27743 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27744 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27746 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27747 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27749 var targetWidth = this.minWidth - 2 * x;
27750 var targetHeight = this.minHeight - 2 * y;
27754 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27755 scale = targetWidth / width;
27758 if(x > 0 && y == 0){
27759 scale = targetHeight / height;
27762 if(x > 0 && y > 0){
27763 scale = targetWidth / width;
27765 if(width < height){
27766 scale = targetHeight / height;
27770 context.scale(scale, scale);
27772 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27773 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27775 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27776 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27778 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27780 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27785 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27786 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27788 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27789 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27791 var targetWidth = this.minWidth - 2 * x;
27792 var targetHeight = this.minHeight - 2 * y;
27796 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27797 scale = targetWidth / width;
27800 if(x > 0 && y == 0){
27801 scale = targetHeight / height;
27804 if(x > 0 && y > 0){
27805 scale = targetWidth / width;
27807 if(width < height){
27808 scale = targetHeight / height;
27812 context.scale(scale, scale);
27814 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27815 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27817 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27818 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27820 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27821 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27823 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27828 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27829 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27831 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27832 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27834 var targetWidth = this.minWidth - 2 * x;
27835 var targetHeight = this.minHeight - 2 * y;
27839 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27840 scale = targetWidth / width;
27843 if(x > 0 && y == 0){
27844 scale = targetHeight / height;
27847 if(x > 0 && y > 0){
27848 scale = targetWidth / width;
27850 if(width < height){
27851 scale = targetHeight / height;
27855 context.scale(scale, scale);
27857 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27858 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27860 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27861 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27863 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27865 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27872 this.cropData = canvas.toDataURL(this.cropType);
27874 if(this.fireEvent('crop', this, this.cropData) !== false){
27875 this.process(this.file, this.cropData);
27882 setThumbBoxSize : function()
27886 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27887 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27888 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27890 this.minWidth = width;
27891 this.minHeight = height;
27893 if(this.rotate == 90 || this.rotate == 270){
27894 this.minWidth = height;
27895 this.minHeight = width;
27900 width = Math.ceil(this.minWidth * height / this.minHeight);
27902 if(this.minWidth > this.minHeight){
27904 height = Math.ceil(this.minHeight * width / this.minWidth);
27907 this.thumbEl.setStyle({
27908 width : width + 'px',
27909 height : height + 'px'
27916 setThumbBoxPosition : function()
27918 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27919 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27921 this.thumbEl.setLeft(x);
27922 this.thumbEl.setTop(y);
27926 baseRotateLevel : function()
27928 this.baseRotate = 1;
27931 typeof(this.exif) != 'undefined' &&
27932 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27933 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27935 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27938 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27942 baseScaleLevel : function()
27946 if(this.isDocument){
27948 if(this.baseRotate == 6 || this.baseRotate == 8){
27950 height = this.thumbEl.getHeight();
27951 this.baseScale = height / this.imageEl.OriginWidth;
27953 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27954 width = this.thumbEl.getWidth();
27955 this.baseScale = width / this.imageEl.OriginHeight;
27961 height = this.thumbEl.getHeight();
27962 this.baseScale = height / this.imageEl.OriginHeight;
27964 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27965 width = this.thumbEl.getWidth();
27966 this.baseScale = width / this.imageEl.OriginWidth;
27972 if(this.baseRotate == 6 || this.baseRotate == 8){
27974 width = this.thumbEl.getHeight();
27975 this.baseScale = width / this.imageEl.OriginHeight;
27977 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27978 height = this.thumbEl.getWidth();
27979 this.baseScale = height / this.imageEl.OriginHeight;
27982 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27983 height = this.thumbEl.getWidth();
27984 this.baseScale = height / this.imageEl.OriginHeight;
27986 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27987 width = this.thumbEl.getHeight();
27988 this.baseScale = width / this.imageEl.OriginWidth;
27995 width = this.thumbEl.getWidth();
27996 this.baseScale = width / this.imageEl.OriginWidth;
27998 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27999 height = this.thumbEl.getHeight();
28000 this.baseScale = height / this.imageEl.OriginHeight;
28003 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28005 height = this.thumbEl.getHeight();
28006 this.baseScale = height / this.imageEl.OriginHeight;
28008 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28009 width = this.thumbEl.getWidth();
28010 this.baseScale = width / this.imageEl.OriginWidth;
28018 getScaleLevel : function()
28020 return this.baseScale * Math.pow(1.1, this.scale);
28023 onTouchStart : function(e)
28025 if(!this.canvasLoaded){
28026 this.beforeSelectFile(e);
28030 var touches = e.browserEvent.touches;
28036 if(touches.length == 1){
28037 this.onMouseDown(e);
28041 if(touches.length != 2){
28047 for(var i = 0, finger; finger = touches[i]; i++){
28048 coords.push(finger.pageX, finger.pageY);
28051 var x = Math.pow(coords[0] - coords[2], 2);
28052 var y = Math.pow(coords[1] - coords[3], 2);
28054 this.startDistance = Math.sqrt(x + y);
28056 this.startScale = this.scale;
28058 this.pinching = true;
28059 this.dragable = false;
28063 onTouchMove : function(e)
28065 if(!this.pinching && !this.dragable){
28069 var touches = e.browserEvent.touches;
28076 this.onMouseMove(e);
28082 for(var i = 0, finger; finger = touches[i]; i++){
28083 coords.push(finger.pageX, finger.pageY);
28086 var x = Math.pow(coords[0] - coords[2], 2);
28087 var y = Math.pow(coords[1] - coords[3], 2);
28089 this.endDistance = Math.sqrt(x + y);
28091 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28093 if(!this.zoomable()){
28094 this.scale = this.startScale;
28102 onTouchEnd : function(e)
28104 this.pinching = false;
28105 this.dragable = false;
28109 process : function(file, crop)
28112 this.maskEl.mask(this.loadingText);
28115 this.xhr = new XMLHttpRequest();
28117 file.xhr = this.xhr;
28119 this.xhr.open(this.method, this.url, true);
28122 "Accept": "application/json",
28123 "Cache-Control": "no-cache",
28124 "X-Requested-With": "XMLHttpRequest"
28127 for (var headerName in headers) {
28128 var headerValue = headers[headerName];
28130 this.xhr.setRequestHeader(headerName, headerValue);
28136 this.xhr.onload = function()
28138 _this.xhrOnLoad(_this.xhr);
28141 this.xhr.onerror = function()
28143 _this.xhrOnError(_this.xhr);
28146 var formData = new FormData();
28148 formData.append('returnHTML', 'NO');
28151 formData.append('crop', crop);
28154 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28155 formData.append(this.paramName, file, file.name);
28158 if(typeof(file.filename) != 'undefined'){
28159 formData.append('filename', file.filename);
28162 if(typeof(file.mimetype) != 'undefined'){
28163 formData.append('mimetype', file.mimetype);
28166 if(this.fireEvent('arrange', this, formData) != false){
28167 this.xhr.send(formData);
28171 xhrOnLoad : function(xhr)
28174 this.maskEl.unmask();
28177 if (xhr.readyState !== 4) {
28178 this.fireEvent('exception', this, xhr);
28182 var response = Roo.decode(xhr.responseText);
28184 if(!response.success){
28185 this.fireEvent('exception', this, xhr);
28189 var response = Roo.decode(xhr.responseText);
28191 this.fireEvent('upload', this, response);
28195 xhrOnError : function()
28198 this.maskEl.unmask();
28201 Roo.log('xhr on error');
28203 var response = Roo.decode(xhr.responseText);
28209 prepare : function(file)
28212 this.maskEl.mask(this.loadingText);
28218 if(typeof(file) === 'string'){
28219 this.loadCanvas(file);
28223 if(!file || !this.urlAPI){
28228 this.cropType = file.type;
28232 if(this.fireEvent('prepare', this, this.file) != false){
28234 var reader = new FileReader();
28236 reader.onload = function (e) {
28237 if (e.target.error) {
28238 Roo.log(e.target.error);
28242 var buffer = e.target.result,
28243 dataView = new DataView(buffer),
28245 maxOffset = dataView.byteLength - 4,
28249 if (dataView.getUint16(0) === 0xffd8) {
28250 while (offset < maxOffset) {
28251 markerBytes = dataView.getUint16(offset);
28253 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28254 markerLength = dataView.getUint16(offset + 2) + 2;
28255 if (offset + markerLength > dataView.byteLength) {
28256 Roo.log('Invalid meta data: Invalid segment size.');
28260 if(markerBytes == 0xffe1){
28261 _this.parseExifData(
28268 offset += markerLength;
28278 var url = _this.urlAPI.createObjectURL(_this.file);
28280 _this.loadCanvas(url);
28285 reader.readAsArrayBuffer(this.file);
28291 parseExifData : function(dataView, offset, length)
28293 var tiffOffset = offset + 10,
28297 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28298 // No Exif data, might be XMP data instead
28302 // Check for the ASCII code for "Exif" (0x45786966):
28303 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28304 // No Exif data, might be XMP data instead
28307 if (tiffOffset + 8 > dataView.byteLength) {
28308 Roo.log('Invalid Exif data: Invalid segment size.');
28311 // Check for the two null bytes:
28312 if (dataView.getUint16(offset + 8) !== 0x0000) {
28313 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28316 // Check the byte alignment:
28317 switch (dataView.getUint16(tiffOffset)) {
28319 littleEndian = true;
28322 littleEndian = false;
28325 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28328 // Check for the TIFF tag marker (0x002A):
28329 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28330 Roo.log('Invalid Exif data: Missing TIFF marker.');
28333 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28334 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28336 this.parseExifTags(
28339 tiffOffset + dirOffset,
28344 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28349 if (dirOffset + 6 > dataView.byteLength) {
28350 Roo.log('Invalid Exif data: Invalid directory offset.');
28353 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28354 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28355 if (dirEndOffset + 4 > dataView.byteLength) {
28356 Roo.log('Invalid Exif data: Invalid directory size.');
28359 for (i = 0; i < tagsNumber; i += 1) {
28363 dirOffset + 2 + 12 * i, // tag offset
28367 // Return the offset to the next directory:
28368 return dataView.getUint32(dirEndOffset, littleEndian);
28371 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28373 var tag = dataView.getUint16(offset, littleEndian);
28375 this.exif[tag] = this.getExifValue(
28379 dataView.getUint16(offset + 2, littleEndian), // tag type
28380 dataView.getUint32(offset + 4, littleEndian), // tag length
28385 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28387 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28396 Roo.log('Invalid Exif data: Invalid tag type.');
28400 tagSize = tagType.size * length;
28401 // Determine if the value is contained in the dataOffset bytes,
28402 // or if the value at the dataOffset is a pointer to the actual data:
28403 dataOffset = tagSize > 4 ?
28404 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28405 if (dataOffset + tagSize > dataView.byteLength) {
28406 Roo.log('Invalid Exif data: Invalid data offset.');
28409 if (length === 1) {
28410 return tagType.getValue(dataView, dataOffset, littleEndian);
28413 for (i = 0; i < length; i += 1) {
28414 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28417 if (tagType.ascii) {
28419 // Concatenate the chars:
28420 for (i = 0; i < values.length; i += 1) {
28422 // Ignore the terminating NULL byte(s):
28423 if (c === '\u0000') {
28435 Roo.apply(Roo.bootstrap.UploadCropbox, {
28437 'Orientation': 0x0112
28441 1: 0, //'top-left',
28443 3: 180, //'bottom-right',
28444 // 4: 'bottom-left',
28446 6: 90, //'right-top',
28447 // 7: 'right-bottom',
28448 8: 270 //'left-bottom'
28452 // byte, 8-bit unsigned int:
28454 getValue: function (dataView, dataOffset) {
28455 return dataView.getUint8(dataOffset);
28459 // ascii, 8-bit byte:
28461 getValue: function (dataView, dataOffset) {
28462 return String.fromCharCode(dataView.getUint8(dataOffset));
28467 // short, 16 bit int:
28469 getValue: function (dataView, dataOffset, littleEndian) {
28470 return dataView.getUint16(dataOffset, littleEndian);
28474 // long, 32 bit int:
28476 getValue: function (dataView, dataOffset, littleEndian) {
28477 return dataView.getUint32(dataOffset, littleEndian);
28481 // rational = two long values, first is numerator, second is denominator:
28483 getValue: function (dataView, dataOffset, littleEndian) {
28484 return dataView.getUint32(dataOffset, littleEndian) /
28485 dataView.getUint32(dataOffset + 4, littleEndian);
28489 // slong, 32 bit signed int:
28491 getValue: function (dataView, dataOffset, littleEndian) {
28492 return dataView.getInt32(dataOffset, littleEndian);
28496 // srational, two slongs, first is numerator, second is denominator:
28498 getValue: function (dataView, dataOffset, littleEndian) {
28499 return dataView.getInt32(dataOffset, littleEndian) /
28500 dataView.getInt32(dataOffset + 4, littleEndian);
28510 cls : 'btn-group roo-upload-cropbox-rotate-left',
28511 action : 'rotate-left',
28515 cls : 'btn btn-default',
28516 html : '<i class="fa fa-undo"></i>'
28522 cls : 'btn-group roo-upload-cropbox-picture',
28523 action : 'picture',
28527 cls : 'btn btn-default',
28528 html : '<i class="fa fa-picture-o"></i>'
28534 cls : 'btn-group roo-upload-cropbox-rotate-right',
28535 action : 'rotate-right',
28539 cls : 'btn btn-default',
28540 html : '<i class="fa fa-repeat"></i>'
28548 cls : 'btn-group roo-upload-cropbox-rotate-left',
28549 action : 'rotate-left',
28553 cls : 'btn btn-default',
28554 html : '<i class="fa fa-undo"></i>'
28560 cls : 'btn-group roo-upload-cropbox-download',
28561 action : 'download',
28565 cls : 'btn btn-default',
28566 html : '<i class="fa fa-download"></i>'
28572 cls : 'btn-group roo-upload-cropbox-crop',
28577 cls : 'btn btn-default',
28578 html : '<i class="fa fa-crop"></i>'
28584 cls : 'btn-group roo-upload-cropbox-trash',
28589 cls : 'btn btn-default',
28590 html : '<i class="fa fa-trash"></i>'
28596 cls : 'btn-group roo-upload-cropbox-rotate-right',
28597 action : 'rotate-right',
28601 cls : 'btn btn-default',
28602 html : '<i class="fa fa-repeat"></i>'
28610 cls : 'btn-group roo-upload-cropbox-rotate-left',
28611 action : 'rotate-left',
28615 cls : 'btn btn-default',
28616 html : '<i class="fa fa-undo"></i>'
28622 cls : 'btn-group roo-upload-cropbox-rotate-right',
28623 action : 'rotate-right',
28627 cls : 'btn btn-default',
28628 html : '<i class="fa fa-repeat"></i>'
28641 * @class Roo.bootstrap.DocumentManager
28642 * @extends Roo.bootstrap.Component
28643 * Bootstrap DocumentManager class
28644 * @cfg {String} paramName default 'imageUpload'
28645 * @cfg {String} toolTipName default 'filename'
28646 * @cfg {String} method default POST
28647 * @cfg {String} url action url
28648 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28649 * @cfg {Boolean} multiple multiple upload default true
28650 * @cfg {Number} thumbSize default 300
28651 * @cfg {String} fieldLabel
28652 * @cfg {Number} labelWidth default 4
28653 * @cfg {String} labelAlign (left|top) default left
28654 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28655 * @cfg {Number} labellg set the width of label (1-12)
28656 * @cfg {Number} labelmd set the width of label (1-12)
28657 * @cfg {Number} labelsm set the width of label (1-12)
28658 * @cfg {Number} labelxs set the width of label (1-12)
28661 * Create a new DocumentManager
28662 * @param {Object} config The config object
28665 Roo.bootstrap.DocumentManager = function(config){
28666 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28669 this.delegates = [];
28674 * Fire when initial the DocumentManager
28675 * @param {Roo.bootstrap.DocumentManager} this
28680 * inspect selected file
28681 * @param {Roo.bootstrap.DocumentManager} this
28682 * @param {File} file
28687 * Fire when xhr load exception
28688 * @param {Roo.bootstrap.DocumentManager} this
28689 * @param {XMLHttpRequest} xhr
28691 "exception" : true,
28693 * @event afterupload
28694 * Fire when xhr load exception
28695 * @param {Roo.bootstrap.DocumentManager} this
28696 * @param {XMLHttpRequest} xhr
28698 "afterupload" : true,
28701 * prepare the form data
28702 * @param {Roo.bootstrap.DocumentManager} this
28703 * @param {Object} formData
28708 * Fire when remove the file
28709 * @param {Roo.bootstrap.DocumentManager} this
28710 * @param {Object} file
28715 * Fire after refresh the file
28716 * @param {Roo.bootstrap.DocumentManager} this
28721 * Fire after click the image
28722 * @param {Roo.bootstrap.DocumentManager} this
28723 * @param {Object} file
28728 * Fire when upload a image and editable set to true
28729 * @param {Roo.bootstrap.DocumentManager} this
28730 * @param {Object} file
28734 * @event beforeselectfile
28735 * Fire before select file
28736 * @param {Roo.bootstrap.DocumentManager} this
28738 "beforeselectfile" : true,
28741 * Fire before process file
28742 * @param {Roo.bootstrap.DocumentManager} this
28743 * @param {Object} file
28747 * @event previewrendered
28748 * Fire when preview rendered
28749 * @param {Roo.bootstrap.DocumentManager} this
28750 * @param {Object} file
28752 "previewrendered" : true,
28755 "previewResize" : true
28760 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28769 paramName : 'imageUpload',
28770 toolTipName : 'filename',
28773 labelAlign : 'left',
28783 getAutoCreate : function()
28785 var managerWidget = {
28787 cls : 'roo-document-manager',
28791 cls : 'roo-document-manager-selector',
28796 cls : 'roo-document-manager-uploader',
28800 cls : 'roo-document-manager-upload-btn',
28801 html : '<i class="fa fa-plus"></i>'
28812 cls : 'column col-md-12',
28817 if(this.fieldLabel.length){
28822 cls : 'column col-md-12',
28823 html : this.fieldLabel
28827 cls : 'column col-md-12',
28832 if(this.labelAlign == 'left'){
28837 html : this.fieldLabel
28846 if(this.labelWidth > 12){
28847 content[0].style = "width: " + this.labelWidth + 'px';
28850 if(this.labelWidth < 13 && this.labelmd == 0){
28851 this.labelmd = this.labelWidth;
28854 if(this.labellg > 0){
28855 content[0].cls += ' col-lg-' + this.labellg;
28856 content[1].cls += ' col-lg-' + (12 - this.labellg);
28859 if(this.labelmd > 0){
28860 content[0].cls += ' col-md-' + this.labelmd;
28861 content[1].cls += ' col-md-' + (12 - this.labelmd);
28864 if(this.labelsm > 0){
28865 content[0].cls += ' col-sm-' + this.labelsm;
28866 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28869 if(this.labelxs > 0){
28870 content[0].cls += ' col-xs-' + this.labelxs;
28871 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28879 cls : 'row clearfix',
28887 initEvents : function()
28889 this.managerEl = this.el.select('.roo-document-manager', true).first();
28890 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28892 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28893 this.selectorEl.hide();
28896 this.selectorEl.attr('multiple', 'multiple');
28899 this.selectorEl.on('change', this.onFileSelected, this);
28901 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28902 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28904 this.uploader.on('click', this.onUploaderClick, this);
28906 this.renderProgressDialog();
28910 window.addEventListener("resize", function() { _this.refresh(); } );
28912 this.fireEvent('initial', this);
28915 renderProgressDialog : function()
28919 this.progressDialog = new Roo.bootstrap.Modal({
28920 cls : 'roo-document-manager-progress-dialog',
28921 allow_close : false,
28931 btnclick : function() {
28932 _this.uploadCancel();
28938 this.progressDialog.render(Roo.get(document.body));
28940 this.progress = new Roo.bootstrap.Progress({
28941 cls : 'roo-document-manager-progress',
28946 this.progress.render(this.progressDialog.getChildContainer());
28948 this.progressBar = new Roo.bootstrap.ProgressBar({
28949 cls : 'roo-document-manager-progress-bar',
28952 aria_valuemax : 12,
28956 this.progressBar.render(this.progress.getChildContainer());
28959 onUploaderClick : function(e)
28961 e.preventDefault();
28963 if(this.fireEvent('beforeselectfile', this) != false){
28964 this.selectorEl.dom.click();
28969 onFileSelected : function(e)
28971 e.preventDefault();
28973 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28977 Roo.each(this.selectorEl.dom.files, function(file){
28978 if(this.fireEvent('inspect', this, file) != false){
28979 this.files.push(file);
28989 this.selectorEl.dom.value = '';
28991 if(!this.files || !this.files.length){
28995 if(this.boxes > 0 && this.files.length > this.boxes){
28996 this.files = this.files.slice(0, this.boxes);
28999 this.uploader.show();
29001 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29002 this.uploader.hide();
29011 Roo.each(this.files, function(file){
29013 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29014 var f = this.renderPreview(file);
29019 if(file.type.indexOf('image') != -1){
29020 this.delegates.push(
29022 _this.process(file);
29023 }).createDelegate(this)
29031 _this.process(file);
29032 }).createDelegate(this)
29037 this.files = files;
29039 this.delegates = this.delegates.concat(docs);
29041 if(!this.delegates.length){
29046 this.progressBar.aria_valuemax = this.delegates.length;
29053 arrange : function()
29055 if(!this.delegates.length){
29056 this.progressDialog.hide();
29061 var delegate = this.delegates.shift();
29063 this.progressDialog.show();
29065 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29067 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29072 refresh : function()
29074 this.uploader.show();
29076 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29077 this.uploader.hide();
29080 Roo.isTouch ? this.closable(false) : this.closable(true);
29082 this.fireEvent('refresh', this);
29085 onRemove : function(e, el, o)
29087 e.preventDefault();
29089 this.fireEvent('remove', this, o);
29093 remove : function(o)
29097 Roo.each(this.files, function(file){
29098 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29107 this.files = files;
29114 Roo.each(this.files, function(file){
29119 file.target.remove();
29128 onClick : function(e, el, o)
29130 e.preventDefault();
29132 this.fireEvent('click', this, o);
29136 closable : function(closable)
29138 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29140 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29152 xhrOnLoad : function(xhr)
29154 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29158 if (xhr.readyState !== 4) {
29160 this.fireEvent('exception', this, xhr);
29164 var response = Roo.decode(xhr.responseText);
29166 if(!response.success){
29168 this.fireEvent('exception', this, xhr);
29172 var file = this.renderPreview(response.data);
29174 this.files.push(file);
29178 this.fireEvent('afterupload', this, xhr);
29182 xhrOnError : function(xhr)
29184 Roo.log('xhr on error');
29186 var response = Roo.decode(xhr.responseText);
29193 process : function(file)
29195 if(this.fireEvent('process', this, file) !== false){
29196 if(this.editable && file.type.indexOf('image') != -1){
29197 this.fireEvent('edit', this, file);
29201 this.uploadStart(file, false);
29208 uploadStart : function(file, crop)
29210 this.xhr = new XMLHttpRequest();
29212 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29217 file.xhr = this.xhr;
29219 this.managerEl.createChild({
29221 cls : 'roo-document-manager-loading',
29225 tooltip : file.name,
29226 cls : 'roo-document-manager-thumb',
29227 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29233 this.xhr.open(this.method, this.url, true);
29236 "Accept": "application/json",
29237 "Cache-Control": "no-cache",
29238 "X-Requested-With": "XMLHttpRequest"
29241 for (var headerName in headers) {
29242 var headerValue = headers[headerName];
29244 this.xhr.setRequestHeader(headerName, headerValue);
29250 this.xhr.onload = function()
29252 _this.xhrOnLoad(_this.xhr);
29255 this.xhr.onerror = function()
29257 _this.xhrOnError(_this.xhr);
29260 var formData = new FormData();
29262 formData.append('returnHTML', 'NO');
29265 formData.append('crop', crop);
29268 formData.append(this.paramName, file, file.name);
29275 if(this.fireEvent('prepare', this, formData, options) != false){
29277 if(options.manually){
29281 this.xhr.send(formData);
29285 this.uploadCancel();
29288 uploadCancel : function()
29294 this.delegates = [];
29296 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29303 renderPreview : function(file)
29305 if(typeof(file.target) != 'undefined' && file.target){
29309 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29311 var previewEl = this.managerEl.createChild({
29313 cls : 'roo-document-manager-preview',
29317 tooltip : file[this.toolTipName],
29318 cls : 'roo-document-manager-thumb',
29319 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29324 html : '<i class="fa fa-times-circle"></i>'
29329 var close = previewEl.select('button.close', true).first();
29331 close.on('click', this.onRemove, this, file);
29333 file.target = previewEl;
29335 var image = previewEl.select('img', true).first();
29339 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29341 image.on('click', this.onClick, this, file);
29343 this.fireEvent('previewrendered', this, file);
29349 onPreviewLoad : function(file, image)
29351 if(typeof(file.target) == 'undefined' || !file.target){
29355 var width = image.dom.naturalWidth || image.dom.width;
29356 var height = image.dom.naturalHeight || image.dom.height;
29358 if(!this.previewResize) {
29362 if(width > height){
29363 file.target.addClass('wide');
29367 file.target.addClass('tall');
29372 uploadFromSource : function(file, crop)
29374 this.xhr = new XMLHttpRequest();
29376 this.managerEl.createChild({
29378 cls : 'roo-document-manager-loading',
29382 tooltip : file.name,
29383 cls : 'roo-document-manager-thumb',
29384 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29390 this.xhr.open(this.method, this.url, true);
29393 "Accept": "application/json",
29394 "Cache-Control": "no-cache",
29395 "X-Requested-With": "XMLHttpRequest"
29398 for (var headerName in headers) {
29399 var headerValue = headers[headerName];
29401 this.xhr.setRequestHeader(headerName, headerValue);
29407 this.xhr.onload = function()
29409 _this.xhrOnLoad(_this.xhr);
29412 this.xhr.onerror = function()
29414 _this.xhrOnError(_this.xhr);
29417 var formData = new FormData();
29419 formData.append('returnHTML', 'NO');
29421 formData.append('crop', crop);
29423 if(typeof(file.filename) != 'undefined'){
29424 formData.append('filename', file.filename);
29427 if(typeof(file.mimetype) != 'undefined'){
29428 formData.append('mimetype', file.mimetype);
29433 if(this.fireEvent('prepare', this, formData) != false){
29434 this.xhr.send(formData);
29444 * @class Roo.bootstrap.DocumentViewer
29445 * @extends Roo.bootstrap.Component
29446 * Bootstrap DocumentViewer class
29447 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29448 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29451 * Create a new DocumentViewer
29452 * @param {Object} config The config object
29455 Roo.bootstrap.DocumentViewer = function(config){
29456 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29461 * Fire after initEvent
29462 * @param {Roo.bootstrap.DocumentViewer} this
29468 * @param {Roo.bootstrap.DocumentViewer} this
29473 * Fire after download button
29474 * @param {Roo.bootstrap.DocumentViewer} this
29479 * Fire after trash button
29480 * @param {Roo.bootstrap.DocumentViewer} this
29487 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29489 showDownload : true,
29493 getAutoCreate : function()
29497 cls : 'roo-document-viewer',
29501 cls : 'roo-document-viewer-body',
29505 cls : 'roo-document-viewer-thumb',
29509 cls : 'roo-document-viewer-image'
29517 cls : 'roo-document-viewer-footer',
29520 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29524 cls : 'btn-group roo-document-viewer-download',
29528 cls : 'btn btn-default',
29529 html : '<i class="fa fa-download"></i>'
29535 cls : 'btn-group roo-document-viewer-trash',
29539 cls : 'btn btn-default',
29540 html : '<i class="fa fa-trash"></i>'
29553 initEvents : function()
29555 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29556 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29558 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29559 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29561 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29562 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29564 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29565 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29567 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29568 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29570 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29571 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29573 this.bodyEl.on('click', this.onClick, this);
29574 this.downloadBtn.on('click', this.onDownload, this);
29575 this.trashBtn.on('click', this.onTrash, this);
29577 this.downloadBtn.hide();
29578 this.trashBtn.hide();
29580 if(this.showDownload){
29581 this.downloadBtn.show();
29584 if(this.showTrash){
29585 this.trashBtn.show();
29588 if(!this.showDownload && !this.showTrash) {
29589 this.footerEl.hide();
29594 initial : function()
29596 this.fireEvent('initial', this);
29600 onClick : function(e)
29602 e.preventDefault();
29604 this.fireEvent('click', this);
29607 onDownload : function(e)
29609 e.preventDefault();
29611 this.fireEvent('download', this);
29614 onTrash : function(e)
29616 e.preventDefault();
29618 this.fireEvent('trash', this);
29630 * @class Roo.bootstrap.NavProgressBar
29631 * @extends Roo.bootstrap.Component
29632 * Bootstrap NavProgressBar class
29635 * Create a new nav progress bar
29636 * @param {Object} config The config object
29639 Roo.bootstrap.NavProgressBar = function(config){
29640 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29642 this.bullets = this.bullets || [];
29644 // Roo.bootstrap.NavProgressBar.register(this);
29648 * Fires when the active item changes
29649 * @param {Roo.bootstrap.NavProgressBar} this
29650 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29651 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29658 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29663 getAutoCreate : function()
29665 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29669 cls : 'roo-navigation-bar-group',
29673 cls : 'roo-navigation-top-bar'
29677 cls : 'roo-navigation-bullets-bar',
29681 cls : 'roo-navigation-bar'
29688 cls : 'roo-navigation-bottom-bar'
29698 initEvents: function()
29703 onRender : function(ct, position)
29705 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29707 if(this.bullets.length){
29708 Roo.each(this.bullets, function(b){
29717 addItem : function(cfg)
29719 var item = new Roo.bootstrap.NavProgressItem(cfg);
29721 item.parentId = this.id;
29722 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29725 var top = new Roo.bootstrap.Element({
29727 cls : 'roo-navigation-bar-text'
29730 var bottom = new Roo.bootstrap.Element({
29732 cls : 'roo-navigation-bar-text'
29735 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29736 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29738 var topText = new Roo.bootstrap.Element({
29740 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29743 var bottomText = new Roo.bootstrap.Element({
29745 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29748 topText.onRender(top.el, null);
29749 bottomText.onRender(bottom.el, null);
29752 item.bottomEl = bottom;
29755 this.barItems.push(item);
29760 getActive : function()
29762 var active = false;
29764 Roo.each(this.barItems, function(v){
29766 if (!v.isActive()) {
29778 setActiveItem : function(item)
29782 Roo.each(this.barItems, function(v){
29783 if (v.rid == item.rid) {
29787 if (v.isActive()) {
29788 v.setActive(false);
29793 item.setActive(true);
29795 this.fireEvent('changed', this, item, prev);
29798 getBarItem: function(rid)
29802 Roo.each(this.barItems, function(e) {
29803 if (e.rid != rid) {
29814 indexOfItem : function(item)
29818 Roo.each(this.barItems, function(v, i){
29820 if (v.rid != item.rid) {
29831 setActiveNext : function()
29833 var i = this.indexOfItem(this.getActive());
29835 if (i > this.barItems.length) {
29839 this.setActiveItem(this.barItems[i+1]);
29842 setActivePrev : function()
29844 var i = this.indexOfItem(this.getActive());
29850 this.setActiveItem(this.barItems[i-1]);
29853 format : function()
29855 if(!this.barItems.length){
29859 var width = 100 / this.barItems.length;
29861 Roo.each(this.barItems, function(i){
29862 i.el.setStyle('width', width + '%');
29863 i.topEl.el.setStyle('width', width + '%');
29864 i.bottomEl.el.setStyle('width', width + '%');
29873 * Nav Progress Item
29878 * @class Roo.bootstrap.NavProgressItem
29879 * @extends Roo.bootstrap.Component
29880 * Bootstrap NavProgressItem class
29881 * @cfg {String} rid the reference id
29882 * @cfg {Boolean} active (true|false) Is item active default false
29883 * @cfg {Boolean} disabled (true|false) Is item active default false
29884 * @cfg {String} html
29885 * @cfg {String} position (top|bottom) text position default bottom
29886 * @cfg {String} icon show icon instead of number
29889 * Create a new NavProgressItem
29890 * @param {Object} config The config object
29892 Roo.bootstrap.NavProgressItem = function(config){
29893 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29898 * The raw click event for the entire grid.
29899 * @param {Roo.bootstrap.NavProgressItem} this
29900 * @param {Roo.EventObject} e
29907 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29913 position : 'bottom',
29916 getAutoCreate : function()
29918 var iconCls = 'roo-navigation-bar-item-icon';
29920 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29924 cls: 'roo-navigation-bar-item',
29934 cfg.cls += ' active';
29937 cfg.cls += ' disabled';
29943 disable : function()
29945 this.setDisabled(true);
29948 enable : function()
29950 this.setDisabled(false);
29953 initEvents: function()
29955 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29957 this.iconEl.on('click', this.onClick, this);
29960 onClick : function(e)
29962 e.preventDefault();
29968 if(this.fireEvent('click', this, e) === false){
29972 this.parent().setActiveItem(this);
29975 isActive: function ()
29977 return this.active;
29980 setActive : function(state)
29982 if(this.active == state){
29986 this.active = state;
29989 this.el.addClass('active');
29993 this.el.removeClass('active');
29998 setDisabled : function(state)
30000 if(this.disabled == state){
30004 this.disabled = state;
30007 this.el.addClass('disabled');
30011 this.el.removeClass('disabled');
30014 tooltipEl : function()
30016 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30029 * @class Roo.bootstrap.FieldLabel
30030 * @extends Roo.bootstrap.Component
30031 * Bootstrap FieldLabel class
30032 * @cfg {String} html contents of the element
30033 * @cfg {String} tag tag of the element default label
30034 * @cfg {String} cls class of the element
30035 * @cfg {String} target label target
30036 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30037 * @cfg {String} invalidClass default "text-warning"
30038 * @cfg {String} validClass default "text-success"
30039 * @cfg {String} iconTooltip default "This field is required"
30040 * @cfg {String} indicatorpos (left|right) default left
30043 * Create a new FieldLabel
30044 * @param {Object} config The config object
30047 Roo.bootstrap.FieldLabel = function(config){
30048 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30053 * Fires after the field has been marked as invalid.
30054 * @param {Roo.form.FieldLabel} this
30055 * @param {String} msg The validation message
30060 * Fires after the field has been validated with no errors.
30061 * @param {Roo.form.FieldLabel} this
30067 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30074 invalidClass : 'has-warning',
30075 validClass : 'has-success',
30076 iconTooltip : 'This field is required',
30077 indicatorpos : 'left',
30079 getAutoCreate : function(){
30082 if (!this.allowBlank) {
30088 cls : 'roo-bootstrap-field-label ' + this.cls,
30093 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30094 tooltip : this.iconTooltip
30103 if(this.indicatorpos == 'right'){
30106 cls : 'roo-bootstrap-field-label ' + this.cls,
30115 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30116 tooltip : this.iconTooltip
30125 initEvents: function()
30127 Roo.bootstrap.Element.superclass.initEvents.call(this);
30129 this.indicator = this.indicatorEl();
30131 if(this.indicator){
30132 this.indicator.removeClass('visible');
30133 this.indicator.addClass('invisible');
30136 Roo.bootstrap.FieldLabel.register(this);
30139 indicatorEl : function()
30141 var indicator = this.el.select('i.roo-required-indicator',true).first();
30152 * Mark this field as valid
30154 markValid : function()
30156 if(this.indicator){
30157 this.indicator.removeClass('visible');
30158 this.indicator.addClass('invisible');
30161 this.el.removeClass(this.invalidClass);
30163 this.el.addClass(this.validClass);
30165 this.fireEvent('valid', this);
30169 * Mark this field as invalid
30170 * @param {String} msg The validation message
30172 markInvalid : function(msg)
30174 if(this.indicator){
30175 this.indicator.removeClass('invisible');
30176 this.indicator.addClass('visible');
30179 this.el.removeClass(this.validClass);
30181 this.el.addClass(this.invalidClass);
30183 this.fireEvent('invalid', this, msg);
30189 Roo.apply(Roo.bootstrap.FieldLabel, {
30194 * register a FieldLabel Group
30195 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30197 register : function(label)
30199 if(this.groups.hasOwnProperty(label.target)){
30203 this.groups[label.target] = label;
30207 * fetch a FieldLabel Group based on the target
30208 * @param {string} target
30209 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30211 get: function(target) {
30212 if (typeof(this.groups[target]) == 'undefined') {
30216 return this.groups[target] ;
30225 * page DateSplitField.
30231 * @class Roo.bootstrap.DateSplitField
30232 * @extends Roo.bootstrap.Component
30233 * Bootstrap DateSplitField class
30234 * @cfg {string} fieldLabel - the label associated
30235 * @cfg {Number} labelWidth set the width of label (0-12)
30236 * @cfg {String} labelAlign (top|left)
30237 * @cfg {Boolean} dayAllowBlank (true|false) default false
30238 * @cfg {Boolean} monthAllowBlank (true|false) default false
30239 * @cfg {Boolean} yearAllowBlank (true|false) default false
30240 * @cfg {string} dayPlaceholder
30241 * @cfg {string} monthPlaceholder
30242 * @cfg {string} yearPlaceholder
30243 * @cfg {string} dayFormat default 'd'
30244 * @cfg {string} monthFormat default 'm'
30245 * @cfg {string} yearFormat default 'Y'
30246 * @cfg {Number} labellg set the width of label (1-12)
30247 * @cfg {Number} labelmd set the width of label (1-12)
30248 * @cfg {Number} labelsm set the width of label (1-12)
30249 * @cfg {Number} labelxs set the width of label (1-12)
30253 * Create a new DateSplitField
30254 * @param {Object} config The config object
30257 Roo.bootstrap.DateSplitField = function(config){
30258 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30264 * getting the data of years
30265 * @param {Roo.bootstrap.DateSplitField} this
30266 * @param {Object} years
30271 * getting the data of days
30272 * @param {Roo.bootstrap.DateSplitField} this
30273 * @param {Object} days
30278 * Fires after the field has been marked as invalid.
30279 * @param {Roo.form.Field} this
30280 * @param {String} msg The validation message
30285 * Fires after the field has been validated with no errors.
30286 * @param {Roo.form.Field} this
30292 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30295 labelAlign : 'top',
30297 dayAllowBlank : false,
30298 monthAllowBlank : false,
30299 yearAllowBlank : false,
30300 dayPlaceholder : '',
30301 monthPlaceholder : '',
30302 yearPlaceholder : '',
30306 isFormField : true,
30312 getAutoCreate : function()
30316 cls : 'row roo-date-split-field-group',
30321 cls : 'form-hidden-field roo-date-split-field-group-value',
30327 var labelCls = 'col-md-12';
30328 var contentCls = 'col-md-4';
30330 if(this.fieldLabel){
30334 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30338 html : this.fieldLabel
30343 if(this.labelAlign == 'left'){
30345 if(this.labelWidth > 12){
30346 label.style = "width: " + this.labelWidth + 'px';
30349 if(this.labelWidth < 13 && this.labelmd == 0){
30350 this.labelmd = this.labelWidth;
30353 if(this.labellg > 0){
30354 labelCls = ' col-lg-' + this.labellg;
30355 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30358 if(this.labelmd > 0){
30359 labelCls = ' col-md-' + this.labelmd;
30360 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30363 if(this.labelsm > 0){
30364 labelCls = ' col-sm-' + this.labelsm;
30365 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30368 if(this.labelxs > 0){
30369 labelCls = ' col-xs-' + this.labelxs;
30370 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30374 label.cls += ' ' + labelCls;
30376 cfg.cn.push(label);
30379 Roo.each(['day', 'month', 'year'], function(t){
30382 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30389 inputEl: function ()
30391 return this.el.select('.roo-date-split-field-group-value', true).first();
30394 onRender : function(ct, position)
30398 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30400 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30402 this.dayField = new Roo.bootstrap.ComboBox({
30403 allowBlank : this.dayAllowBlank,
30404 alwaysQuery : true,
30405 displayField : 'value',
30408 forceSelection : true,
30410 placeholder : this.dayPlaceholder,
30411 selectOnFocus : true,
30412 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30413 triggerAction : 'all',
30415 valueField : 'value',
30416 store : new Roo.data.SimpleStore({
30417 data : (function() {
30419 _this.fireEvent('days', _this, days);
30422 fields : [ 'value' ]
30425 select : function (_self, record, index)
30427 _this.setValue(_this.getValue());
30432 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30434 this.monthField = new Roo.bootstrap.MonthField({
30435 after : '<i class=\"fa fa-calendar\"></i>',
30436 allowBlank : this.monthAllowBlank,
30437 placeholder : this.monthPlaceholder,
30440 render : function (_self)
30442 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30443 e.preventDefault();
30447 select : function (_self, oldvalue, newvalue)
30449 _this.setValue(_this.getValue());
30454 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30456 this.yearField = new Roo.bootstrap.ComboBox({
30457 allowBlank : this.yearAllowBlank,
30458 alwaysQuery : true,
30459 displayField : 'value',
30462 forceSelection : true,
30464 placeholder : this.yearPlaceholder,
30465 selectOnFocus : true,
30466 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30467 triggerAction : 'all',
30469 valueField : 'value',
30470 store : new Roo.data.SimpleStore({
30471 data : (function() {
30473 _this.fireEvent('years', _this, years);
30476 fields : [ 'value' ]
30479 select : function (_self, record, index)
30481 _this.setValue(_this.getValue());
30486 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30489 setValue : function(v, format)
30491 this.inputEl.dom.value = v;
30493 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30495 var d = Date.parseDate(v, f);
30502 this.setDay(d.format(this.dayFormat));
30503 this.setMonth(d.format(this.monthFormat));
30504 this.setYear(d.format(this.yearFormat));
30511 setDay : function(v)
30513 this.dayField.setValue(v);
30514 this.inputEl.dom.value = this.getValue();
30519 setMonth : function(v)
30521 this.monthField.setValue(v, true);
30522 this.inputEl.dom.value = this.getValue();
30527 setYear : function(v)
30529 this.yearField.setValue(v);
30530 this.inputEl.dom.value = this.getValue();
30535 getDay : function()
30537 return this.dayField.getValue();
30540 getMonth : function()
30542 return this.monthField.getValue();
30545 getYear : function()
30547 return this.yearField.getValue();
30550 getValue : function()
30552 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30554 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30564 this.inputEl.dom.value = '';
30569 validate : function()
30571 var d = this.dayField.validate();
30572 var m = this.monthField.validate();
30573 var y = this.yearField.validate();
30578 (!this.dayAllowBlank && !d) ||
30579 (!this.monthAllowBlank && !m) ||
30580 (!this.yearAllowBlank && !y)
30585 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30594 this.markInvalid();
30599 markValid : function()
30602 var label = this.el.select('label', true).first();
30603 var icon = this.el.select('i.fa-star', true).first();
30609 this.fireEvent('valid', this);
30613 * Mark this field as invalid
30614 * @param {String} msg The validation message
30616 markInvalid : function(msg)
30619 var label = this.el.select('label', true).first();
30620 var icon = this.el.select('i.fa-star', true).first();
30622 if(label && !icon){
30623 this.el.select('.roo-date-split-field-label', true).createChild({
30625 cls : 'text-danger fa fa-lg fa-star',
30626 tooltip : 'This field is required',
30627 style : 'margin-right:5px;'
30631 this.fireEvent('invalid', this, msg);
30634 clearInvalid : function()
30636 var label = this.el.select('label', true).first();
30637 var icon = this.el.select('i.fa-star', true).first();
30643 this.fireEvent('valid', this);
30646 getName: function()
30656 * http://masonry.desandro.com
30658 * The idea is to render all the bricks based on vertical width...
30660 * The original code extends 'outlayer' - we might need to use that....
30666 * @class Roo.bootstrap.LayoutMasonry
30667 * @extends Roo.bootstrap.Component
30668 * Bootstrap Layout Masonry class
30671 * Create a new Element
30672 * @param {Object} config The config object
30675 Roo.bootstrap.LayoutMasonry = function(config){
30677 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30681 Roo.bootstrap.LayoutMasonry.register(this);
30687 * Fire after layout the items
30688 * @param {Roo.bootstrap.LayoutMasonry} this
30689 * @param {Roo.EventObject} e
30696 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30699 * @cfg {Boolean} isLayoutInstant = no animation?
30701 isLayoutInstant : false, // needed?
30704 * @cfg {Number} boxWidth width of the columns
30709 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30714 * @cfg {Number} padWidth padding below box..
30719 * @cfg {Number} gutter gutter width..
30724 * @cfg {Number} maxCols maximum number of columns
30730 * @cfg {Boolean} isAutoInitial defalut true
30732 isAutoInitial : true,
30737 * @cfg {Boolean} isHorizontal defalut false
30739 isHorizontal : false,
30741 currentSize : null,
30747 bricks: null, //CompositeElement
30751 _isLayoutInited : false,
30753 // isAlternative : false, // only use for vertical layout...
30756 * @cfg {Number} alternativePadWidth padding below box..
30758 alternativePadWidth : 50,
30760 selectedBrick : [],
30762 getAutoCreate : function(){
30764 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30768 cls: 'blog-masonary-wrapper ' + this.cls,
30770 cls : 'mas-boxes masonary'
30777 getChildContainer: function( )
30779 if (this.boxesEl) {
30780 return this.boxesEl;
30783 this.boxesEl = this.el.select('.mas-boxes').first();
30785 return this.boxesEl;
30789 initEvents : function()
30793 if(this.isAutoInitial){
30794 Roo.log('hook children rendered');
30795 this.on('childrenrendered', function() {
30796 Roo.log('children rendered');
30802 initial : function()
30804 this.selectedBrick = [];
30806 this.currentSize = this.el.getBox(true);
30808 Roo.EventManager.onWindowResize(this.resize, this);
30810 if(!this.isAutoInitial){
30818 //this.layout.defer(500,this);
30822 resize : function()
30824 var cs = this.el.getBox(true);
30827 this.currentSize.width == cs.width &&
30828 this.currentSize.x == cs.x &&
30829 this.currentSize.height == cs.height &&
30830 this.currentSize.y == cs.y
30832 Roo.log("no change in with or X or Y");
30836 this.currentSize = cs;
30842 layout : function()
30844 this._resetLayout();
30846 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30848 this.layoutItems( isInstant );
30850 this._isLayoutInited = true;
30852 this.fireEvent('layout', this);
30856 _resetLayout : function()
30858 if(this.isHorizontal){
30859 this.horizontalMeasureColumns();
30863 this.verticalMeasureColumns();
30867 verticalMeasureColumns : function()
30869 this.getContainerWidth();
30871 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30872 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30876 var boxWidth = this.boxWidth + this.padWidth;
30878 if(this.containerWidth < this.boxWidth){
30879 boxWidth = this.containerWidth
30882 var containerWidth = this.containerWidth;
30884 var cols = Math.floor(containerWidth / boxWidth);
30886 this.cols = Math.max( cols, 1 );
30888 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30890 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30892 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30894 this.colWidth = boxWidth + avail - this.padWidth;
30896 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30897 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30900 horizontalMeasureColumns : function()
30902 this.getContainerWidth();
30904 var boxWidth = this.boxWidth;
30906 if(this.containerWidth < boxWidth){
30907 boxWidth = this.containerWidth;
30910 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30912 this.el.setHeight(boxWidth);
30916 getContainerWidth : function()
30918 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30921 layoutItems : function( isInstant )
30923 Roo.log(this.bricks);
30925 var items = Roo.apply([], this.bricks);
30927 if(this.isHorizontal){
30928 this._horizontalLayoutItems( items , isInstant );
30932 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30933 // this._verticalAlternativeLayoutItems( items , isInstant );
30937 this._verticalLayoutItems( items , isInstant );
30941 _verticalLayoutItems : function ( items , isInstant)
30943 if ( !items || !items.length ) {
30948 ['xs', 'xs', 'xs', 'tall'],
30949 ['xs', 'xs', 'tall'],
30950 ['xs', 'xs', 'sm'],
30951 ['xs', 'xs', 'xs'],
30957 ['sm', 'xs', 'xs'],
30961 ['tall', 'xs', 'xs', 'xs'],
30962 ['tall', 'xs', 'xs'],
30974 Roo.each(items, function(item, k){
30976 switch (item.size) {
30977 // these layouts take up a full box,
30988 boxes.push([item]);
31011 var filterPattern = function(box, length)
31019 var pattern = box.slice(0, length);
31023 Roo.each(pattern, function(i){
31024 format.push(i.size);
31027 Roo.each(standard, function(s){
31029 if(String(s) != String(format)){
31038 if(!match && length == 1){
31043 filterPattern(box, length - 1);
31047 queue.push(pattern);
31049 box = box.slice(length, box.length);
31051 filterPattern(box, 4);
31057 Roo.each(boxes, function(box, k){
31063 if(box.length == 1){
31068 filterPattern(box, 4);
31072 this._processVerticalLayoutQueue( queue, isInstant );
31076 // _verticalAlternativeLayoutItems : function( items , isInstant )
31078 // if ( !items || !items.length ) {
31082 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31086 _horizontalLayoutItems : function ( items , isInstant)
31088 if ( !items || !items.length || items.length < 3) {
31094 var eItems = items.slice(0, 3);
31096 items = items.slice(3, items.length);
31099 ['xs', 'xs', 'xs', 'wide'],
31100 ['xs', 'xs', 'wide'],
31101 ['xs', 'xs', 'sm'],
31102 ['xs', 'xs', 'xs'],
31108 ['sm', 'xs', 'xs'],
31112 ['wide', 'xs', 'xs', 'xs'],
31113 ['wide', 'xs', 'xs'],
31126 Roo.each(items, function(item, k){
31128 switch (item.size) {
31139 boxes.push([item]);
31163 var filterPattern = function(box, length)
31171 var pattern = box.slice(0, length);
31175 Roo.each(pattern, function(i){
31176 format.push(i.size);
31179 Roo.each(standard, function(s){
31181 if(String(s) != String(format)){
31190 if(!match && length == 1){
31195 filterPattern(box, length - 1);
31199 queue.push(pattern);
31201 box = box.slice(length, box.length);
31203 filterPattern(box, 4);
31209 Roo.each(boxes, function(box, k){
31215 if(box.length == 1){
31220 filterPattern(box, 4);
31227 var pos = this.el.getBox(true);
31231 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31233 var hit_end = false;
31235 Roo.each(queue, function(box){
31239 Roo.each(box, function(b){
31241 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31251 Roo.each(box, function(b){
31253 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31256 mx = Math.max(mx, b.x);
31260 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31264 Roo.each(box, function(b){
31266 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31280 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31283 /** Sets position of item in DOM
31284 * @param {Element} item
31285 * @param {Number} x - horizontal position
31286 * @param {Number} y - vertical position
31287 * @param {Boolean} isInstant - disables transitions
31289 _processVerticalLayoutQueue : function( queue, isInstant )
31291 var pos = this.el.getBox(true);
31296 for (var i = 0; i < this.cols; i++){
31300 Roo.each(queue, function(box, k){
31302 var col = k % this.cols;
31304 Roo.each(box, function(b,kk){
31306 b.el.position('absolute');
31308 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31309 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31311 if(b.size == 'md-left' || b.size == 'md-right'){
31312 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31313 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31316 b.el.setWidth(width);
31317 b.el.setHeight(height);
31319 b.el.select('iframe',true).setSize(width,height);
31323 for (var i = 0; i < this.cols; i++){
31325 if(maxY[i] < maxY[col]){
31330 col = Math.min(col, i);
31334 x = pos.x + col * (this.colWidth + this.padWidth);
31338 var positions = [];
31340 switch (box.length){
31342 positions = this.getVerticalOneBoxColPositions(x, y, box);
31345 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31348 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31351 positions = this.getVerticalFourBoxColPositions(x, y, box);
31357 Roo.each(box, function(b,kk){
31359 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31361 var sz = b.el.getSize();
31363 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31371 for (var i = 0; i < this.cols; i++){
31372 mY = Math.max(mY, maxY[i]);
31375 this.el.setHeight(mY - pos.y);
31379 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31381 // var pos = this.el.getBox(true);
31384 // var maxX = pos.right;
31386 // var maxHeight = 0;
31388 // Roo.each(items, function(item, k){
31392 // item.el.position('absolute');
31394 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31396 // item.el.setWidth(width);
31398 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31400 // item.el.setHeight(height);
31403 // item.el.setXY([x, y], isInstant ? false : true);
31405 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31408 // y = y + height + this.alternativePadWidth;
31410 // maxHeight = maxHeight + height + this.alternativePadWidth;
31414 // this.el.setHeight(maxHeight);
31418 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31420 var pos = this.el.getBox(true);
31425 var maxX = pos.right;
31427 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31429 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31431 Roo.each(queue, function(box, k){
31433 Roo.each(box, function(b, kk){
31435 b.el.position('absolute');
31437 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31438 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31440 if(b.size == 'md-left' || b.size == 'md-right'){
31441 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31442 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31445 b.el.setWidth(width);
31446 b.el.setHeight(height);
31454 var positions = [];
31456 switch (box.length){
31458 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31461 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31464 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31467 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31473 Roo.each(box, function(b,kk){
31475 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31477 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31485 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31487 Roo.each(eItems, function(b,k){
31489 b.size = (k == 0) ? 'sm' : 'xs';
31490 b.x = (k == 0) ? 2 : 1;
31491 b.y = (k == 0) ? 2 : 1;
31493 b.el.position('absolute');
31495 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31497 b.el.setWidth(width);
31499 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31501 b.el.setHeight(height);
31505 var positions = [];
31508 x : maxX - this.unitWidth * 2 - this.gutter,
31513 x : maxX - this.unitWidth,
31514 y : minY + (this.unitWidth + this.gutter) * 2
31518 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31522 Roo.each(eItems, function(b,k){
31524 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31530 getVerticalOneBoxColPositions : function(x, y, box)
31534 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31536 if(box[0].size == 'md-left'){
31540 if(box[0].size == 'md-right'){
31545 x : x + (this.unitWidth + this.gutter) * rand,
31552 getVerticalTwoBoxColPositions : function(x, y, box)
31556 if(box[0].size == 'xs'){
31560 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31564 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31578 x : x + (this.unitWidth + this.gutter) * 2,
31579 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31586 getVerticalThreeBoxColPositions : function(x, y, box)
31590 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31598 x : x + (this.unitWidth + this.gutter) * 1,
31603 x : x + (this.unitWidth + this.gutter) * 2,
31611 if(box[0].size == 'xs' && box[1].size == 'xs'){
31620 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31624 x : x + (this.unitWidth + this.gutter) * 1,
31638 x : x + (this.unitWidth + this.gutter) * 2,
31643 x : x + (this.unitWidth + this.gutter) * 2,
31644 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31651 getVerticalFourBoxColPositions : function(x, y, box)
31655 if(box[0].size == 'xs'){
31664 y : y + (this.unitHeight + this.gutter) * 1
31669 y : y + (this.unitHeight + this.gutter) * 2
31673 x : x + (this.unitWidth + this.gutter) * 1,
31687 x : x + (this.unitWidth + this.gutter) * 2,
31692 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31693 y : y + (this.unitHeight + this.gutter) * 1
31697 x : x + (this.unitWidth + this.gutter) * 2,
31698 y : y + (this.unitWidth + this.gutter) * 2
31705 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31709 if(box[0].size == 'md-left'){
31711 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31718 if(box[0].size == 'md-right'){
31720 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31721 y : minY + (this.unitWidth + this.gutter) * 1
31727 var rand = Math.floor(Math.random() * (4 - box[0].y));
31730 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31731 y : minY + (this.unitWidth + this.gutter) * rand
31738 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31742 if(box[0].size == 'xs'){
31745 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31750 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31751 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31759 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31764 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31765 y : minY + (this.unitWidth + this.gutter) * 2
31772 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31776 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31779 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31784 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31785 y : minY + (this.unitWidth + this.gutter) * 1
31789 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31790 y : minY + (this.unitWidth + this.gutter) * 2
31797 if(box[0].size == 'xs' && box[1].size == 'xs'){
31800 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31805 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31810 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31811 y : minY + (this.unitWidth + this.gutter) * 1
31819 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31824 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31825 y : minY + (this.unitWidth + this.gutter) * 2
31829 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31830 y : minY + (this.unitWidth + this.gutter) * 2
31837 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31841 if(box[0].size == 'xs'){
31844 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31849 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31854 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),
31859 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31860 y : minY + (this.unitWidth + this.gutter) * 1
31868 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31873 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31874 y : minY + (this.unitWidth + this.gutter) * 2
31878 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31879 y : minY + (this.unitWidth + this.gutter) * 2
31883 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),
31884 y : minY + (this.unitWidth + this.gutter) * 2
31892 * remove a Masonry Brick
31893 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31895 removeBrick : function(brick_id)
31901 for (var i = 0; i<this.bricks.length; i++) {
31902 if (this.bricks[i].id == brick_id) {
31903 this.bricks.splice(i,1);
31904 this.el.dom.removeChild(Roo.get(brick_id).dom);
31911 * adds a Masonry Brick
31912 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31914 addBrick : function(cfg)
31916 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31917 //this.register(cn);
31918 cn.parentId = this.id;
31919 cn.onRender(this.el, null);
31924 * register a Masonry Brick
31925 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31928 register : function(brick)
31930 this.bricks.push(brick);
31931 brick.masonryId = this.id;
31935 * clear all the Masonry Brick
31937 clearAll : function()
31940 //this.getChildContainer().dom.innerHTML = "";
31941 this.el.dom.innerHTML = '';
31944 getSelected : function()
31946 if (!this.selectedBrick) {
31950 return this.selectedBrick;
31954 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31958 * register a Masonry Layout
31959 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31962 register : function(layout)
31964 this.groups[layout.id] = layout;
31967 * fetch a Masonry Layout based on the masonry layout ID
31968 * @param {string} the masonry layout to add
31969 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31972 get: function(layout_id) {
31973 if (typeof(this.groups[layout_id]) == 'undefined') {
31976 return this.groups[layout_id] ;
31988 * http://masonry.desandro.com
31990 * The idea is to render all the bricks based on vertical width...
31992 * The original code extends 'outlayer' - we might need to use that....
31998 * @class Roo.bootstrap.LayoutMasonryAuto
31999 * @extends Roo.bootstrap.Component
32000 * Bootstrap Layout Masonry class
32003 * Create a new Element
32004 * @param {Object} config The config object
32007 Roo.bootstrap.LayoutMasonryAuto = function(config){
32008 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32011 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32014 * @cfg {Boolean} isFitWidth - resize the width..
32016 isFitWidth : false, // options..
32018 * @cfg {Boolean} isOriginLeft = left align?
32020 isOriginLeft : true,
32022 * @cfg {Boolean} isOriginTop = top align?
32024 isOriginTop : false,
32026 * @cfg {Boolean} isLayoutInstant = no animation?
32028 isLayoutInstant : false, // needed?
32030 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32032 isResizingContainer : true,
32034 * @cfg {Number} columnWidth width of the columns
32040 * @cfg {Number} maxCols maximum number of columns
32045 * @cfg {Number} padHeight padding below box..
32051 * @cfg {Boolean} isAutoInitial defalut true
32054 isAutoInitial : true,
32060 initialColumnWidth : 0,
32061 currentSize : null,
32063 colYs : null, // array.
32070 bricks: null, //CompositeElement
32071 cols : 0, // array?
32072 // element : null, // wrapped now this.el
32073 _isLayoutInited : null,
32076 getAutoCreate : function(){
32080 cls: 'blog-masonary-wrapper ' + this.cls,
32082 cls : 'mas-boxes masonary'
32089 getChildContainer: function( )
32091 if (this.boxesEl) {
32092 return this.boxesEl;
32095 this.boxesEl = this.el.select('.mas-boxes').first();
32097 return this.boxesEl;
32101 initEvents : function()
32105 if(this.isAutoInitial){
32106 Roo.log('hook children rendered');
32107 this.on('childrenrendered', function() {
32108 Roo.log('children rendered');
32115 initial : function()
32117 this.reloadItems();
32119 this.currentSize = this.el.getBox(true);
32121 /// was window resize... - let's see if this works..
32122 Roo.EventManager.onWindowResize(this.resize, this);
32124 if(!this.isAutoInitial){
32129 this.layout.defer(500,this);
32132 reloadItems: function()
32134 this.bricks = this.el.select('.masonry-brick', true);
32136 this.bricks.each(function(b) {
32137 //Roo.log(b.getSize());
32138 if (!b.attr('originalwidth')) {
32139 b.attr('originalwidth', b.getSize().width);
32144 Roo.log(this.bricks.elements.length);
32147 resize : function()
32150 var cs = this.el.getBox(true);
32152 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32153 Roo.log("no change in with or X");
32156 this.currentSize = cs;
32160 layout : function()
32163 this._resetLayout();
32164 //this._manageStamps();
32166 // don't animate first layout
32167 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32168 this.layoutItems( isInstant );
32170 // flag for initalized
32171 this._isLayoutInited = true;
32174 layoutItems : function( isInstant )
32176 //var items = this._getItemsForLayout( this.items );
32177 // original code supports filtering layout items.. we just ignore it..
32179 this._layoutItems( this.bricks , isInstant );
32181 this._postLayout();
32183 _layoutItems : function ( items , isInstant)
32185 //this.fireEvent( 'layout', this, items );
32188 if ( !items || !items.elements.length ) {
32189 // no items, emit event with empty array
32194 items.each(function(item) {
32195 Roo.log("layout item");
32197 // get x/y object from method
32198 var position = this._getItemLayoutPosition( item );
32200 position.item = item;
32201 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32202 queue.push( position );
32205 this._processLayoutQueue( queue );
32207 /** Sets position of item in DOM
32208 * @param {Element} item
32209 * @param {Number} x - horizontal position
32210 * @param {Number} y - vertical position
32211 * @param {Boolean} isInstant - disables transitions
32213 _processLayoutQueue : function( queue )
32215 for ( var i=0, len = queue.length; i < len; i++ ) {
32216 var obj = queue[i];
32217 obj.item.position('absolute');
32218 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32224 * Any logic you want to do after each layout,
32225 * i.e. size the container
32227 _postLayout : function()
32229 this.resizeContainer();
32232 resizeContainer : function()
32234 if ( !this.isResizingContainer ) {
32237 var size = this._getContainerSize();
32239 this.el.setSize(size.width,size.height);
32240 this.boxesEl.setSize(size.width,size.height);
32246 _resetLayout : function()
32248 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32249 this.colWidth = this.el.getWidth();
32250 //this.gutter = this.el.getWidth();
32252 this.measureColumns();
32258 this.colYs.push( 0 );
32264 measureColumns : function()
32266 this.getContainerWidth();
32267 // if columnWidth is 0, default to outerWidth of first item
32268 if ( !this.columnWidth ) {
32269 var firstItem = this.bricks.first();
32270 Roo.log(firstItem);
32271 this.columnWidth = this.containerWidth;
32272 if (firstItem && firstItem.attr('originalwidth') ) {
32273 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32275 // columnWidth fall back to item of first element
32276 Roo.log("set column width?");
32277 this.initialColumnWidth = this.columnWidth ;
32279 // if first elem has no width, default to size of container
32284 if (this.initialColumnWidth) {
32285 this.columnWidth = this.initialColumnWidth;
32290 // column width is fixed at the top - however if container width get's smaller we should
32293 // this bit calcs how man columns..
32295 var columnWidth = this.columnWidth += this.gutter;
32297 // calculate columns
32298 var containerWidth = this.containerWidth + this.gutter;
32300 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32301 // fix rounding errors, typically with gutters
32302 var excess = columnWidth - containerWidth % columnWidth;
32305 // if overshoot is less than a pixel, round up, otherwise floor it
32306 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32307 cols = Math[ mathMethod ]( cols );
32308 this.cols = Math.max( cols, 1 );
32309 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32311 // padding positioning..
32312 var totalColWidth = this.cols * this.columnWidth;
32313 var padavail = this.containerWidth - totalColWidth;
32314 // so for 2 columns - we need 3 'pads'
32316 var padNeeded = (1+this.cols) * this.padWidth;
32318 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32320 this.columnWidth += padExtra
32321 //this.padWidth = Math.floor(padavail / ( this.cols));
32323 // adjust colum width so that padding is fixed??
32325 // we have 3 columns ... total = width * 3
32326 // we have X left over... that should be used by
32328 //if (this.expandC) {
32336 getContainerWidth : function()
32338 /* // container is parent if fit width
32339 var container = this.isFitWidth ? this.element.parentNode : this.element;
32340 // check that this.size and size are there
32341 // IE8 triggers resize on body size change, so they might not be
32343 var size = getSize( container ); //FIXME
32344 this.containerWidth = size && size.innerWidth; //FIXME
32347 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32351 _getItemLayoutPosition : function( item ) // what is item?
32353 // we resize the item to our columnWidth..
32355 item.setWidth(this.columnWidth);
32356 item.autoBoxAdjust = false;
32358 var sz = item.getSize();
32360 // how many columns does this brick span
32361 var remainder = this.containerWidth % this.columnWidth;
32363 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32364 // round if off by 1 pixel, otherwise use ceil
32365 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32366 colSpan = Math.min( colSpan, this.cols );
32368 // normally this should be '1' as we dont' currently allow multi width columns..
32370 var colGroup = this._getColGroup( colSpan );
32371 // get the minimum Y value from the columns
32372 var minimumY = Math.min.apply( Math, colGroup );
32373 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32375 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32377 // position the brick
32379 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32380 y: this.currentSize.y + minimumY + this.padHeight
32384 // apply setHeight to necessary columns
32385 var setHeight = minimumY + sz.height + this.padHeight;
32386 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32388 var setSpan = this.cols + 1 - colGroup.length;
32389 for ( var i = 0; i < setSpan; i++ ) {
32390 this.colYs[ shortColIndex + i ] = setHeight ;
32397 * @param {Number} colSpan - number of columns the element spans
32398 * @returns {Array} colGroup
32400 _getColGroup : function( colSpan )
32402 if ( colSpan < 2 ) {
32403 // if brick spans only one column, use all the column Ys
32408 // how many different places could this brick fit horizontally
32409 var groupCount = this.cols + 1 - colSpan;
32410 // for each group potential horizontal position
32411 for ( var i = 0; i < groupCount; i++ ) {
32412 // make an array of colY values for that one group
32413 var groupColYs = this.colYs.slice( i, i + colSpan );
32414 // and get the max value of the array
32415 colGroup[i] = Math.max.apply( Math, groupColYs );
32420 _manageStamp : function( stamp )
32422 var stampSize = stamp.getSize();
32423 var offset = stamp.getBox();
32424 // get the columns that this stamp affects
32425 var firstX = this.isOriginLeft ? offset.x : offset.right;
32426 var lastX = firstX + stampSize.width;
32427 var firstCol = Math.floor( firstX / this.columnWidth );
32428 firstCol = Math.max( 0, firstCol );
32430 var lastCol = Math.floor( lastX / this.columnWidth );
32431 // lastCol should not go over if multiple of columnWidth #425
32432 lastCol -= lastX % this.columnWidth ? 0 : 1;
32433 lastCol = Math.min( this.cols - 1, lastCol );
32435 // set colYs to bottom of the stamp
32436 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32439 for ( var i = firstCol; i <= lastCol; i++ ) {
32440 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32445 _getContainerSize : function()
32447 this.maxY = Math.max.apply( Math, this.colYs );
32452 if ( this.isFitWidth ) {
32453 size.width = this._getContainerFitWidth();
32459 _getContainerFitWidth : function()
32461 var unusedCols = 0;
32462 // count unused columns
32465 if ( this.colYs[i] !== 0 ) {
32470 // fit container to columns that have been used
32471 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32474 needsResizeLayout : function()
32476 var previousWidth = this.containerWidth;
32477 this.getContainerWidth();
32478 return previousWidth !== this.containerWidth;
32493 * @class Roo.bootstrap.MasonryBrick
32494 * @extends Roo.bootstrap.Component
32495 * Bootstrap MasonryBrick class
32498 * Create a new MasonryBrick
32499 * @param {Object} config The config object
32502 Roo.bootstrap.MasonryBrick = function(config){
32504 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32506 Roo.bootstrap.MasonryBrick.register(this);
32512 * When a MasonryBrick is clcik
32513 * @param {Roo.bootstrap.MasonryBrick} this
32514 * @param {Roo.EventObject} e
32520 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32523 * @cfg {String} title
32527 * @cfg {String} html
32531 * @cfg {String} bgimage
32535 * @cfg {String} videourl
32539 * @cfg {String} cls
32543 * @cfg {String} href
32547 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32552 * @cfg {String} placetitle (center|bottom)
32557 * @cfg {Boolean} isFitContainer defalut true
32559 isFitContainer : true,
32562 * @cfg {Boolean} preventDefault defalut false
32564 preventDefault : false,
32567 * @cfg {Boolean} inverse defalut false
32569 maskInverse : false,
32571 getAutoCreate : function()
32573 if(!this.isFitContainer){
32574 return this.getSplitAutoCreate();
32577 var cls = 'masonry-brick masonry-brick-full';
32579 if(this.href.length){
32580 cls += ' masonry-brick-link';
32583 if(this.bgimage.length){
32584 cls += ' masonry-brick-image';
32587 if(this.maskInverse){
32588 cls += ' mask-inverse';
32591 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32592 cls += ' enable-mask';
32596 cls += ' masonry-' + this.size + '-brick';
32599 if(this.placetitle.length){
32601 switch (this.placetitle) {
32603 cls += ' masonry-center-title';
32606 cls += ' masonry-bottom-title';
32613 if(!this.html.length && !this.bgimage.length){
32614 cls += ' masonry-center-title';
32617 if(!this.html.length && this.bgimage.length){
32618 cls += ' masonry-bottom-title';
32623 cls += ' ' + this.cls;
32627 tag: (this.href.length) ? 'a' : 'div',
32632 cls: 'masonry-brick-mask'
32636 cls: 'masonry-brick-paragraph',
32642 if(this.href.length){
32643 cfg.href = this.href;
32646 var cn = cfg.cn[1].cn;
32648 if(this.title.length){
32651 cls: 'masonry-brick-title',
32656 if(this.html.length){
32659 cls: 'masonry-brick-text',
32664 if (!this.title.length && !this.html.length) {
32665 cfg.cn[1].cls += ' hide';
32668 if(this.bgimage.length){
32671 cls: 'masonry-brick-image-view',
32676 if(this.videourl.length){
32677 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32678 // youtube support only?
32681 cls: 'masonry-brick-image-view',
32684 allowfullscreen : true
32692 getSplitAutoCreate : function()
32694 var cls = 'masonry-brick masonry-brick-split';
32696 if(this.href.length){
32697 cls += ' masonry-brick-link';
32700 if(this.bgimage.length){
32701 cls += ' masonry-brick-image';
32705 cls += ' masonry-' + this.size + '-brick';
32708 switch (this.placetitle) {
32710 cls += ' masonry-center-title';
32713 cls += ' masonry-bottom-title';
32716 if(!this.bgimage.length){
32717 cls += ' masonry-center-title';
32720 if(this.bgimage.length){
32721 cls += ' masonry-bottom-title';
32727 cls += ' ' + this.cls;
32731 tag: (this.href.length) ? 'a' : 'div',
32736 cls: 'masonry-brick-split-head',
32740 cls: 'masonry-brick-paragraph',
32747 cls: 'masonry-brick-split-body',
32753 if(this.href.length){
32754 cfg.href = this.href;
32757 if(this.title.length){
32758 cfg.cn[0].cn[0].cn.push({
32760 cls: 'masonry-brick-title',
32765 if(this.html.length){
32766 cfg.cn[1].cn.push({
32768 cls: 'masonry-brick-text',
32773 if(this.bgimage.length){
32774 cfg.cn[0].cn.push({
32776 cls: 'masonry-brick-image-view',
32781 if(this.videourl.length){
32782 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32783 // youtube support only?
32784 cfg.cn[0].cn.cn.push({
32786 cls: 'masonry-brick-image-view',
32789 allowfullscreen : true
32796 initEvents: function()
32798 switch (this.size) {
32831 this.el.on('touchstart', this.onTouchStart, this);
32832 this.el.on('touchmove', this.onTouchMove, this);
32833 this.el.on('touchend', this.onTouchEnd, this);
32834 this.el.on('contextmenu', this.onContextMenu, this);
32836 this.el.on('mouseenter' ,this.enter, this);
32837 this.el.on('mouseleave', this.leave, this);
32838 this.el.on('click', this.onClick, this);
32841 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32842 this.parent().bricks.push(this);
32847 onClick: function(e, el)
32849 var time = this.endTimer - this.startTimer;
32850 // Roo.log(e.preventDefault());
32853 e.preventDefault();
32858 if(!this.preventDefault){
32862 e.preventDefault();
32864 if (this.activeClass != '') {
32865 this.selectBrick();
32868 this.fireEvent('click', this, e);
32871 enter: function(e, el)
32873 e.preventDefault();
32875 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32879 if(this.bgimage.length && this.html.length){
32880 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32884 leave: function(e, el)
32886 e.preventDefault();
32888 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32892 if(this.bgimage.length && this.html.length){
32893 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32897 onTouchStart: function(e, el)
32899 // e.preventDefault();
32901 this.touchmoved = false;
32903 if(!this.isFitContainer){
32907 if(!this.bgimage.length || !this.html.length){
32911 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32913 this.timer = new Date().getTime();
32917 onTouchMove: function(e, el)
32919 this.touchmoved = true;
32922 onContextMenu : function(e,el)
32924 e.preventDefault();
32925 e.stopPropagation();
32929 onTouchEnd: function(e, el)
32931 // e.preventDefault();
32933 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32940 if(!this.bgimage.length || !this.html.length){
32942 if(this.href.length){
32943 window.location.href = this.href;
32949 if(!this.isFitContainer){
32953 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32955 window.location.href = this.href;
32958 //selection on single brick only
32959 selectBrick : function() {
32961 if (!this.parentId) {
32965 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32966 var index = m.selectedBrick.indexOf(this.id);
32969 m.selectedBrick.splice(index,1);
32970 this.el.removeClass(this.activeClass);
32974 for(var i = 0; i < m.selectedBrick.length; i++) {
32975 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32976 b.el.removeClass(b.activeClass);
32979 m.selectedBrick = [];
32981 m.selectedBrick.push(this.id);
32982 this.el.addClass(this.activeClass);
32986 isSelected : function(){
32987 return this.el.hasClass(this.activeClass);
32992 Roo.apply(Roo.bootstrap.MasonryBrick, {
32995 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32997 * register a Masonry Brick
32998 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33001 register : function(brick)
33003 //this.groups[brick.id] = brick;
33004 this.groups.add(brick.id, brick);
33007 * fetch a masonry brick based on the masonry brick ID
33008 * @param {string} the masonry brick to add
33009 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33012 get: function(brick_id)
33014 // if (typeof(this.groups[brick_id]) == 'undefined') {
33017 // return this.groups[brick_id] ;
33019 if(this.groups.key(brick_id)) {
33020 return this.groups.key(brick_id);
33038 * @class Roo.bootstrap.Brick
33039 * @extends Roo.bootstrap.Component
33040 * Bootstrap Brick class
33043 * Create a new Brick
33044 * @param {Object} config The config object
33047 Roo.bootstrap.Brick = function(config){
33048 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33054 * When a Brick is click
33055 * @param {Roo.bootstrap.Brick} this
33056 * @param {Roo.EventObject} e
33062 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33065 * @cfg {String} title
33069 * @cfg {String} html
33073 * @cfg {String} bgimage
33077 * @cfg {String} cls
33081 * @cfg {String} href
33085 * @cfg {String} video
33089 * @cfg {Boolean} square
33093 getAutoCreate : function()
33095 var cls = 'roo-brick';
33097 if(this.href.length){
33098 cls += ' roo-brick-link';
33101 if(this.bgimage.length){
33102 cls += ' roo-brick-image';
33105 if(!this.html.length && !this.bgimage.length){
33106 cls += ' roo-brick-center-title';
33109 if(!this.html.length && this.bgimage.length){
33110 cls += ' roo-brick-bottom-title';
33114 cls += ' ' + this.cls;
33118 tag: (this.href.length) ? 'a' : 'div',
33123 cls: 'roo-brick-paragraph',
33129 if(this.href.length){
33130 cfg.href = this.href;
33133 var cn = cfg.cn[0].cn;
33135 if(this.title.length){
33138 cls: 'roo-brick-title',
33143 if(this.html.length){
33146 cls: 'roo-brick-text',
33153 if(this.bgimage.length){
33156 cls: 'roo-brick-image-view',
33164 initEvents: function()
33166 if(this.title.length || this.html.length){
33167 this.el.on('mouseenter' ,this.enter, this);
33168 this.el.on('mouseleave', this.leave, this);
33171 Roo.EventManager.onWindowResize(this.resize, this);
33173 if(this.bgimage.length){
33174 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33175 this.imageEl.on('load', this.onImageLoad, this);
33182 onImageLoad : function()
33187 resize : function()
33189 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33191 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33193 if(this.bgimage.length){
33194 var image = this.el.select('.roo-brick-image-view', true).first();
33196 image.setWidth(paragraph.getWidth());
33199 image.setHeight(paragraph.getWidth());
33202 this.el.setHeight(image.getHeight());
33203 paragraph.setHeight(image.getHeight());
33209 enter: function(e, el)
33211 e.preventDefault();
33213 if(this.bgimage.length){
33214 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33215 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33219 leave: function(e, el)
33221 e.preventDefault();
33223 if(this.bgimage.length){
33224 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33225 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33240 * @class Roo.bootstrap.NumberField
33241 * @extends Roo.bootstrap.Input
33242 * Bootstrap NumberField class
33248 * Create a new NumberField
33249 * @param {Object} config The config object
33252 Roo.bootstrap.NumberField = function(config){
33253 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33256 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33259 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33261 allowDecimals : true,
33263 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33265 decimalSeparator : ".",
33267 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33269 decimalPrecision : 2,
33271 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33273 allowNegative : true,
33276 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33280 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33282 minValue : Number.NEGATIVE_INFINITY,
33284 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33286 maxValue : Number.MAX_VALUE,
33288 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33290 minText : "The minimum value for this field is {0}",
33292 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33294 maxText : "The maximum value for this field is {0}",
33296 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33297 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33299 nanText : "{0} is not a valid number",
33301 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33303 thousandsDelimiter : false,
33305 * @cfg {String} valueAlign alignment of value
33307 valueAlign : "left",
33309 getAutoCreate : function()
33311 var hiddenInput = {
33315 cls: 'hidden-number-input'
33319 hiddenInput.name = this.name;
33324 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33326 this.name = hiddenInput.name;
33328 if(cfg.cn.length > 0) {
33329 cfg.cn.push(hiddenInput);
33336 initEvents : function()
33338 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33340 var allowed = "0123456789";
33342 if(this.allowDecimals){
33343 allowed += this.decimalSeparator;
33346 if(this.allowNegative){
33350 if(this.thousandsDelimiter) {
33354 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33356 var keyPress = function(e){
33358 var k = e.getKey();
33360 var c = e.getCharCode();
33363 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33364 allowed.indexOf(String.fromCharCode(c)) === -1
33370 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33374 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33379 this.el.on("keypress", keyPress, this);
33382 validateValue : function(value)
33385 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33389 var num = this.parseValue(value);
33392 this.markInvalid(String.format(this.nanText, value));
33396 if(num < this.minValue){
33397 this.markInvalid(String.format(this.minText, this.minValue));
33401 if(num > this.maxValue){
33402 this.markInvalid(String.format(this.maxText, this.maxValue));
33409 getValue : function()
33411 var v = this.hiddenEl().getValue();
33413 return this.fixPrecision(this.parseValue(v));
33416 parseValue : function(value)
33418 if(this.thousandsDelimiter) {
33420 r = new RegExp(",", "g");
33421 value = value.replace(r, "");
33424 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33425 return isNaN(value) ? '' : value;
33428 fixPrecision : function(value)
33430 if(this.thousandsDelimiter) {
33432 r = new RegExp(",", "g");
33433 value = value.replace(r, "");
33436 var nan = isNaN(value);
33438 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33439 return nan ? '' : value;
33441 return parseFloat(value).toFixed(this.decimalPrecision);
33444 setValue : function(v)
33446 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33452 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33454 this.inputEl().dom.value = (v == '') ? '' :
33455 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33457 if(!this.allowZero && v === '0') {
33458 this.hiddenEl().dom.value = '';
33459 this.inputEl().dom.value = '';
33466 decimalPrecisionFcn : function(v)
33468 return Math.floor(v);
33471 beforeBlur : function()
33473 var v = this.parseValue(this.getRawValue());
33475 if(v || v === 0 || v === ''){
33480 hiddenEl : function()
33482 return this.el.select('input.hidden-number-input',true).first();
33494 * @class Roo.bootstrap.DocumentSlider
33495 * @extends Roo.bootstrap.Component
33496 * Bootstrap DocumentSlider class
33499 * Create a new DocumentViewer
33500 * @param {Object} config The config object
33503 Roo.bootstrap.DocumentSlider = function(config){
33504 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33511 * Fire after initEvent
33512 * @param {Roo.bootstrap.DocumentSlider} this
33517 * Fire after update
33518 * @param {Roo.bootstrap.DocumentSlider} this
33524 * @param {Roo.bootstrap.DocumentSlider} this
33530 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33536 getAutoCreate : function()
33540 cls : 'roo-document-slider',
33544 cls : 'roo-document-slider-header',
33548 cls : 'roo-document-slider-header-title'
33554 cls : 'roo-document-slider-body',
33558 cls : 'roo-document-slider-prev',
33562 cls : 'fa fa-chevron-left'
33568 cls : 'roo-document-slider-thumb',
33572 cls : 'roo-document-slider-image'
33578 cls : 'roo-document-slider-next',
33582 cls : 'fa fa-chevron-right'
33594 initEvents : function()
33596 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33597 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33599 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33600 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33602 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33603 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33605 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33606 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33608 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33609 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33611 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33612 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33614 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33615 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33617 this.thumbEl.on('click', this.onClick, this);
33619 this.prevIndicator.on('click', this.prev, this);
33621 this.nextIndicator.on('click', this.next, this);
33625 initial : function()
33627 if(this.files.length){
33628 this.indicator = 1;
33632 this.fireEvent('initial', this);
33635 update : function()
33637 this.imageEl.attr('src', this.files[this.indicator - 1]);
33639 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33641 this.prevIndicator.show();
33643 if(this.indicator == 1){
33644 this.prevIndicator.hide();
33647 this.nextIndicator.show();
33649 if(this.indicator == this.files.length){
33650 this.nextIndicator.hide();
33653 this.thumbEl.scrollTo('top');
33655 this.fireEvent('update', this);
33658 onClick : function(e)
33660 e.preventDefault();
33662 this.fireEvent('click', this);
33667 e.preventDefault();
33669 this.indicator = Math.max(1, this.indicator - 1);
33676 e.preventDefault();
33678 this.indicator = Math.min(this.files.length, this.indicator + 1);
33692 * @class Roo.bootstrap.RadioSet
33693 * @extends Roo.bootstrap.Input
33694 * Bootstrap RadioSet class
33695 * @cfg {String} indicatorpos (left|right) default left
33696 * @cfg {Boolean} inline (true|false) inline the element (default true)
33697 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33699 * Create a new RadioSet
33700 * @param {Object} config The config object
33703 Roo.bootstrap.RadioSet = function(config){
33705 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33709 Roo.bootstrap.RadioSet.register(this);
33714 * Fires when the element is checked or unchecked.
33715 * @param {Roo.bootstrap.RadioSet} this This radio
33716 * @param {Roo.bootstrap.Radio} item The checked item
33721 * Fires when the element is click.
33722 * @param {Roo.bootstrap.RadioSet} this This radio set
33723 * @param {Roo.bootstrap.Radio} item The checked item
33724 * @param {Roo.EventObject} e The event object
33731 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33739 indicatorpos : 'left',
33741 getAutoCreate : function()
33745 cls : 'roo-radio-set-label',
33749 html : this.fieldLabel
33754 if(this.indicatorpos == 'left'){
33757 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33758 tooltip : 'This field is required'
33763 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33764 tooltip : 'This field is required'
33770 cls : 'roo-radio-set-items'
33773 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33775 if (align === 'left' && this.fieldLabel.length) {
33778 cls : "roo-radio-set-right",
33784 if(this.labelWidth > 12){
33785 label.style = "width: " + this.labelWidth + 'px';
33788 if(this.labelWidth < 13 && this.labelmd == 0){
33789 this.labelmd = this.labelWidth;
33792 if(this.labellg > 0){
33793 label.cls += ' col-lg-' + this.labellg;
33794 items.cls += ' col-lg-' + (12 - this.labellg);
33797 if(this.labelmd > 0){
33798 label.cls += ' col-md-' + this.labelmd;
33799 items.cls += ' col-md-' + (12 - this.labelmd);
33802 if(this.labelsm > 0){
33803 label.cls += ' col-sm-' + this.labelsm;
33804 items.cls += ' col-sm-' + (12 - this.labelsm);
33807 if(this.labelxs > 0){
33808 label.cls += ' col-xs-' + this.labelxs;
33809 items.cls += ' col-xs-' + (12 - this.labelxs);
33815 cls : 'roo-radio-set',
33819 cls : 'roo-radio-set-input',
33822 value : this.value ? this.value : ''
33829 if(this.weight.length){
33830 cfg.cls += ' roo-radio-' + this.weight;
33834 cfg.cls += ' roo-radio-set-inline';
33838 ['xs','sm','md','lg'].map(function(size){
33839 if (settings[size]) {
33840 cfg.cls += ' col-' + size + '-' + settings[size];
33848 initEvents : function()
33850 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33851 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33853 if(!this.fieldLabel.length){
33854 this.labelEl.hide();
33857 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33858 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33860 this.indicator = this.indicatorEl();
33862 if(this.indicator){
33863 this.indicator.addClass('invisible');
33866 this.originalValue = this.getValue();
33870 inputEl: function ()
33872 return this.el.select('.roo-radio-set-input', true).first();
33875 getChildContainer : function()
33877 return this.itemsEl;
33880 register : function(item)
33882 this.radioes.push(item);
33886 validate : function()
33888 if(this.getVisibilityEl().hasClass('hidden')){
33894 Roo.each(this.radioes, function(i){
33903 if(this.allowBlank) {
33907 if(this.disabled || valid){
33912 this.markInvalid();
33917 markValid : function()
33919 if(this.labelEl.isVisible(true)){
33920 this.indicatorEl().removeClass('visible');
33921 this.indicatorEl().addClass('invisible');
33924 this.el.removeClass([this.invalidClass, this.validClass]);
33925 this.el.addClass(this.validClass);
33927 this.fireEvent('valid', this);
33930 markInvalid : function(msg)
33932 if(this.allowBlank || this.disabled){
33936 if(this.labelEl.isVisible(true)){
33937 this.indicatorEl().removeClass('invisible');
33938 this.indicatorEl().addClass('visible');
33941 this.el.removeClass([this.invalidClass, this.validClass]);
33942 this.el.addClass(this.invalidClass);
33944 this.fireEvent('invalid', this, msg);
33948 setValue : function(v, suppressEvent)
33950 if(this.value === v){
33957 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33960 Roo.each(this.radioes, function(i){
33962 i.el.removeClass('checked');
33965 Roo.each(this.radioes, function(i){
33967 if(i.value === v || i.value.toString() === v.toString()){
33969 i.el.addClass('checked');
33971 if(suppressEvent !== true){
33972 this.fireEvent('check', this, i);
33983 clearInvalid : function(){
33985 if(!this.el || this.preventMark){
33989 this.el.removeClass([this.invalidClass]);
33991 this.fireEvent('valid', this);
33996 Roo.apply(Roo.bootstrap.RadioSet, {
34000 register : function(set)
34002 this.groups[set.name] = set;
34005 get: function(name)
34007 if (typeof(this.groups[name]) == 'undefined') {
34011 return this.groups[name] ;
34017 * Ext JS Library 1.1.1
34018 * Copyright(c) 2006-2007, Ext JS, LLC.
34020 * Originally Released Under LGPL - original licence link has changed is not relivant.
34023 * <script type="text/javascript">
34028 * @class Roo.bootstrap.SplitBar
34029 * @extends Roo.util.Observable
34030 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34034 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34035 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34036 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34037 split.minSize = 100;
34038 split.maxSize = 600;
34039 split.animate = true;
34040 split.on('moved', splitterMoved);
34043 * Create a new SplitBar
34044 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34045 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34046 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34047 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34048 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34049 position of the SplitBar).
34051 Roo.bootstrap.SplitBar = function(cfg){
34056 // dragElement : elm
34057 // resizingElement: el,
34059 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34060 // placement : Roo.bootstrap.SplitBar.LEFT ,
34061 // existingProxy ???
34064 this.el = Roo.get(cfg.dragElement, true);
34065 this.el.dom.unselectable = "on";
34067 this.resizingEl = Roo.get(cfg.resizingElement, true);
34071 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34072 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34075 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34078 * The minimum size of the resizing element. (Defaults to 0)
34084 * The maximum size of the resizing element. (Defaults to 2000)
34087 this.maxSize = 2000;
34090 * Whether to animate the transition to the new size
34093 this.animate = false;
34096 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34099 this.useShim = false;
34104 if(!cfg.existingProxy){
34106 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34108 this.proxy = Roo.get(cfg.existingProxy).dom;
34111 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34114 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34117 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34120 this.dragSpecs = {};
34123 * @private The adapter to use to positon and resize elements
34125 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34126 this.adapter.init(this);
34128 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34130 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34131 this.el.addClass("roo-splitbar-h");
34134 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34135 this.el.addClass("roo-splitbar-v");
34141 * Fires when the splitter is moved (alias for {@link #event-moved})
34142 * @param {Roo.bootstrap.SplitBar} this
34143 * @param {Number} newSize the new width or height
34148 * Fires when the splitter is moved
34149 * @param {Roo.bootstrap.SplitBar} this
34150 * @param {Number} newSize the new width or height
34154 * @event beforeresize
34155 * Fires before the splitter is dragged
34156 * @param {Roo.bootstrap.SplitBar} this
34158 "beforeresize" : true,
34160 "beforeapply" : true
34163 Roo.util.Observable.call(this);
34166 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34167 onStartProxyDrag : function(x, y){
34168 this.fireEvent("beforeresize", this);
34170 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34172 o.enableDisplayMode("block");
34173 // all splitbars share the same overlay
34174 Roo.bootstrap.SplitBar.prototype.overlay = o;
34176 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34177 this.overlay.show();
34178 Roo.get(this.proxy).setDisplayed("block");
34179 var size = this.adapter.getElementSize(this);
34180 this.activeMinSize = this.getMinimumSize();;
34181 this.activeMaxSize = this.getMaximumSize();;
34182 var c1 = size - this.activeMinSize;
34183 var c2 = Math.max(this.activeMaxSize - size, 0);
34184 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34185 this.dd.resetConstraints();
34186 this.dd.setXConstraint(
34187 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34188 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34190 this.dd.setYConstraint(0, 0);
34192 this.dd.resetConstraints();
34193 this.dd.setXConstraint(0, 0);
34194 this.dd.setYConstraint(
34195 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34196 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34199 this.dragSpecs.startSize = size;
34200 this.dragSpecs.startPoint = [x, y];
34201 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34205 * @private Called after the drag operation by the DDProxy
34207 onEndProxyDrag : function(e){
34208 Roo.get(this.proxy).setDisplayed(false);
34209 var endPoint = Roo.lib.Event.getXY(e);
34211 this.overlay.hide();
34214 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34215 newSize = this.dragSpecs.startSize +
34216 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34217 endPoint[0] - this.dragSpecs.startPoint[0] :
34218 this.dragSpecs.startPoint[0] - endPoint[0]
34221 newSize = this.dragSpecs.startSize +
34222 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34223 endPoint[1] - this.dragSpecs.startPoint[1] :
34224 this.dragSpecs.startPoint[1] - endPoint[1]
34227 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34228 if(newSize != this.dragSpecs.startSize){
34229 if(this.fireEvent('beforeapply', this, newSize) !== false){
34230 this.adapter.setElementSize(this, newSize);
34231 this.fireEvent("moved", this, newSize);
34232 this.fireEvent("resize", this, newSize);
34238 * Get the adapter this SplitBar uses
34239 * @return The adapter object
34241 getAdapter : function(){
34242 return this.adapter;
34246 * Set the adapter this SplitBar uses
34247 * @param {Object} adapter A SplitBar adapter object
34249 setAdapter : function(adapter){
34250 this.adapter = adapter;
34251 this.adapter.init(this);
34255 * Gets the minimum size for the resizing element
34256 * @return {Number} The minimum size
34258 getMinimumSize : function(){
34259 return this.minSize;
34263 * Sets the minimum size for the resizing element
34264 * @param {Number} minSize The minimum size
34266 setMinimumSize : function(minSize){
34267 this.minSize = minSize;
34271 * Gets the maximum size for the resizing element
34272 * @return {Number} The maximum size
34274 getMaximumSize : function(){
34275 return this.maxSize;
34279 * Sets the maximum size for the resizing element
34280 * @param {Number} maxSize The maximum size
34282 setMaximumSize : function(maxSize){
34283 this.maxSize = maxSize;
34287 * Sets the initialize size for the resizing element
34288 * @param {Number} size The initial size
34290 setCurrentSize : function(size){
34291 var oldAnimate = this.animate;
34292 this.animate = false;
34293 this.adapter.setElementSize(this, size);
34294 this.animate = oldAnimate;
34298 * Destroy this splitbar.
34299 * @param {Boolean} removeEl True to remove the element
34301 destroy : function(removeEl){
34303 this.shim.remove();
34306 this.proxy.parentNode.removeChild(this.proxy);
34314 * @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.
34316 Roo.bootstrap.SplitBar.createProxy = function(dir){
34317 var proxy = new Roo.Element(document.createElement("div"));
34318 proxy.unselectable();
34319 var cls = 'roo-splitbar-proxy';
34320 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34321 document.body.appendChild(proxy.dom);
34326 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34327 * Default Adapter. It assumes the splitter and resizing element are not positioned
34328 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34330 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34333 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34334 // do nothing for now
34335 init : function(s){
34339 * Called before drag operations to get the current size of the resizing element.
34340 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34342 getElementSize : function(s){
34343 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34344 return s.resizingEl.getWidth();
34346 return s.resizingEl.getHeight();
34351 * Called after drag operations to set the size of the resizing element.
34352 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34353 * @param {Number} newSize The new size to set
34354 * @param {Function} onComplete A function to be invoked when resizing is complete
34356 setElementSize : function(s, newSize, onComplete){
34357 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34359 s.resizingEl.setWidth(newSize);
34361 onComplete(s, newSize);
34364 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34369 s.resizingEl.setHeight(newSize);
34371 onComplete(s, newSize);
34374 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34381 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34382 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34383 * Adapter that moves the splitter element to align with the resized sizing element.
34384 * Used with an absolute positioned SplitBar.
34385 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34386 * document.body, make sure you assign an id to the body element.
34388 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34389 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34390 this.container = Roo.get(container);
34393 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34394 init : function(s){
34395 this.basic.init(s);
34398 getElementSize : function(s){
34399 return this.basic.getElementSize(s);
34402 setElementSize : function(s, newSize, onComplete){
34403 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34406 moveSplitter : function(s){
34407 var yes = Roo.bootstrap.SplitBar;
34408 switch(s.placement){
34410 s.el.setX(s.resizingEl.getRight());
34413 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34416 s.el.setY(s.resizingEl.getBottom());
34419 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34426 * Orientation constant - Create a vertical SplitBar
34430 Roo.bootstrap.SplitBar.VERTICAL = 1;
34433 * Orientation constant - Create a horizontal SplitBar
34437 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34440 * Placement constant - The resizing element is to the left of the splitter element
34444 Roo.bootstrap.SplitBar.LEFT = 1;
34447 * Placement constant - The resizing element is to the right of the splitter element
34451 Roo.bootstrap.SplitBar.RIGHT = 2;
34454 * Placement constant - The resizing element is positioned above the splitter element
34458 Roo.bootstrap.SplitBar.TOP = 3;
34461 * Placement constant - The resizing element is positioned under splitter element
34465 Roo.bootstrap.SplitBar.BOTTOM = 4;
34466 Roo.namespace("Roo.bootstrap.layout");/*
34468 * Ext JS Library 1.1.1
34469 * Copyright(c) 2006-2007, Ext JS, LLC.
34471 * Originally Released Under LGPL - original licence link has changed is not relivant.
34474 * <script type="text/javascript">
34478 * @class Roo.bootstrap.layout.Manager
34479 * @extends Roo.bootstrap.Component
34480 * Base class for layout managers.
34482 Roo.bootstrap.layout.Manager = function(config)
34484 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34490 /** false to disable window resize monitoring @type Boolean */
34491 this.monitorWindowResize = true;
34496 * Fires when a layout is performed.
34497 * @param {Roo.LayoutManager} this
34501 * @event regionresized
34502 * Fires when the user resizes a region.
34503 * @param {Roo.LayoutRegion} region The resized region
34504 * @param {Number} newSize The new size (width for east/west, height for north/south)
34506 "regionresized" : true,
34508 * @event regioncollapsed
34509 * Fires when a region is collapsed.
34510 * @param {Roo.LayoutRegion} region The collapsed region
34512 "regioncollapsed" : true,
34514 * @event regionexpanded
34515 * Fires when a region is expanded.
34516 * @param {Roo.LayoutRegion} region The expanded region
34518 "regionexpanded" : true
34520 this.updating = false;
34523 this.el = Roo.get(config.el);
34529 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34534 monitorWindowResize : true,
34540 onRender : function(ct, position)
34543 this.el = Roo.get(ct);
34546 //this.fireEvent('render',this);
34550 initEvents: function()
34554 // ie scrollbar fix
34555 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34556 document.body.scroll = "no";
34557 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34558 this.el.position('relative');
34560 this.id = this.el.id;
34561 this.el.addClass("roo-layout-container");
34562 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34563 if(this.el.dom != document.body ) {
34564 this.el.on('resize', this.layout,this);
34565 this.el.on('show', this.layout,this);
34571 * Returns true if this layout is currently being updated
34572 * @return {Boolean}
34574 isUpdating : function(){
34575 return this.updating;
34579 * Suspend the LayoutManager from doing auto-layouts while
34580 * making multiple add or remove calls
34582 beginUpdate : function(){
34583 this.updating = true;
34587 * Restore auto-layouts and optionally disable the manager from performing a layout
34588 * @param {Boolean} noLayout true to disable a layout update
34590 endUpdate : function(noLayout){
34591 this.updating = false;
34597 layout: function(){
34601 onRegionResized : function(region, newSize){
34602 this.fireEvent("regionresized", region, newSize);
34606 onRegionCollapsed : function(region){
34607 this.fireEvent("regioncollapsed", region);
34610 onRegionExpanded : function(region){
34611 this.fireEvent("regionexpanded", region);
34615 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34616 * performs box-model adjustments.
34617 * @return {Object} The size as an object {width: (the width), height: (the height)}
34619 getViewSize : function()
34622 if(this.el.dom != document.body){
34623 size = this.el.getSize();
34625 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34627 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34628 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34633 * Returns the Element this layout is bound to.
34634 * @return {Roo.Element}
34636 getEl : function(){
34641 * Returns the specified region.
34642 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34643 * @return {Roo.LayoutRegion}
34645 getRegion : function(target){
34646 return this.regions[target.toLowerCase()];
34649 onWindowResize : function(){
34650 if(this.monitorWindowResize){
34657 * Ext JS Library 1.1.1
34658 * Copyright(c) 2006-2007, Ext JS, LLC.
34660 * Originally Released Under LGPL - original licence link has changed is not relivant.
34663 * <script type="text/javascript">
34666 * @class Roo.bootstrap.layout.Border
34667 * @extends Roo.bootstrap.layout.Manager
34668 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34669 * please see: examples/bootstrap/nested.html<br><br>
34671 <b>The container the layout is rendered into can be either the body element or any other element.
34672 If it is not the body element, the container needs to either be an absolute positioned element,
34673 or you will need to add "position:relative" to the css of the container. You will also need to specify
34674 the container size if it is not the body element.</b>
34677 * Create a new Border
34678 * @param {Object} config Configuration options
34680 Roo.bootstrap.layout.Border = function(config){
34681 config = config || {};
34682 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34686 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34687 if(config[region]){
34688 config[region].region = region;
34689 this.addRegion(config[region]);
34695 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34697 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34699 * Creates and adds a new region if it doesn't already exist.
34700 * @param {String} target The target region key (north, south, east, west or center).
34701 * @param {Object} config The regions config object
34702 * @return {BorderLayoutRegion} The new region
34704 addRegion : function(config)
34706 if(!this.regions[config.region]){
34707 var r = this.factory(config);
34708 this.bindRegion(r);
34710 return this.regions[config.region];
34714 bindRegion : function(r){
34715 this.regions[r.config.region] = r;
34717 r.on("visibilitychange", this.layout, this);
34718 r.on("paneladded", this.layout, this);
34719 r.on("panelremoved", this.layout, this);
34720 r.on("invalidated", this.layout, this);
34721 r.on("resized", this.onRegionResized, this);
34722 r.on("collapsed", this.onRegionCollapsed, this);
34723 r.on("expanded", this.onRegionExpanded, this);
34727 * Performs a layout update.
34729 layout : function()
34731 if(this.updating) {
34735 // render all the rebions if they have not been done alreayd?
34736 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34737 if(this.regions[region] && !this.regions[region].bodyEl){
34738 this.regions[region].onRender(this.el)
34742 var size = this.getViewSize();
34743 var w = size.width;
34744 var h = size.height;
34749 //var x = 0, y = 0;
34751 var rs = this.regions;
34752 var north = rs["north"];
34753 var south = rs["south"];
34754 var west = rs["west"];
34755 var east = rs["east"];
34756 var center = rs["center"];
34757 //if(this.hideOnLayout){ // not supported anymore
34758 //c.el.setStyle("display", "none");
34760 if(north && north.isVisible()){
34761 var b = north.getBox();
34762 var m = north.getMargins();
34763 b.width = w - (m.left+m.right);
34766 centerY = b.height + b.y + m.bottom;
34767 centerH -= centerY;
34768 north.updateBox(this.safeBox(b));
34770 if(south && south.isVisible()){
34771 var b = south.getBox();
34772 var m = south.getMargins();
34773 b.width = w - (m.left+m.right);
34775 var totalHeight = (b.height + m.top + m.bottom);
34776 b.y = h - totalHeight + m.top;
34777 centerH -= totalHeight;
34778 south.updateBox(this.safeBox(b));
34780 if(west && west.isVisible()){
34781 var b = west.getBox();
34782 var m = west.getMargins();
34783 b.height = centerH - (m.top+m.bottom);
34785 b.y = centerY + m.top;
34786 var totalWidth = (b.width + m.left + m.right);
34787 centerX += totalWidth;
34788 centerW -= totalWidth;
34789 west.updateBox(this.safeBox(b));
34791 if(east && east.isVisible()){
34792 var b = east.getBox();
34793 var m = east.getMargins();
34794 b.height = centerH - (m.top+m.bottom);
34795 var totalWidth = (b.width + m.left + m.right);
34796 b.x = w - totalWidth + m.left;
34797 b.y = centerY + m.top;
34798 centerW -= totalWidth;
34799 east.updateBox(this.safeBox(b));
34802 var m = center.getMargins();
34804 x: centerX + m.left,
34805 y: centerY + m.top,
34806 width: centerW - (m.left+m.right),
34807 height: centerH - (m.top+m.bottom)
34809 //if(this.hideOnLayout){
34810 //center.el.setStyle("display", "block");
34812 center.updateBox(this.safeBox(centerBox));
34815 this.fireEvent("layout", this);
34819 safeBox : function(box){
34820 box.width = Math.max(0, box.width);
34821 box.height = Math.max(0, box.height);
34826 * Adds a ContentPanel (or subclass) to this layout.
34827 * @param {String} target The target region key (north, south, east, west or center).
34828 * @param {Roo.ContentPanel} panel The panel to add
34829 * @return {Roo.ContentPanel} The added panel
34831 add : function(target, panel){
34833 target = target.toLowerCase();
34834 return this.regions[target].add(panel);
34838 * Remove a ContentPanel (or subclass) to this layout.
34839 * @param {String} target The target region key (north, south, east, west or center).
34840 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34841 * @return {Roo.ContentPanel} The removed panel
34843 remove : function(target, panel){
34844 target = target.toLowerCase();
34845 return this.regions[target].remove(panel);
34849 * Searches all regions for a panel with the specified id
34850 * @param {String} panelId
34851 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34853 findPanel : function(panelId){
34854 var rs = this.regions;
34855 for(var target in rs){
34856 if(typeof rs[target] != "function"){
34857 var p = rs[target].getPanel(panelId);
34867 * Searches all regions for a panel with the specified id and activates (shows) it.
34868 * @param {String/ContentPanel} panelId The panels id or the panel itself
34869 * @return {Roo.ContentPanel} The shown panel or null
34871 showPanel : function(panelId) {
34872 var rs = this.regions;
34873 for(var target in rs){
34874 var r = rs[target];
34875 if(typeof r != "function"){
34876 if(r.hasPanel(panelId)){
34877 return r.showPanel(panelId);
34885 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34886 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34889 restoreState : function(provider){
34891 provider = Roo.state.Manager;
34893 var sm = new Roo.LayoutStateManager();
34894 sm.init(this, provider);
34900 * Adds a xtype elements to the layout.
34904 xtype : 'ContentPanel',
34911 xtype : 'NestedLayoutPanel',
34917 items : [ ... list of content panels or nested layout panels.. ]
34921 * @param {Object} cfg Xtype definition of item to add.
34923 addxtype : function(cfg)
34925 // basically accepts a pannel...
34926 // can accept a layout region..!?!?
34927 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34930 // theory? children can only be panels??
34932 //if (!cfg.xtype.match(/Panel$/)) {
34937 if (typeof(cfg.region) == 'undefined') {
34938 Roo.log("Failed to add Panel, region was not set");
34942 var region = cfg.region;
34948 xitems = cfg.items;
34955 case 'Content': // ContentPanel (el, cfg)
34956 case 'Scroll': // ContentPanel (el, cfg)
34958 cfg.autoCreate = true;
34959 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34961 // var el = this.el.createChild();
34962 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34965 this.add(region, ret);
34969 case 'TreePanel': // our new panel!
34970 cfg.el = this.el.createChild();
34971 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34972 this.add(region, ret);
34977 // create a new Layout (which is a Border Layout...
34979 var clayout = cfg.layout;
34980 clayout.el = this.el.createChild();
34981 clayout.items = clayout.items || [];
34985 // replace this exitems with the clayout ones..
34986 xitems = clayout.items;
34988 // force background off if it's in center...
34989 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34990 cfg.background = false;
34992 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34995 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34996 //console.log('adding nested layout panel ' + cfg.toSource());
34997 this.add(region, ret);
34998 nb = {}; /// find first...
35003 // needs grid and region
35005 //var el = this.getRegion(region).el.createChild();
35007 *var el = this.el.createChild();
35008 // create the grid first...
35009 cfg.grid.container = el;
35010 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35013 if (region == 'center' && this.active ) {
35014 cfg.background = false;
35017 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35019 this.add(region, ret);
35021 if (cfg.background) {
35022 // render grid on panel activation (if panel background)
35023 ret.on('activate', function(gp) {
35024 if (!gp.grid.rendered) {
35025 // gp.grid.render(el);
35029 // cfg.grid.render(el);
35035 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35036 // it was the old xcomponent building that caused this before.
35037 // espeically if border is the top element in the tree.
35047 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35049 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35050 this.add(region, ret);
35054 throw "Can not add '" + cfg.xtype + "' to Border";
35060 this.beginUpdate();
35064 Roo.each(xitems, function(i) {
35065 region = nb && i.region ? i.region : false;
35067 var add = ret.addxtype(i);
35070 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35071 if (!i.background) {
35072 abn[region] = nb[region] ;
35079 // make the last non-background panel active..
35080 //if (nb) { Roo.log(abn); }
35083 for(var r in abn) {
35084 region = this.getRegion(r);
35086 // tried using nb[r], but it does not work..
35088 region.showPanel(abn[r]);
35099 factory : function(cfg)
35102 var validRegions = Roo.bootstrap.layout.Border.regions;
35104 var target = cfg.region;
35107 var r = Roo.bootstrap.layout;
35111 return new r.North(cfg);
35113 return new r.South(cfg);
35115 return new r.East(cfg);
35117 return new r.West(cfg);
35119 return new r.Center(cfg);
35121 throw 'Layout region "'+target+'" not supported.';
35128 * Ext JS Library 1.1.1
35129 * Copyright(c) 2006-2007, Ext JS, LLC.
35131 * Originally Released Under LGPL - original licence link has changed is not relivant.
35134 * <script type="text/javascript">
35138 * @class Roo.bootstrap.layout.Basic
35139 * @extends Roo.util.Observable
35140 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35141 * and does not have a titlebar, tabs or any other features. All it does is size and position
35142 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35143 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35144 * @cfg {string} region the region that it inhabits..
35145 * @cfg {bool} skipConfig skip config?
35149 Roo.bootstrap.layout.Basic = function(config){
35151 this.mgr = config.mgr;
35153 this.position = config.region;
35155 var skipConfig = config.skipConfig;
35159 * @scope Roo.BasicLayoutRegion
35163 * @event beforeremove
35164 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35165 * @param {Roo.LayoutRegion} this
35166 * @param {Roo.ContentPanel} panel The panel
35167 * @param {Object} e The cancel event object
35169 "beforeremove" : true,
35171 * @event invalidated
35172 * Fires when the layout for this region is changed.
35173 * @param {Roo.LayoutRegion} this
35175 "invalidated" : true,
35177 * @event visibilitychange
35178 * Fires when this region is shown or hidden
35179 * @param {Roo.LayoutRegion} this
35180 * @param {Boolean} visibility true or false
35182 "visibilitychange" : true,
35184 * @event paneladded
35185 * Fires when a panel is added.
35186 * @param {Roo.LayoutRegion} this
35187 * @param {Roo.ContentPanel} panel The panel
35189 "paneladded" : true,
35191 * @event panelremoved
35192 * Fires when a panel is removed.
35193 * @param {Roo.LayoutRegion} this
35194 * @param {Roo.ContentPanel} panel The panel
35196 "panelremoved" : true,
35198 * @event beforecollapse
35199 * Fires when this region before collapse.
35200 * @param {Roo.LayoutRegion} this
35202 "beforecollapse" : true,
35205 * Fires when this region is collapsed.
35206 * @param {Roo.LayoutRegion} this
35208 "collapsed" : true,
35211 * Fires when this region is expanded.
35212 * @param {Roo.LayoutRegion} this
35217 * Fires when this region is slid into view.
35218 * @param {Roo.LayoutRegion} this
35220 "slideshow" : true,
35223 * Fires when this region slides out of view.
35224 * @param {Roo.LayoutRegion} this
35226 "slidehide" : true,
35228 * @event panelactivated
35229 * Fires when a panel is activated.
35230 * @param {Roo.LayoutRegion} this
35231 * @param {Roo.ContentPanel} panel The activated panel
35233 "panelactivated" : true,
35236 * Fires when the user resizes this region.
35237 * @param {Roo.LayoutRegion} this
35238 * @param {Number} newSize The new size (width for east/west, height for north/south)
35242 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35243 this.panels = new Roo.util.MixedCollection();
35244 this.panels.getKey = this.getPanelId.createDelegate(this);
35246 this.activePanel = null;
35247 // ensure listeners are added...
35249 if (config.listeners || config.events) {
35250 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35251 listeners : config.listeners || {},
35252 events : config.events || {}
35256 if(skipConfig !== true){
35257 this.applyConfig(config);
35261 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35263 getPanelId : function(p){
35267 applyConfig : function(config){
35268 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35269 this.config = config;
35274 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35275 * the width, for horizontal (north, south) the height.
35276 * @param {Number} newSize The new width or height
35278 resizeTo : function(newSize){
35279 var el = this.el ? this.el :
35280 (this.activePanel ? this.activePanel.getEl() : null);
35282 switch(this.position){
35285 el.setWidth(newSize);
35286 this.fireEvent("resized", this, newSize);
35290 el.setHeight(newSize);
35291 this.fireEvent("resized", this, newSize);
35297 getBox : function(){
35298 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35301 getMargins : function(){
35302 return this.margins;
35305 updateBox : function(box){
35307 var el = this.activePanel.getEl();
35308 el.dom.style.left = box.x + "px";
35309 el.dom.style.top = box.y + "px";
35310 this.activePanel.setSize(box.width, box.height);
35314 * Returns the container element for this region.
35315 * @return {Roo.Element}
35317 getEl : function(){
35318 return this.activePanel;
35322 * Returns true if this region is currently visible.
35323 * @return {Boolean}
35325 isVisible : function(){
35326 return this.activePanel ? true : false;
35329 setActivePanel : function(panel){
35330 panel = this.getPanel(panel);
35331 if(this.activePanel && this.activePanel != panel){
35332 this.activePanel.setActiveState(false);
35333 this.activePanel.getEl().setLeftTop(-10000,-10000);
35335 this.activePanel = panel;
35336 panel.setActiveState(true);
35338 panel.setSize(this.box.width, this.box.height);
35340 this.fireEvent("panelactivated", this, panel);
35341 this.fireEvent("invalidated");
35345 * Show the specified panel.
35346 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35347 * @return {Roo.ContentPanel} The shown panel or null
35349 showPanel : function(panel){
35350 panel = this.getPanel(panel);
35352 this.setActivePanel(panel);
35358 * Get the active panel for this region.
35359 * @return {Roo.ContentPanel} The active panel or null
35361 getActivePanel : function(){
35362 return this.activePanel;
35366 * Add the passed ContentPanel(s)
35367 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35368 * @return {Roo.ContentPanel} The panel added (if only one was added)
35370 add : function(panel){
35371 if(arguments.length > 1){
35372 for(var i = 0, len = arguments.length; i < len; i++) {
35373 this.add(arguments[i]);
35377 if(this.hasPanel(panel)){
35378 this.showPanel(panel);
35381 var el = panel.getEl();
35382 if(el.dom.parentNode != this.mgr.el.dom){
35383 this.mgr.el.dom.appendChild(el.dom);
35385 if(panel.setRegion){
35386 panel.setRegion(this);
35388 this.panels.add(panel);
35389 el.setStyle("position", "absolute");
35390 if(!panel.background){
35391 this.setActivePanel(panel);
35392 if(this.config.initialSize && this.panels.getCount()==1){
35393 this.resizeTo(this.config.initialSize);
35396 this.fireEvent("paneladded", this, panel);
35401 * Returns true if the panel is in this region.
35402 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35403 * @return {Boolean}
35405 hasPanel : function(panel){
35406 if(typeof panel == "object"){ // must be panel obj
35407 panel = panel.getId();
35409 return this.getPanel(panel) ? true : false;
35413 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35414 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35415 * @param {Boolean} preservePanel Overrides the config preservePanel option
35416 * @return {Roo.ContentPanel} The panel that was removed
35418 remove : function(panel, preservePanel){
35419 panel = this.getPanel(panel);
35424 this.fireEvent("beforeremove", this, panel, e);
35425 if(e.cancel === true){
35428 var panelId = panel.getId();
35429 this.panels.removeKey(panelId);
35434 * Returns the panel specified or null if it's not in this region.
35435 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35436 * @return {Roo.ContentPanel}
35438 getPanel : function(id){
35439 if(typeof id == "object"){ // must be panel obj
35442 return this.panels.get(id);
35446 * Returns this regions position (north/south/east/west/center).
35449 getPosition: function(){
35450 return this.position;
35454 * Ext JS Library 1.1.1
35455 * Copyright(c) 2006-2007, Ext JS, LLC.
35457 * Originally Released Under LGPL - original licence link has changed is not relivant.
35460 * <script type="text/javascript">
35464 * @class Roo.bootstrap.layout.Region
35465 * @extends Roo.bootstrap.layout.Basic
35466 * This class represents a region in a layout manager.
35468 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35469 * @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})
35470 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35471 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35472 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35473 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35474 * @cfg {String} title The title for the region (overrides panel titles)
35475 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35476 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35477 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35478 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35479 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35480 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35481 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35482 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35483 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35484 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35486 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35487 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35488 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35489 * @cfg {Number} width For East/West panels
35490 * @cfg {Number} height For North/South panels
35491 * @cfg {Boolean} split To show the splitter
35492 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35494 * @cfg {string} cls Extra CSS classes to add to region
35496 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35497 * @cfg {string} region the region that it inhabits..
35500 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35501 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35503 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35504 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35505 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35507 Roo.bootstrap.layout.Region = function(config)
35509 this.applyConfig(config);
35511 var mgr = config.mgr;
35512 var pos = config.region;
35513 config.skipConfig = true;
35514 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35517 this.onRender(mgr.el);
35520 this.visible = true;
35521 this.collapsed = false;
35522 this.unrendered_panels = [];
35525 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35527 position: '', // set by wrapper (eg. north/south etc..)
35528 unrendered_panels : null, // unrendered panels.
35529 createBody : function(){
35530 /** This region's body element
35531 * @type Roo.Element */
35532 this.bodyEl = this.el.createChild({
35534 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35538 onRender: function(ctr, pos)
35540 var dh = Roo.DomHelper;
35541 /** This region's container element
35542 * @type Roo.Element */
35543 this.el = dh.append(ctr.dom, {
35545 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35547 /** This region's title element
35548 * @type Roo.Element */
35550 this.titleEl = dh.append(this.el.dom,
35553 unselectable: "on",
35554 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35556 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35557 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35560 this.titleEl.enableDisplayMode();
35561 /** This region's title text element
35562 * @type HTMLElement */
35563 this.titleTextEl = this.titleEl.dom.firstChild;
35564 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35566 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35567 this.closeBtn.enableDisplayMode();
35568 this.closeBtn.on("click", this.closeClicked, this);
35569 this.closeBtn.hide();
35571 this.createBody(this.config);
35572 if(this.config.hideWhenEmpty){
35574 this.on("paneladded", this.validateVisibility, this);
35575 this.on("panelremoved", this.validateVisibility, this);
35577 if(this.autoScroll){
35578 this.bodyEl.setStyle("overflow", "auto");
35580 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35582 //if(c.titlebar !== false){
35583 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35584 this.titleEl.hide();
35586 this.titleEl.show();
35587 if(this.config.title){
35588 this.titleTextEl.innerHTML = this.config.title;
35592 if(this.config.collapsed){
35593 this.collapse(true);
35595 if(this.config.hidden){
35599 if (this.unrendered_panels && this.unrendered_panels.length) {
35600 for (var i =0;i< this.unrendered_panels.length; i++) {
35601 this.add(this.unrendered_panels[i]);
35603 this.unrendered_panels = null;
35609 applyConfig : function(c)
35612 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35613 var dh = Roo.DomHelper;
35614 if(c.titlebar !== false){
35615 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35616 this.collapseBtn.on("click", this.collapse, this);
35617 this.collapseBtn.enableDisplayMode();
35619 if(c.showPin === true || this.showPin){
35620 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35621 this.stickBtn.enableDisplayMode();
35622 this.stickBtn.on("click", this.expand, this);
35623 this.stickBtn.hide();
35628 /** This region's collapsed element
35629 * @type Roo.Element */
35632 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35633 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35636 if(c.floatable !== false){
35637 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35638 this.collapsedEl.on("click", this.collapseClick, this);
35641 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35642 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35643 id: "message", unselectable: "on", style:{"float":"left"}});
35644 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35646 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35647 this.expandBtn.on("click", this.expand, this);
35651 if(this.collapseBtn){
35652 this.collapseBtn.setVisible(c.collapsible == true);
35655 this.cmargins = c.cmargins || this.cmargins ||
35656 (this.position == "west" || this.position == "east" ?
35657 {top: 0, left: 2, right:2, bottom: 0} :
35658 {top: 2, left: 0, right:0, bottom: 2});
35660 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35663 this.bottomTabs = c.tabPosition != "top";
35665 this.autoScroll = c.autoScroll || false;
35670 this.duration = c.duration || .30;
35671 this.slideDuration = c.slideDuration || .45;
35676 * Returns true if this region is currently visible.
35677 * @return {Boolean}
35679 isVisible : function(){
35680 return this.visible;
35684 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35685 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35687 //setCollapsedTitle : function(title){
35688 // title = title || " ";
35689 // if(this.collapsedTitleTextEl){
35690 // this.collapsedTitleTextEl.innerHTML = title;
35694 getBox : function(){
35696 // if(!this.collapsed){
35697 b = this.el.getBox(false, true);
35699 // b = this.collapsedEl.getBox(false, true);
35704 getMargins : function(){
35705 return this.margins;
35706 //return this.collapsed ? this.cmargins : this.margins;
35709 highlight : function(){
35710 this.el.addClass("x-layout-panel-dragover");
35713 unhighlight : function(){
35714 this.el.removeClass("x-layout-panel-dragover");
35717 updateBox : function(box)
35719 if (!this.bodyEl) {
35720 return; // not rendered yet..
35724 if(!this.collapsed){
35725 this.el.dom.style.left = box.x + "px";
35726 this.el.dom.style.top = box.y + "px";
35727 this.updateBody(box.width, box.height);
35729 this.collapsedEl.dom.style.left = box.x + "px";
35730 this.collapsedEl.dom.style.top = box.y + "px";
35731 this.collapsedEl.setSize(box.width, box.height);
35734 this.tabs.autoSizeTabs();
35738 updateBody : function(w, h)
35741 this.el.setWidth(w);
35742 w -= this.el.getBorderWidth("rl");
35743 if(this.config.adjustments){
35744 w += this.config.adjustments[0];
35747 if(h !== null && h > 0){
35748 this.el.setHeight(h);
35749 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35750 h -= this.el.getBorderWidth("tb");
35751 if(this.config.adjustments){
35752 h += this.config.adjustments[1];
35754 this.bodyEl.setHeight(h);
35756 h = this.tabs.syncHeight(h);
35759 if(this.panelSize){
35760 w = w !== null ? w : this.panelSize.width;
35761 h = h !== null ? h : this.panelSize.height;
35763 if(this.activePanel){
35764 var el = this.activePanel.getEl();
35765 w = w !== null ? w : el.getWidth();
35766 h = h !== null ? h : el.getHeight();
35767 this.panelSize = {width: w, height: h};
35768 this.activePanel.setSize(w, h);
35770 if(Roo.isIE && this.tabs){
35771 this.tabs.el.repaint();
35776 * Returns the container element for this region.
35777 * @return {Roo.Element}
35779 getEl : function(){
35784 * Hides this region.
35787 //if(!this.collapsed){
35788 this.el.dom.style.left = "-2000px";
35791 // this.collapsedEl.dom.style.left = "-2000px";
35792 // this.collapsedEl.hide();
35794 this.visible = false;
35795 this.fireEvent("visibilitychange", this, false);
35799 * Shows this region if it was previously hidden.
35802 //if(!this.collapsed){
35805 // this.collapsedEl.show();
35807 this.visible = true;
35808 this.fireEvent("visibilitychange", this, true);
35811 closeClicked : function(){
35812 if(this.activePanel){
35813 this.remove(this.activePanel);
35817 collapseClick : function(e){
35819 e.stopPropagation();
35822 e.stopPropagation();
35828 * Collapses this region.
35829 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35832 collapse : function(skipAnim, skipCheck = false){
35833 if(this.collapsed) {
35837 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35839 this.collapsed = true;
35841 this.split.el.hide();
35843 if(this.config.animate && skipAnim !== true){
35844 this.fireEvent("invalidated", this);
35845 this.animateCollapse();
35847 this.el.setLocation(-20000,-20000);
35849 this.collapsedEl.show();
35850 this.fireEvent("collapsed", this);
35851 this.fireEvent("invalidated", this);
35857 animateCollapse : function(){
35862 * Expands this region if it was previously collapsed.
35863 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35864 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35867 expand : function(e, skipAnim){
35869 e.stopPropagation();
35871 if(!this.collapsed || this.el.hasActiveFx()) {
35875 this.afterSlideIn();
35878 this.collapsed = false;
35879 if(this.config.animate && skipAnim !== true){
35880 this.animateExpand();
35884 this.split.el.show();
35886 this.collapsedEl.setLocation(-2000,-2000);
35887 this.collapsedEl.hide();
35888 this.fireEvent("invalidated", this);
35889 this.fireEvent("expanded", this);
35893 animateExpand : function(){
35897 initTabs : function()
35899 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35901 var ts = new Roo.bootstrap.panel.Tabs({
35902 el: this.bodyEl.dom,
35903 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35904 disableTooltips: this.config.disableTabTips,
35905 toolbar : this.config.toolbar
35908 if(this.config.hideTabs){
35909 ts.stripWrap.setDisplayed(false);
35912 ts.resizeTabs = this.config.resizeTabs === true;
35913 ts.minTabWidth = this.config.minTabWidth || 40;
35914 ts.maxTabWidth = this.config.maxTabWidth || 250;
35915 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35916 ts.monitorResize = false;
35917 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35918 ts.bodyEl.addClass('roo-layout-tabs-body');
35919 this.panels.each(this.initPanelAsTab, this);
35922 initPanelAsTab : function(panel){
35923 var ti = this.tabs.addTab(
35927 this.config.closeOnTab && panel.isClosable(),
35930 if(panel.tabTip !== undefined){
35931 ti.setTooltip(panel.tabTip);
35933 ti.on("activate", function(){
35934 this.setActivePanel(panel);
35937 if(this.config.closeOnTab){
35938 ti.on("beforeclose", function(t, e){
35940 this.remove(panel);
35944 panel.tabItem = ti;
35949 updatePanelTitle : function(panel, title)
35951 if(this.activePanel == panel){
35952 this.updateTitle(title);
35955 var ti = this.tabs.getTab(panel.getEl().id);
35957 if(panel.tabTip !== undefined){
35958 ti.setTooltip(panel.tabTip);
35963 updateTitle : function(title){
35964 if(this.titleTextEl && !this.config.title){
35965 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35969 setActivePanel : function(panel)
35971 panel = this.getPanel(panel);
35972 if(this.activePanel && this.activePanel != panel){
35973 if(this.activePanel.setActiveState(false) === false){
35977 this.activePanel = panel;
35978 panel.setActiveState(true);
35979 if(this.panelSize){
35980 panel.setSize(this.panelSize.width, this.panelSize.height);
35983 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35985 this.updateTitle(panel.getTitle());
35987 this.fireEvent("invalidated", this);
35989 this.fireEvent("panelactivated", this, panel);
35993 * Shows the specified panel.
35994 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35995 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35997 showPanel : function(panel)
35999 panel = this.getPanel(panel);
36002 var tab = this.tabs.getTab(panel.getEl().id);
36003 if(tab.isHidden()){
36004 this.tabs.unhideTab(tab.id);
36008 this.setActivePanel(panel);
36015 * Get the active panel for this region.
36016 * @return {Roo.ContentPanel} The active panel or null
36018 getActivePanel : function(){
36019 return this.activePanel;
36022 validateVisibility : function(){
36023 if(this.panels.getCount() < 1){
36024 this.updateTitle(" ");
36025 this.closeBtn.hide();
36028 if(!this.isVisible()){
36035 * Adds the passed ContentPanel(s) to this region.
36036 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36037 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36039 add : function(panel)
36041 if(arguments.length > 1){
36042 for(var i = 0, len = arguments.length; i < len; i++) {
36043 this.add(arguments[i]);
36048 // if we have not been rendered yet, then we can not really do much of this..
36049 if (!this.bodyEl) {
36050 this.unrendered_panels.push(panel);
36057 if(this.hasPanel(panel)){
36058 this.showPanel(panel);
36061 panel.setRegion(this);
36062 this.panels.add(panel);
36063 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36064 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36065 // and hide them... ???
36066 this.bodyEl.dom.appendChild(panel.getEl().dom);
36067 if(panel.background !== true){
36068 this.setActivePanel(panel);
36070 this.fireEvent("paneladded", this, panel);
36077 this.initPanelAsTab(panel);
36081 if(panel.background !== true){
36082 this.tabs.activate(panel.getEl().id);
36084 this.fireEvent("paneladded", this, panel);
36089 * Hides the tab for the specified panel.
36090 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36092 hidePanel : function(panel){
36093 if(this.tabs && (panel = this.getPanel(panel))){
36094 this.tabs.hideTab(panel.getEl().id);
36099 * Unhides the tab for a previously hidden panel.
36100 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36102 unhidePanel : function(panel){
36103 if(this.tabs && (panel = this.getPanel(panel))){
36104 this.tabs.unhideTab(panel.getEl().id);
36108 clearPanels : function(){
36109 while(this.panels.getCount() > 0){
36110 this.remove(this.panels.first());
36115 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36116 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36117 * @param {Boolean} preservePanel Overrides the config preservePanel option
36118 * @return {Roo.ContentPanel} The panel that was removed
36120 remove : function(panel, preservePanel)
36122 panel = this.getPanel(panel);
36127 this.fireEvent("beforeremove", this, panel, e);
36128 if(e.cancel === true){
36131 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36132 var panelId = panel.getId();
36133 this.panels.removeKey(panelId);
36135 document.body.appendChild(panel.getEl().dom);
36138 this.tabs.removeTab(panel.getEl().id);
36139 }else if (!preservePanel){
36140 this.bodyEl.dom.removeChild(panel.getEl().dom);
36142 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36143 var p = this.panels.first();
36144 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36145 tempEl.appendChild(p.getEl().dom);
36146 this.bodyEl.update("");
36147 this.bodyEl.dom.appendChild(p.getEl().dom);
36149 this.updateTitle(p.getTitle());
36151 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36152 this.setActivePanel(p);
36154 panel.setRegion(null);
36155 if(this.activePanel == panel){
36156 this.activePanel = null;
36158 if(this.config.autoDestroy !== false && preservePanel !== true){
36159 try{panel.destroy();}catch(e){}
36161 this.fireEvent("panelremoved", this, panel);
36166 * Returns the TabPanel component used by this region
36167 * @return {Roo.TabPanel}
36169 getTabs : function(){
36173 createTool : function(parentEl, className){
36174 var btn = Roo.DomHelper.append(parentEl, {
36176 cls: "x-layout-tools-button",
36179 cls: "roo-layout-tools-button-inner " + className,
36183 btn.addClassOnOver("roo-layout-tools-button-over");
36188 * Ext JS Library 1.1.1
36189 * Copyright(c) 2006-2007, Ext JS, LLC.
36191 * Originally Released Under LGPL - original licence link has changed is not relivant.
36194 * <script type="text/javascript">
36200 * @class Roo.SplitLayoutRegion
36201 * @extends Roo.LayoutRegion
36202 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36204 Roo.bootstrap.layout.Split = function(config){
36205 this.cursor = config.cursor;
36206 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36209 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36211 splitTip : "Drag to resize.",
36212 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36213 useSplitTips : false,
36215 applyConfig : function(config){
36216 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36219 onRender : function(ctr,pos) {
36221 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36222 if(!this.config.split){
36227 var splitEl = Roo.DomHelper.append(ctr.dom, {
36229 id: this.el.id + "-split",
36230 cls: "roo-layout-split roo-layout-split-"+this.position,
36233 /** The SplitBar for this region
36234 * @type Roo.SplitBar */
36235 // does not exist yet...
36236 Roo.log([this.position, this.orientation]);
36238 this.split = new Roo.bootstrap.SplitBar({
36239 dragElement : splitEl,
36240 resizingElement: this.el,
36241 orientation : this.orientation
36244 this.split.on("moved", this.onSplitMove, this);
36245 this.split.useShim = this.config.useShim === true;
36246 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36247 if(this.useSplitTips){
36248 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36250 //if(config.collapsible){
36251 // this.split.el.on("dblclick", this.collapse, this);
36254 if(typeof this.config.minSize != "undefined"){
36255 this.split.minSize = this.config.minSize;
36257 if(typeof this.config.maxSize != "undefined"){
36258 this.split.maxSize = this.config.maxSize;
36260 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36261 this.hideSplitter();
36266 getHMaxSize : function(){
36267 var cmax = this.config.maxSize || 10000;
36268 var center = this.mgr.getRegion("center");
36269 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36272 getVMaxSize : function(){
36273 var cmax = this.config.maxSize || 10000;
36274 var center = this.mgr.getRegion("center");
36275 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36278 onSplitMove : function(split, newSize){
36279 this.fireEvent("resized", this, newSize);
36283 * Returns the {@link Roo.SplitBar} for this region.
36284 * @return {Roo.SplitBar}
36286 getSplitBar : function(){
36291 this.hideSplitter();
36292 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36295 hideSplitter : function(){
36297 this.split.el.setLocation(-2000,-2000);
36298 this.split.el.hide();
36304 this.split.el.show();
36306 Roo.bootstrap.layout.Split.superclass.show.call(this);
36309 beforeSlide: function(){
36310 if(Roo.isGecko){// firefox overflow auto bug workaround
36311 this.bodyEl.clip();
36313 this.tabs.bodyEl.clip();
36315 if(this.activePanel){
36316 this.activePanel.getEl().clip();
36318 if(this.activePanel.beforeSlide){
36319 this.activePanel.beforeSlide();
36325 afterSlide : function(){
36326 if(Roo.isGecko){// firefox overflow auto bug workaround
36327 this.bodyEl.unclip();
36329 this.tabs.bodyEl.unclip();
36331 if(this.activePanel){
36332 this.activePanel.getEl().unclip();
36333 if(this.activePanel.afterSlide){
36334 this.activePanel.afterSlide();
36340 initAutoHide : function(){
36341 if(this.autoHide !== false){
36342 if(!this.autoHideHd){
36343 var st = new Roo.util.DelayedTask(this.slideIn, this);
36344 this.autoHideHd = {
36345 "mouseout": function(e){
36346 if(!e.within(this.el, true)){
36350 "mouseover" : function(e){
36356 this.el.on(this.autoHideHd);
36360 clearAutoHide : function(){
36361 if(this.autoHide !== false){
36362 this.el.un("mouseout", this.autoHideHd.mouseout);
36363 this.el.un("mouseover", this.autoHideHd.mouseover);
36367 clearMonitor : function(){
36368 Roo.get(document).un("click", this.slideInIf, this);
36371 // these names are backwards but not changed for compat
36372 slideOut : function(){
36373 if(this.isSlid || this.el.hasActiveFx()){
36376 this.isSlid = true;
36377 if(this.collapseBtn){
36378 this.collapseBtn.hide();
36380 this.closeBtnState = this.closeBtn.getStyle('display');
36381 this.closeBtn.hide();
36383 this.stickBtn.show();
36386 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36387 this.beforeSlide();
36388 this.el.setStyle("z-index", 10001);
36389 this.el.slideIn(this.getSlideAnchor(), {
36390 callback: function(){
36392 this.initAutoHide();
36393 Roo.get(document).on("click", this.slideInIf, this);
36394 this.fireEvent("slideshow", this);
36401 afterSlideIn : function(){
36402 this.clearAutoHide();
36403 this.isSlid = false;
36404 this.clearMonitor();
36405 this.el.setStyle("z-index", "");
36406 if(this.collapseBtn){
36407 this.collapseBtn.show();
36409 this.closeBtn.setStyle('display', this.closeBtnState);
36411 this.stickBtn.hide();
36413 this.fireEvent("slidehide", this);
36416 slideIn : function(cb){
36417 if(!this.isSlid || this.el.hasActiveFx()){
36421 this.isSlid = false;
36422 this.beforeSlide();
36423 this.el.slideOut(this.getSlideAnchor(), {
36424 callback: function(){
36425 this.el.setLeftTop(-10000, -10000);
36427 this.afterSlideIn();
36435 slideInIf : function(e){
36436 if(!e.within(this.el)){
36441 animateCollapse : function(){
36442 this.beforeSlide();
36443 this.el.setStyle("z-index", 20000);
36444 var anchor = this.getSlideAnchor();
36445 this.el.slideOut(anchor, {
36446 callback : function(){
36447 this.el.setStyle("z-index", "");
36448 this.collapsedEl.slideIn(anchor, {duration:.3});
36450 this.el.setLocation(-10000,-10000);
36452 this.fireEvent("collapsed", this);
36459 animateExpand : function(){
36460 this.beforeSlide();
36461 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36462 this.el.setStyle("z-index", 20000);
36463 this.collapsedEl.hide({
36466 this.el.slideIn(this.getSlideAnchor(), {
36467 callback : function(){
36468 this.el.setStyle("z-index", "");
36471 this.split.el.show();
36473 this.fireEvent("invalidated", this);
36474 this.fireEvent("expanded", this);
36502 getAnchor : function(){
36503 return this.anchors[this.position];
36506 getCollapseAnchor : function(){
36507 return this.canchors[this.position];
36510 getSlideAnchor : function(){
36511 return this.sanchors[this.position];
36514 getAlignAdj : function(){
36515 var cm = this.cmargins;
36516 switch(this.position){
36532 getExpandAdj : function(){
36533 var c = this.collapsedEl, cm = this.cmargins;
36534 switch(this.position){
36536 return [-(cm.right+c.getWidth()+cm.left), 0];
36539 return [cm.right+c.getWidth()+cm.left, 0];
36542 return [0, -(cm.top+cm.bottom+c.getHeight())];
36545 return [0, cm.top+cm.bottom+c.getHeight()];
36551 * Ext JS Library 1.1.1
36552 * Copyright(c) 2006-2007, Ext JS, LLC.
36554 * Originally Released Under LGPL - original licence link has changed is not relivant.
36557 * <script type="text/javascript">
36560 * These classes are private internal classes
36562 Roo.bootstrap.layout.Center = function(config){
36563 config.region = "center";
36564 Roo.bootstrap.layout.Region.call(this, config);
36565 this.visible = true;
36566 this.minWidth = config.minWidth || 20;
36567 this.minHeight = config.minHeight || 20;
36570 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36572 // center panel can't be hidden
36576 // center panel can't be hidden
36579 getMinWidth: function(){
36580 return this.minWidth;
36583 getMinHeight: function(){
36584 return this.minHeight;
36597 Roo.bootstrap.layout.North = function(config)
36599 config.region = 'north';
36600 config.cursor = 'n-resize';
36602 Roo.bootstrap.layout.Split.call(this, config);
36606 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36607 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36608 this.split.el.addClass("roo-layout-split-v");
36610 var size = config.initialSize || config.height;
36611 if(typeof size != "undefined"){
36612 this.el.setHeight(size);
36615 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36617 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36621 getBox : function(){
36622 if(this.collapsed){
36623 return this.collapsedEl.getBox();
36625 var box = this.el.getBox();
36627 box.height += this.split.el.getHeight();
36632 updateBox : function(box){
36633 if(this.split && !this.collapsed){
36634 box.height -= this.split.el.getHeight();
36635 this.split.el.setLeft(box.x);
36636 this.split.el.setTop(box.y+box.height);
36637 this.split.el.setWidth(box.width);
36639 if(this.collapsed){
36640 this.updateBody(box.width, null);
36642 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36650 Roo.bootstrap.layout.South = function(config){
36651 config.region = 'south';
36652 config.cursor = 's-resize';
36653 Roo.bootstrap.layout.Split.call(this, config);
36655 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36656 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36657 this.split.el.addClass("roo-layout-split-v");
36659 var size = config.initialSize || config.height;
36660 if(typeof size != "undefined"){
36661 this.el.setHeight(size);
36665 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36666 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36667 getBox : function(){
36668 if(this.collapsed){
36669 return this.collapsedEl.getBox();
36671 var box = this.el.getBox();
36673 var sh = this.split.el.getHeight();
36680 updateBox : function(box){
36681 if(this.split && !this.collapsed){
36682 var sh = this.split.el.getHeight();
36685 this.split.el.setLeft(box.x);
36686 this.split.el.setTop(box.y-sh);
36687 this.split.el.setWidth(box.width);
36689 if(this.collapsed){
36690 this.updateBody(box.width, null);
36692 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36696 Roo.bootstrap.layout.East = function(config){
36697 config.region = "east";
36698 config.cursor = "e-resize";
36699 Roo.bootstrap.layout.Split.call(this, config);
36701 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36702 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36703 this.split.el.addClass("roo-layout-split-h");
36705 var size = config.initialSize || config.width;
36706 if(typeof size != "undefined"){
36707 this.el.setWidth(size);
36710 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36711 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36712 getBox : function(){
36713 if(this.collapsed){
36714 return this.collapsedEl.getBox();
36716 var box = this.el.getBox();
36718 var sw = this.split.el.getWidth();
36725 updateBox : function(box){
36726 if(this.split && !this.collapsed){
36727 var sw = this.split.el.getWidth();
36729 this.split.el.setLeft(box.x);
36730 this.split.el.setTop(box.y);
36731 this.split.el.setHeight(box.height);
36734 if(this.collapsed){
36735 this.updateBody(null, box.height);
36737 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36741 Roo.bootstrap.layout.West = function(config){
36742 config.region = "west";
36743 config.cursor = "w-resize";
36745 Roo.bootstrap.layout.Split.call(this, config);
36747 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36748 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36749 this.split.el.addClass("roo-layout-split-h");
36753 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36754 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36756 onRender: function(ctr, pos)
36758 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36759 var size = this.config.initialSize || this.config.width;
36760 if(typeof size != "undefined"){
36761 this.el.setWidth(size);
36765 getBox : function(){
36766 if(this.collapsed){
36767 return this.collapsedEl.getBox();
36769 var box = this.el.getBox();
36771 box.width += this.split.el.getWidth();
36776 updateBox : function(box){
36777 if(this.split && !this.collapsed){
36778 var sw = this.split.el.getWidth();
36780 this.split.el.setLeft(box.x+box.width);
36781 this.split.el.setTop(box.y);
36782 this.split.el.setHeight(box.height);
36784 if(this.collapsed){
36785 this.updateBody(null, box.height);
36787 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36790 Roo.namespace("Roo.bootstrap.panel");/*
36792 * Ext JS Library 1.1.1
36793 * Copyright(c) 2006-2007, Ext JS, LLC.
36795 * Originally Released Under LGPL - original licence link has changed is not relivant.
36798 * <script type="text/javascript">
36801 * @class Roo.ContentPanel
36802 * @extends Roo.util.Observable
36803 * A basic ContentPanel element.
36804 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36805 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36806 * @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
36807 * @cfg {Boolean} closable True if the panel can be closed/removed
36808 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36809 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36810 * @cfg {Toolbar} toolbar A toolbar for this panel
36811 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36812 * @cfg {String} title The title for this panel
36813 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36814 * @cfg {String} url Calls {@link #setUrl} with this value
36815 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36816 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36817 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36818 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36819 * @cfg {Boolean} badges render the badges
36822 * Create a new ContentPanel.
36823 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36824 * @param {String/Object} config A string to set only the title or a config object
36825 * @param {String} content (optional) Set the HTML content for this panel
36826 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36828 Roo.bootstrap.panel.Content = function( config){
36830 this.tpl = config.tpl || false;
36832 var el = config.el;
36833 var content = config.content;
36835 if(config.autoCreate){ // xtype is available if this is called from factory
36838 this.el = Roo.get(el);
36839 if(!this.el && config && config.autoCreate){
36840 if(typeof config.autoCreate == "object"){
36841 if(!config.autoCreate.id){
36842 config.autoCreate.id = config.id||el;
36844 this.el = Roo.DomHelper.append(document.body,
36845 config.autoCreate, true);
36847 var elcfg = { tag: "div",
36848 cls: "roo-layout-inactive-content",
36852 elcfg.html = config.html;
36856 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36859 this.closable = false;
36860 this.loaded = false;
36861 this.active = false;
36864 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36866 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36868 this.wrapEl = this.el; //this.el.wrap();
36870 if (config.toolbar.items) {
36871 ti = config.toolbar.items ;
36872 delete config.toolbar.items ;
36876 this.toolbar.render(this.wrapEl, 'before');
36877 for(var i =0;i < ti.length;i++) {
36878 // Roo.log(['add child', items[i]]);
36879 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36881 this.toolbar.items = nitems;
36882 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36883 delete config.toolbar;
36887 // xtype created footer. - not sure if will work as we normally have to render first..
36888 if (this.footer && !this.footer.el && this.footer.xtype) {
36889 if (!this.wrapEl) {
36890 this.wrapEl = this.el.wrap();
36893 this.footer.container = this.wrapEl.createChild();
36895 this.footer = Roo.factory(this.footer, Roo);
36900 if(typeof config == "string"){
36901 this.title = config;
36903 Roo.apply(this, config);
36907 this.resizeEl = Roo.get(this.resizeEl, true);
36909 this.resizeEl = this.el;
36911 // handle view.xtype
36919 * Fires when this panel is activated.
36920 * @param {Roo.ContentPanel} this
36924 * @event deactivate
36925 * Fires when this panel is activated.
36926 * @param {Roo.ContentPanel} this
36928 "deactivate" : true,
36932 * Fires when this panel is resized if fitToFrame is true.
36933 * @param {Roo.ContentPanel} this
36934 * @param {Number} width The width after any component adjustments
36935 * @param {Number} height The height after any component adjustments
36941 * Fires when this tab is created
36942 * @param {Roo.ContentPanel} this
36953 if(this.autoScroll){
36954 this.resizeEl.setStyle("overflow", "auto");
36956 // fix randome scrolling
36957 //this.el.on('scroll', function() {
36958 // Roo.log('fix random scolling');
36959 // this.scrollTo('top',0);
36962 content = content || this.content;
36964 this.setContent(content);
36966 if(config && config.url){
36967 this.setUrl(this.url, this.params, this.loadOnce);
36972 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36974 if (this.view && typeof(this.view.xtype) != 'undefined') {
36975 this.view.el = this.el.appendChild(document.createElement("div"));
36976 this.view = Roo.factory(this.view);
36977 this.view.render && this.view.render(false, '');
36981 this.fireEvent('render', this);
36984 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36988 setRegion : function(region){
36989 this.region = region;
36990 this.setActiveClass(region && !this.background);
36994 setActiveClass: function(state)
36997 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36998 this.el.setStyle('position','relative');
37000 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37001 this.el.setStyle('position', 'absolute');
37006 * Returns the toolbar for this Panel if one was configured.
37007 * @return {Roo.Toolbar}
37009 getToolbar : function(){
37010 return this.toolbar;
37013 setActiveState : function(active)
37015 this.active = active;
37016 this.setActiveClass(active);
37018 if(this.fireEvent("deactivate", this) === false){
37023 this.fireEvent("activate", this);
37027 * Updates this panel's element
37028 * @param {String} content The new content
37029 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37031 setContent : function(content, loadScripts){
37032 this.el.update(content, loadScripts);
37035 ignoreResize : function(w, h){
37036 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37039 this.lastSize = {width: w, height: h};
37044 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37045 * @return {Roo.UpdateManager} The UpdateManager
37047 getUpdateManager : function(){
37048 return this.el.getUpdateManager();
37051 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37052 * @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:
37055 url: "your-url.php",
37056 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37057 callback: yourFunction,
37058 scope: yourObject, //(optional scope)
37061 text: "Loading...",
37066 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37067 * 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.
37068 * @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}
37069 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37070 * @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.
37071 * @return {Roo.ContentPanel} this
37074 var um = this.el.getUpdateManager();
37075 um.update.apply(um, arguments);
37081 * 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.
37082 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37083 * @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)
37084 * @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)
37085 * @return {Roo.UpdateManager} The UpdateManager
37087 setUrl : function(url, params, loadOnce){
37088 if(this.refreshDelegate){
37089 this.removeListener("activate", this.refreshDelegate);
37091 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37092 this.on("activate", this.refreshDelegate);
37093 return this.el.getUpdateManager();
37096 _handleRefresh : function(url, params, loadOnce){
37097 if(!loadOnce || !this.loaded){
37098 var updater = this.el.getUpdateManager();
37099 updater.update(url, params, this._setLoaded.createDelegate(this));
37103 _setLoaded : function(){
37104 this.loaded = true;
37108 * Returns this panel's id
37111 getId : function(){
37116 * Returns this panel's element - used by regiosn to add.
37117 * @return {Roo.Element}
37119 getEl : function(){
37120 return this.wrapEl || this.el;
37125 adjustForComponents : function(width, height)
37127 //Roo.log('adjustForComponents ');
37128 if(this.resizeEl != this.el){
37129 width -= this.el.getFrameWidth('lr');
37130 height -= this.el.getFrameWidth('tb');
37133 var te = this.toolbar.getEl();
37134 te.setWidth(width);
37135 height -= te.getHeight();
37138 var te = this.footer.getEl();
37139 te.setWidth(width);
37140 height -= te.getHeight();
37144 if(this.adjustments){
37145 width += this.adjustments[0];
37146 height += this.adjustments[1];
37148 return {"width": width, "height": height};
37151 setSize : function(width, height){
37152 if(this.fitToFrame && !this.ignoreResize(width, height)){
37153 if(this.fitContainer && this.resizeEl != this.el){
37154 this.el.setSize(width, height);
37156 var size = this.adjustForComponents(width, height);
37157 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37158 this.fireEvent('resize', this, size.width, size.height);
37163 * Returns this panel's title
37166 getTitle : function(){
37168 if (typeof(this.title) != 'object') {
37173 for (var k in this.title) {
37174 if (!this.title.hasOwnProperty(k)) {
37178 if (k.indexOf('-') >= 0) {
37179 var s = k.split('-');
37180 for (var i = 0; i<s.length; i++) {
37181 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37184 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37191 * Set this panel's title
37192 * @param {String} title
37194 setTitle : function(title){
37195 this.title = title;
37197 this.region.updatePanelTitle(this, title);
37202 * Returns true is this panel was configured to be closable
37203 * @return {Boolean}
37205 isClosable : function(){
37206 return this.closable;
37209 beforeSlide : function(){
37211 this.resizeEl.clip();
37214 afterSlide : function(){
37216 this.resizeEl.unclip();
37220 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37221 * Will fail silently if the {@link #setUrl} method has not been called.
37222 * This does not activate the panel, just updates its content.
37224 refresh : function(){
37225 if(this.refreshDelegate){
37226 this.loaded = false;
37227 this.refreshDelegate();
37232 * Destroys this panel
37234 destroy : function(){
37235 this.el.removeAllListeners();
37236 var tempEl = document.createElement("span");
37237 tempEl.appendChild(this.el.dom);
37238 tempEl.innerHTML = "";
37244 * form - if the content panel contains a form - this is a reference to it.
37245 * @type {Roo.form.Form}
37249 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37250 * This contains a reference to it.
37256 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37266 * @param {Object} cfg Xtype definition of item to add.
37270 getChildContainer: function () {
37271 return this.getEl();
37276 var ret = new Roo.factory(cfg);
37281 if (cfg.xtype.match(/^Form$/)) {
37284 //if (this.footer) {
37285 // el = this.footer.container.insertSibling(false, 'before');
37287 el = this.el.createChild();
37290 this.form = new Roo.form.Form(cfg);
37293 if ( this.form.allItems.length) {
37294 this.form.render(el.dom);
37298 // should only have one of theses..
37299 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37300 // views.. should not be just added - used named prop 'view''
37302 cfg.el = this.el.appendChild(document.createElement("div"));
37305 var ret = new Roo.factory(cfg);
37307 ret.render && ret.render(false, ''); // render blank..
37317 * @class Roo.bootstrap.panel.Grid
37318 * @extends Roo.bootstrap.panel.Content
37320 * Create a new GridPanel.
37321 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37322 * @param {Object} config A the config object
37328 Roo.bootstrap.panel.Grid = function(config)
37332 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37333 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37335 config.el = this.wrapper;
37336 //this.el = this.wrapper;
37338 if (config.container) {
37339 // ctor'ed from a Border/panel.grid
37342 this.wrapper.setStyle("overflow", "hidden");
37343 this.wrapper.addClass('roo-grid-container');
37348 if(config.toolbar){
37349 var tool_el = this.wrapper.createChild();
37350 this.toolbar = Roo.factory(config.toolbar);
37352 if (config.toolbar.items) {
37353 ti = config.toolbar.items ;
37354 delete config.toolbar.items ;
37358 this.toolbar.render(tool_el);
37359 for(var i =0;i < ti.length;i++) {
37360 // Roo.log(['add child', items[i]]);
37361 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37363 this.toolbar.items = nitems;
37365 delete config.toolbar;
37368 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37369 config.grid.scrollBody = true;;
37370 config.grid.monitorWindowResize = false; // turn off autosizing
37371 config.grid.autoHeight = false;
37372 config.grid.autoWidth = false;
37374 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37376 if (config.background) {
37377 // render grid on panel activation (if panel background)
37378 this.on('activate', function(gp) {
37379 if (!gp.grid.rendered) {
37380 gp.grid.render(this.wrapper);
37381 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37386 this.grid.render(this.wrapper);
37387 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37390 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37391 // ??? needed ??? config.el = this.wrapper;
37396 // xtype created footer. - not sure if will work as we normally have to render first..
37397 if (this.footer && !this.footer.el && this.footer.xtype) {
37399 var ctr = this.grid.getView().getFooterPanel(true);
37400 this.footer.dataSource = this.grid.dataSource;
37401 this.footer = Roo.factory(this.footer, Roo);
37402 this.footer.render(ctr);
37412 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37413 getId : function(){
37414 return this.grid.id;
37418 * Returns the grid for this panel
37419 * @return {Roo.bootstrap.Table}
37421 getGrid : function(){
37425 setSize : function(width, height){
37426 if(!this.ignoreResize(width, height)){
37427 var grid = this.grid;
37428 var size = this.adjustForComponents(width, height);
37429 var gridel = grid.getGridEl();
37430 gridel.setSize(size.width, size.height);
37432 var thd = grid.getGridEl().select('thead',true).first();
37433 var tbd = grid.getGridEl().select('tbody', true).first();
37435 tbd.setSize(width, height - thd.getHeight());
37444 beforeSlide : function(){
37445 this.grid.getView().scroller.clip();
37448 afterSlide : function(){
37449 this.grid.getView().scroller.unclip();
37452 destroy : function(){
37453 this.grid.destroy();
37455 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37460 * @class Roo.bootstrap.panel.Nest
37461 * @extends Roo.bootstrap.panel.Content
37463 * Create a new Panel, that can contain a layout.Border.
37466 * @param {Roo.BorderLayout} layout The layout for this panel
37467 * @param {String/Object} config A string to set only the title or a config object
37469 Roo.bootstrap.panel.Nest = function(config)
37471 // construct with only one argument..
37472 /* FIXME - implement nicer consturctors
37473 if (layout.layout) {
37475 layout = config.layout;
37476 delete config.layout;
37478 if (layout.xtype && !layout.getEl) {
37479 // then layout needs constructing..
37480 layout = Roo.factory(layout, Roo);
37484 config.el = config.layout.getEl();
37486 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37488 config.layout.monitorWindowResize = false; // turn off autosizing
37489 this.layout = config.layout;
37490 this.layout.getEl().addClass("roo-layout-nested-layout");
37497 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37499 setSize : function(width, height){
37500 if(!this.ignoreResize(width, height)){
37501 var size = this.adjustForComponents(width, height);
37502 var el = this.layout.getEl();
37503 if (size.height < 1) {
37504 el.setWidth(size.width);
37506 el.setSize(size.width, size.height);
37508 var touch = el.dom.offsetWidth;
37509 this.layout.layout();
37510 // ie requires a double layout on the first pass
37511 if(Roo.isIE && !this.initialized){
37512 this.initialized = true;
37513 this.layout.layout();
37518 // activate all subpanels if not currently active..
37520 setActiveState : function(active){
37521 this.active = active;
37522 this.setActiveClass(active);
37525 this.fireEvent("deactivate", this);
37529 this.fireEvent("activate", this);
37530 // not sure if this should happen before or after..
37531 if (!this.layout) {
37532 return; // should not happen..
37535 for (var r in this.layout.regions) {
37536 reg = this.layout.getRegion(r);
37537 if (reg.getActivePanel()) {
37538 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37539 reg.setActivePanel(reg.getActivePanel());
37542 if (!reg.panels.length) {
37545 reg.showPanel(reg.getPanel(0));
37554 * Returns the nested BorderLayout for this panel
37555 * @return {Roo.BorderLayout}
37557 getLayout : function(){
37558 return this.layout;
37562 * Adds a xtype elements to the layout of the nested panel
37566 xtype : 'ContentPanel',
37573 xtype : 'NestedLayoutPanel',
37579 items : [ ... list of content panels or nested layout panels.. ]
37583 * @param {Object} cfg Xtype definition of item to add.
37585 addxtype : function(cfg) {
37586 return this.layout.addxtype(cfg);
37591 * Ext JS Library 1.1.1
37592 * Copyright(c) 2006-2007, Ext JS, LLC.
37594 * Originally Released Under LGPL - original licence link has changed is not relivant.
37597 * <script type="text/javascript">
37600 * @class Roo.TabPanel
37601 * @extends Roo.util.Observable
37602 * A lightweight tab container.
37606 // basic tabs 1, built from existing content
37607 var tabs = new Roo.TabPanel("tabs1");
37608 tabs.addTab("script", "View Script");
37609 tabs.addTab("markup", "View Markup");
37610 tabs.activate("script");
37612 // more advanced tabs, built from javascript
37613 var jtabs = new Roo.TabPanel("jtabs");
37614 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37616 // set up the UpdateManager
37617 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37618 var updater = tab2.getUpdateManager();
37619 updater.setDefaultUrl("ajax1.htm");
37620 tab2.on('activate', updater.refresh, updater, true);
37622 // Use setUrl for Ajax loading
37623 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37624 tab3.setUrl("ajax2.htm", null, true);
37627 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37630 jtabs.activate("jtabs-1");
37633 * Create a new TabPanel.
37634 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37635 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37637 Roo.bootstrap.panel.Tabs = function(config){
37639 * The container element for this TabPanel.
37640 * @type Roo.Element
37642 this.el = Roo.get(config.el);
37645 if(typeof config == "boolean"){
37646 this.tabPosition = config ? "bottom" : "top";
37648 Roo.apply(this, config);
37652 if(this.tabPosition == "bottom"){
37653 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37654 this.el.addClass("roo-tabs-bottom");
37656 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37657 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37658 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37660 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37662 if(this.tabPosition != "bottom"){
37663 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37664 * @type Roo.Element
37666 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37667 this.el.addClass("roo-tabs-top");
37671 this.bodyEl.setStyle("position", "relative");
37673 this.active = null;
37674 this.activateDelegate = this.activate.createDelegate(this);
37679 * Fires when the active tab changes
37680 * @param {Roo.TabPanel} this
37681 * @param {Roo.TabPanelItem} activePanel The new active tab
37685 * @event beforetabchange
37686 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37687 * @param {Roo.TabPanel} this
37688 * @param {Object} e Set cancel to true on this object to cancel the tab change
37689 * @param {Roo.TabPanelItem} tab The tab being changed to
37691 "beforetabchange" : true
37694 Roo.EventManager.onWindowResize(this.onResize, this);
37695 this.cpad = this.el.getPadding("lr");
37696 this.hiddenCount = 0;
37699 // toolbar on the tabbar support...
37700 if (this.toolbar) {
37701 alert("no toolbar support yet");
37702 this.toolbar = false;
37704 var tcfg = this.toolbar;
37705 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37706 this.toolbar = new Roo.Toolbar(tcfg);
37707 if (Roo.isSafari) {
37708 var tbl = tcfg.container.child('table', true);
37709 tbl.setAttribute('width', '100%');
37717 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37720 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37722 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37724 tabPosition : "top",
37726 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37728 currentTabWidth : 0,
37730 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37734 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37738 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37740 preferredTabWidth : 175,
37742 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37744 resizeTabs : false,
37746 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37748 monitorResize : true,
37750 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37755 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37756 * @param {String} id The id of the div to use <b>or create</b>
37757 * @param {String} text The text for the tab
37758 * @param {String} content (optional) Content to put in the TabPanelItem body
37759 * @param {Boolean} closable (optional) True to create a close icon on the tab
37760 * @return {Roo.TabPanelItem} The created TabPanelItem
37762 addTab : function(id, text, content, closable, tpl)
37764 var item = new Roo.bootstrap.panel.TabItem({
37768 closable : closable,
37771 this.addTabItem(item);
37773 item.setContent(content);
37779 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37780 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37781 * @return {Roo.TabPanelItem}
37783 getTab : function(id){
37784 return this.items[id];
37788 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37789 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37791 hideTab : function(id){
37792 var t = this.items[id];
37795 this.hiddenCount++;
37796 this.autoSizeTabs();
37801 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37802 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37804 unhideTab : function(id){
37805 var t = this.items[id];
37807 t.setHidden(false);
37808 this.hiddenCount--;
37809 this.autoSizeTabs();
37814 * Adds an existing {@link Roo.TabPanelItem}.
37815 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37817 addTabItem : function(item){
37818 this.items[item.id] = item;
37819 this.items.push(item);
37820 // if(this.resizeTabs){
37821 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37822 // this.autoSizeTabs();
37824 // item.autoSize();
37829 * Removes a {@link Roo.TabPanelItem}.
37830 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37832 removeTab : function(id){
37833 var items = this.items;
37834 var tab = items[id];
37835 if(!tab) { return; }
37836 var index = items.indexOf(tab);
37837 if(this.active == tab && items.length > 1){
37838 var newTab = this.getNextAvailable(index);
37843 this.stripEl.dom.removeChild(tab.pnode.dom);
37844 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37845 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37847 items.splice(index, 1);
37848 delete this.items[tab.id];
37849 tab.fireEvent("close", tab);
37850 tab.purgeListeners();
37851 this.autoSizeTabs();
37854 getNextAvailable : function(start){
37855 var items = this.items;
37857 // look for a next tab that will slide over to
37858 // replace the one being removed
37859 while(index < items.length){
37860 var item = items[++index];
37861 if(item && !item.isHidden()){
37865 // if one isn't found select the previous tab (on the left)
37868 var item = items[--index];
37869 if(item && !item.isHidden()){
37877 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37878 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37880 disableTab : function(id){
37881 var tab = this.items[id];
37882 if(tab && this.active != tab){
37888 * Enables a {@link Roo.TabPanelItem} that is disabled.
37889 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37891 enableTab : function(id){
37892 var tab = this.items[id];
37897 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37898 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37899 * @return {Roo.TabPanelItem} The TabPanelItem.
37901 activate : function(id){
37902 var tab = this.items[id];
37906 if(tab == this.active || tab.disabled){
37910 this.fireEvent("beforetabchange", this, e, tab);
37911 if(e.cancel !== true && !tab.disabled){
37913 this.active.hide();
37915 this.active = this.items[id];
37916 this.active.show();
37917 this.fireEvent("tabchange", this, this.active);
37923 * Gets the active {@link Roo.TabPanelItem}.
37924 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37926 getActiveTab : function(){
37927 return this.active;
37931 * Updates the tab body element to fit the height of the container element
37932 * for overflow scrolling
37933 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37935 syncHeight : function(targetHeight){
37936 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37937 var bm = this.bodyEl.getMargins();
37938 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37939 this.bodyEl.setHeight(newHeight);
37943 onResize : function(){
37944 if(this.monitorResize){
37945 this.autoSizeTabs();
37950 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37952 beginUpdate : function(){
37953 this.updating = true;
37957 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37959 endUpdate : function(){
37960 this.updating = false;
37961 this.autoSizeTabs();
37965 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37967 autoSizeTabs : function(){
37968 var count = this.items.length;
37969 var vcount = count - this.hiddenCount;
37970 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37973 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37974 var availWidth = Math.floor(w / vcount);
37975 var b = this.stripBody;
37976 if(b.getWidth() > w){
37977 var tabs = this.items;
37978 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37979 if(availWidth < this.minTabWidth){
37980 /*if(!this.sleft){ // incomplete scrolling code
37981 this.createScrollButtons();
37984 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37987 if(this.currentTabWidth < this.preferredTabWidth){
37988 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37994 * Returns the number of tabs in this TabPanel.
37997 getCount : function(){
37998 return this.items.length;
38002 * Resizes all the tabs to the passed width
38003 * @param {Number} The new width
38005 setTabWidth : function(width){
38006 this.currentTabWidth = width;
38007 for(var i = 0, len = this.items.length; i < len; i++) {
38008 if(!this.items[i].isHidden()) {
38009 this.items[i].setWidth(width);
38015 * Destroys this TabPanel
38016 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38018 destroy : function(removeEl){
38019 Roo.EventManager.removeResizeListener(this.onResize, this);
38020 for(var i = 0, len = this.items.length; i < len; i++){
38021 this.items[i].purgeListeners();
38023 if(removeEl === true){
38024 this.el.update("");
38029 createStrip : function(container)
38031 var strip = document.createElement("nav");
38032 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38033 container.appendChild(strip);
38037 createStripList : function(strip)
38039 // div wrapper for retard IE
38040 // returns the "tr" element.
38041 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38042 //'<div class="x-tabs-strip-wrap">'+
38043 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38044 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38045 return strip.firstChild; //.firstChild.firstChild.firstChild;
38047 createBody : function(container)
38049 var body = document.createElement("div");
38050 Roo.id(body, "tab-body");
38051 //Roo.fly(body).addClass("x-tabs-body");
38052 Roo.fly(body).addClass("tab-content");
38053 container.appendChild(body);
38056 createItemBody :function(bodyEl, id){
38057 var body = Roo.getDom(id);
38059 body = document.createElement("div");
38062 //Roo.fly(body).addClass("x-tabs-item-body");
38063 Roo.fly(body).addClass("tab-pane");
38064 bodyEl.insertBefore(body, bodyEl.firstChild);
38068 createStripElements : function(stripEl, text, closable, tpl)
38070 var td = document.createElement("li"); // was td..
38073 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38076 stripEl.appendChild(td);
38078 td.className = "x-tabs-closable";
38079 if(!this.closeTpl){
38080 this.closeTpl = new Roo.Template(
38081 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38082 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38083 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38086 var el = this.closeTpl.overwrite(td, {"text": text});
38087 var close = el.getElementsByTagName("div")[0];
38088 var inner = el.getElementsByTagName("em")[0];
38089 return {"el": el, "close": close, "inner": inner};
38092 // not sure what this is..
38093 // if(!this.tabTpl){
38094 //this.tabTpl = new Roo.Template(
38095 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38096 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38098 // this.tabTpl = new Roo.Template(
38099 // '<a href="#">' +
38100 // '<span unselectable="on"' +
38101 // (this.disableTooltips ? '' : ' title="{text}"') +
38102 // ' >{text}</span></a>'
38108 var template = tpl || this.tabTpl || false;
38112 template = new Roo.Template(
38114 '<span unselectable="on"' +
38115 (this.disableTooltips ? '' : ' title="{text}"') +
38116 ' >{text}</span></a>'
38120 switch (typeof(template)) {
38124 template = new Roo.Template(template);
38130 var el = template.overwrite(td, {"text": text});
38132 var inner = el.getElementsByTagName("span")[0];
38134 return {"el": el, "inner": inner};
38142 * @class Roo.TabPanelItem
38143 * @extends Roo.util.Observable
38144 * Represents an individual item (tab plus body) in a TabPanel.
38145 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38146 * @param {String} id The id of this TabPanelItem
38147 * @param {String} text The text for the tab of this TabPanelItem
38148 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38150 Roo.bootstrap.panel.TabItem = function(config){
38152 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38153 * @type Roo.TabPanel
38155 this.tabPanel = config.panel;
38157 * The id for this TabPanelItem
38160 this.id = config.id;
38162 this.disabled = false;
38164 this.text = config.text;
38166 this.loaded = false;
38167 this.closable = config.closable;
38170 * The body element for this TabPanelItem.
38171 * @type Roo.Element
38173 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38174 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38175 this.bodyEl.setStyle("display", "block");
38176 this.bodyEl.setStyle("zoom", "1");
38177 //this.hideAction();
38179 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38181 this.el = Roo.get(els.el);
38182 this.inner = Roo.get(els.inner, true);
38183 this.textEl = Roo.get(this.el.dom.firstChild, true);
38184 this.pnode = Roo.get(els.el.parentNode, true);
38185 // this.el.on("mousedown", this.onTabMouseDown, this);
38186 this.el.on("click", this.onTabClick, this);
38188 if(config.closable){
38189 var c = Roo.get(els.close, true);
38190 c.dom.title = this.closeText;
38191 c.addClassOnOver("close-over");
38192 c.on("click", this.closeClick, this);
38198 * Fires when this tab becomes the active tab.
38199 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38200 * @param {Roo.TabPanelItem} this
38204 * @event beforeclose
38205 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38206 * @param {Roo.TabPanelItem} this
38207 * @param {Object} e Set cancel to true on this object to cancel the close.
38209 "beforeclose": true,
38212 * Fires when this tab is closed.
38213 * @param {Roo.TabPanelItem} this
38217 * @event deactivate
38218 * Fires when this tab is no longer the active tab.
38219 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38220 * @param {Roo.TabPanelItem} this
38222 "deactivate" : true
38224 this.hidden = false;
38226 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38229 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38231 purgeListeners : function(){
38232 Roo.util.Observable.prototype.purgeListeners.call(this);
38233 this.el.removeAllListeners();
38236 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38239 this.pnode.addClass("active");
38242 this.tabPanel.stripWrap.repaint();
38244 this.fireEvent("activate", this.tabPanel, this);
38248 * Returns true if this tab is the active tab.
38249 * @return {Boolean}
38251 isActive : function(){
38252 return this.tabPanel.getActiveTab() == this;
38256 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38259 this.pnode.removeClass("active");
38261 this.fireEvent("deactivate", this.tabPanel, this);
38264 hideAction : function(){
38265 this.bodyEl.hide();
38266 this.bodyEl.setStyle("position", "absolute");
38267 this.bodyEl.setLeft("-20000px");
38268 this.bodyEl.setTop("-20000px");
38271 showAction : function(){
38272 this.bodyEl.setStyle("position", "relative");
38273 this.bodyEl.setTop("");
38274 this.bodyEl.setLeft("");
38275 this.bodyEl.show();
38279 * Set the tooltip for the tab.
38280 * @param {String} tooltip The tab's tooltip
38282 setTooltip : function(text){
38283 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38284 this.textEl.dom.qtip = text;
38285 this.textEl.dom.removeAttribute('title');
38287 this.textEl.dom.title = text;
38291 onTabClick : function(e){
38292 e.preventDefault();
38293 this.tabPanel.activate(this.id);
38296 onTabMouseDown : function(e){
38297 e.preventDefault();
38298 this.tabPanel.activate(this.id);
38301 getWidth : function(){
38302 return this.inner.getWidth();
38305 setWidth : function(width){
38306 var iwidth = width - this.pnode.getPadding("lr");
38307 this.inner.setWidth(iwidth);
38308 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38309 this.pnode.setWidth(width);
38313 * Show or hide the tab
38314 * @param {Boolean} hidden True to hide or false to show.
38316 setHidden : function(hidden){
38317 this.hidden = hidden;
38318 this.pnode.setStyle("display", hidden ? "none" : "");
38322 * Returns true if this tab is "hidden"
38323 * @return {Boolean}
38325 isHidden : function(){
38326 return this.hidden;
38330 * Returns the text for this tab
38333 getText : function(){
38337 autoSize : function(){
38338 //this.el.beginMeasure();
38339 this.textEl.setWidth(1);
38341 * #2804 [new] Tabs in Roojs
38342 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38344 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38345 //this.el.endMeasure();
38349 * Sets the text for the tab (Note: this also sets the tooltip text)
38350 * @param {String} text The tab's text and tooltip
38352 setText : function(text){
38354 this.textEl.update(text);
38355 this.setTooltip(text);
38356 //if(!this.tabPanel.resizeTabs){
38357 // this.autoSize();
38361 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38363 activate : function(){
38364 this.tabPanel.activate(this.id);
38368 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38370 disable : function(){
38371 if(this.tabPanel.active != this){
38372 this.disabled = true;
38373 this.pnode.addClass("disabled");
38378 * Enables this TabPanelItem if it was previously disabled.
38380 enable : function(){
38381 this.disabled = false;
38382 this.pnode.removeClass("disabled");
38386 * Sets the content for this TabPanelItem.
38387 * @param {String} content The content
38388 * @param {Boolean} loadScripts true to look for and load scripts
38390 setContent : function(content, loadScripts){
38391 this.bodyEl.update(content, loadScripts);
38395 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38396 * @return {Roo.UpdateManager} The UpdateManager
38398 getUpdateManager : function(){
38399 return this.bodyEl.getUpdateManager();
38403 * Set a URL to be used to load the content for this TabPanelItem.
38404 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38405 * @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)
38406 * @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)
38407 * @return {Roo.UpdateManager} The UpdateManager
38409 setUrl : function(url, params, loadOnce){
38410 if(this.refreshDelegate){
38411 this.un('activate', this.refreshDelegate);
38413 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38414 this.on("activate", this.refreshDelegate);
38415 return this.bodyEl.getUpdateManager();
38419 _handleRefresh : function(url, params, loadOnce){
38420 if(!loadOnce || !this.loaded){
38421 var updater = this.bodyEl.getUpdateManager();
38422 updater.update(url, params, this._setLoaded.createDelegate(this));
38427 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38428 * Will fail silently if the setUrl method has not been called.
38429 * This does not activate the panel, just updates its content.
38431 refresh : function(){
38432 if(this.refreshDelegate){
38433 this.loaded = false;
38434 this.refreshDelegate();
38439 _setLoaded : function(){
38440 this.loaded = true;
38444 closeClick : function(e){
38447 this.fireEvent("beforeclose", this, o);
38448 if(o.cancel !== true){
38449 this.tabPanel.removeTab(this.id);
38453 * The text displayed in the tooltip for the close icon.
38456 closeText : "Close this tab"
38459 * This script refer to:
38460 * Title: International Telephone Input
38461 * Author: Jack O'Connor
38462 * Code version: v12.1.12
38463 * Availability: https://github.com/jackocnr/intl-tel-input.git
38466 Roo.bootstrap.PhoneInputData = function() {
38469 "Afghanistan (افغانستان)",
38474 "Albania (Shqipëri)",
38479 "Algeria (الجزائر)",
38504 "Antigua and Barbuda",
38514 "Armenia (Հայաստան)",
38530 "Austria (Österreich)",
38535 "Azerbaijan (Azərbaycan)",
38545 "Bahrain (البحرين)",
38550 "Bangladesh (বাংলাদেশ)",
38560 "Belarus (Беларусь)",
38565 "Belgium (België)",
38595 "Bosnia and Herzegovina (Босна и Херцеговина)",
38610 "British Indian Ocean Territory",
38615 "British Virgin Islands",
38625 "Bulgaria (България)",
38635 "Burundi (Uburundi)",
38640 "Cambodia (កម្ពុជា)",
38645 "Cameroon (Cameroun)",
38654 ["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"]
38657 "Cape Verde (Kabu Verdi)",
38662 "Caribbean Netherlands",
38673 "Central African Republic (République centrafricaine)",
38693 "Christmas Island",
38699 "Cocos (Keeling) Islands",
38710 "Comoros (جزر القمر)",
38715 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38720 "Congo (Republic) (Congo-Brazzaville)",
38740 "Croatia (Hrvatska)",
38761 "Czech Republic (Česká republika)",
38766 "Denmark (Danmark)",
38781 "Dominican Republic (República Dominicana)",
38785 ["809", "829", "849"]
38803 "Equatorial Guinea (Guinea Ecuatorial)",
38823 "Falkland Islands (Islas Malvinas)",
38828 "Faroe Islands (Føroyar)",
38849 "French Guiana (Guyane française)",
38854 "French Polynesia (Polynésie française)",
38869 "Georgia (საქართველო)",
38874 "Germany (Deutschland)",
38894 "Greenland (Kalaallit Nunaat)",
38931 "Guinea-Bissau (Guiné Bissau)",
38956 "Hungary (Magyarország)",
38961 "Iceland (Ísland)",
38981 "Iraq (العراق)",
38997 "Israel (ישראל)",
39024 "Jordan (الأردن)",
39029 "Kazakhstan (Казахстан)",
39050 "Kuwait (الكويت)",
39055 "Kyrgyzstan (Кыргызстан)",
39065 "Latvia (Latvija)",
39070 "Lebanon (لبنان)",
39085 "Libya (ليبيا)",
39095 "Lithuania (Lietuva)",
39110 "Macedonia (FYROM) (Македонија)",
39115 "Madagascar (Madagasikara)",
39145 "Marshall Islands",
39155 "Mauritania (موريتانيا)",
39160 "Mauritius (Moris)",
39181 "Moldova (Republica Moldova)",
39191 "Mongolia (Монгол)",
39196 "Montenegro (Crna Gora)",
39206 "Morocco (المغرب)",
39212 "Mozambique (Moçambique)",
39217 "Myanmar (Burma) (မြန်မာ)",
39222 "Namibia (Namibië)",
39237 "Netherlands (Nederland)",
39242 "New Caledonia (Nouvelle-Calédonie)",
39277 "North Korea (조선 민주주의 인민 공화국)",
39282 "Northern Mariana Islands",
39298 "Pakistan (پاکستان)",
39308 "Palestine (فلسطين)",
39318 "Papua New Guinea",
39360 "Réunion (La Réunion)",
39366 "Romania (România)",
39382 "Saint Barthélemy",
39393 "Saint Kitts and Nevis",
39403 "Saint Martin (Saint-Martin (partie française))",
39409 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39414 "Saint Vincent and the Grenadines",
39429 "São Tomé and Príncipe (São Tomé e Príncipe)",
39434 "Saudi Arabia (المملكة العربية السعودية)",
39439 "Senegal (Sénégal)",
39469 "Slovakia (Slovensko)",
39474 "Slovenia (Slovenija)",
39484 "Somalia (Soomaaliya)",
39494 "South Korea (대한민국)",
39499 "South Sudan (جنوب السودان)",
39509 "Sri Lanka (ශ්රී ලංකාව)",
39514 "Sudan (السودان)",
39524 "Svalbard and Jan Mayen",
39535 "Sweden (Sverige)",
39540 "Switzerland (Schweiz)",
39545 "Syria (سوريا)",
39590 "Trinidad and Tobago",
39595 "Tunisia (تونس)",
39600 "Turkey (Türkiye)",
39610 "Turks and Caicos Islands",
39620 "U.S. Virgin Islands",
39630 "Ukraine (Україна)",
39635 "United Arab Emirates (الإمارات العربية المتحدة)",
39657 "Uzbekistan (Oʻzbekiston)",
39667 "Vatican City (Città del Vaticano)",
39678 "Vietnam (Việt Nam)",
39683 "Wallis and Futuna (Wallis-et-Futuna)",
39688 "Western Sahara (الصحراء الغربية)",
39694 "Yemen (اليمن)",
39718 * This script refer to:
39719 * Title: International Telephone Input
39720 * Author: Jack O'Connor
39721 * Code version: v12.1.12
39722 * Availability: https://github.com/jackocnr/intl-tel-input.git
39726 * @class Roo.bootstrap.PhoneInput
39727 * @extends Roo.bootstrap.TriggerField
39728 * An input with International dial-code selection
39730 * @cfg {String} defaultDialCode default '+852'
39731 * @cfg {Array} preferedCountries default []
39734 * Create a new PhoneInput.
39735 * @param {Object} config Configuration options
39738 Roo.bootstrap.PhoneInput = function(config) {
39739 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39742 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39744 listWidth: undefined,
39746 selectedClass: 'active',
39748 invalidClass : "has-warning",
39750 validClass: 'has-success',
39752 allowed: '0123456789',
39755 * @cfg {String} defaultDialCode The default dial code when initializing the input
39757 defaultDialCode: '+852',
39760 * @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
39762 preferedCountries: false,
39764 getAutoCreate : function()
39766 var data = Roo.bootstrap.PhoneInputData();
39767 var align = this.labelAlign || this.parentLabelAlign();
39770 this.allCountries = [];
39771 this.dialCodeMapping = [];
39773 for (var i = 0; i < data.length; i++) {
39775 this.allCountries[i] = {
39779 priority: c[3] || 0,
39780 areaCodes: c[4] || null
39782 this.dialCodeMapping[c[2]] = {
39785 priority: c[3] || 0,
39786 areaCodes: c[4] || null
39798 cls : 'form-control tel-input',
39799 autocomplete: 'new-password'
39802 var hiddenInput = {
39805 cls: 'hidden-tel-input'
39809 hiddenInput.name = this.name;
39812 if (this.disabled) {
39813 input.disabled = true;
39816 var flag_container = {
39833 cls: this.hasFeedback ? 'has-feedback' : '',
39839 cls: 'dial-code-holder',
39846 cls: 'roo-select2-container input-group',
39853 if (this.fieldLabel.length) {
39856 tooltip: 'This field is required'
39862 cls: 'control-label',
39868 html: this.fieldLabel
39871 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39877 if(this.indicatorpos == 'right') {
39878 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39885 if(align == 'left') {
39893 if(this.labelWidth > 12){
39894 label.style = "width: " + this.labelWidth + 'px';
39896 if(this.labelWidth < 13 && this.labelmd == 0){
39897 this.labelmd = this.labelWidth;
39899 if(this.labellg > 0){
39900 label.cls += ' col-lg-' + this.labellg;
39901 input.cls += ' col-lg-' + (12 - this.labellg);
39903 if(this.labelmd > 0){
39904 label.cls += ' col-md-' + this.labelmd;
39905 container.cls += ' col-md-' + (12 - this.labelmd);
39907 if(this.labelsm > 0){
39908 label.cls += ' col-sm-' + this.labelsm;
39909 container.cls += ' col-sm-' + (12 - this.labelsm);
39911 if(this.labelxs > 0){
39912 label.cls += ' col-xs-' + this.labelxs;
39913 container.cls += ' col-xs-' + (12 - this.labelxs);
39923 var settings = this;
39925 ['xs','sm','md','lg'].map(function(size){
39926 if (settings[size]) {
39927 cfg.cls += ' col-' + size + '-' + settings[size];
39931 this.store = new Roo.data.Store({
39932 proxy : new Roo.data.MemoryProxy({}),
39933 reader : new Roo.data.JsonReader({
39944 'name' : 'dialCode',
39948 'name' : 'priority',
39952 'name' : 'areaCodes',
39959 if(!this.preferedCountries) {
39960 this.preferedCountries = [
39967 var p = this.preferedCountries.reverse();
39970 for (var i = 0; i < p.length; i++) {
39971 for (var j = 0; j < this.allCountries.length; j++) {
39972 if(this.allCountries[j].iso2 == p[i]) {
39973 var t = this.allCountries[j];
39974 this.allCountries.splice(j,1);
39975 this.allCountries.unshift(t);
39981 this.store.proxy.data = {
39983 data: this.allCountries
39989 initEvents : function()
39992 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39994 this.indicator = this.indicatorEl();
39995 this.flag = this.flagEl();
39996 this.dialCodeHolder = this.dialCodeHolderEl();
39998 this.trigger = this.el.select('div.flag-box',true).first();
39999 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40004 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40005 _this.list.setWidth(lw);
40008 this.list.on('mouseover', this.onViewOver, this);
40009 this.list.on('mousemove', this.onViewMove, this);
40010 this.inputEl().on("keyup", this.onKeyUp, this);
40012 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40014 this.view = new Roo.View(this.list, this.tpl, {
40015 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40018 this.view.on('click', this.onViewClick, this);
40019 this.setValue(this.defaultDialCode);
40022 onTriggerClick : function(e)
40024 Roo.log('trigger click');
40029 if(this.isExpanded()){
40031 this.hasFocus = false;
40033 this.store.load({});
40034 this.hasFocus = true;
40039 isExpanded : function()
40041 return this.list.isVisible();
40044 collapse : function()
40046 if(!this.isExpanded()){
40050 Roo.get(document).un('mousedown', this.collapseIf, this);
40051 Roo.get(document).un('mousewheel', this.collapseIf, this);
40052 this.fireEvent('collapse', this);
40056 expand : function()
40060 if(this.isExpanded() || !this.hasFocus){
40064 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40065 this.list.setWidth(lw);
40068 this.restrictHeight();
40070 Roo.get(document).on('mousedown', this.collapseIf, this);
40071 Roo.get(document).on('mousewheel', this.collapseIf, this);
40073 this.fireEvent('expand', this);
40076 restrictHeight : function()
40078 this.list.alignTo(this.inputEl(), this.listAlign);
40079 this.list.alignTo(this.inputEl(), this.listAlign);
40082 onViewOver : function(e, t)
40084 if(this.inKeyMode){
40087 var item = this.view.findItemFromChild(t);
40090 var index = this.view.indexOf(item);
40091 this.select(index, false);
40096 onViewClick : function(view, doFocus, el, e)
40098 var index = this.view.getSelectedIndexes()[0];
40100 var r = this.store.getAt(index);
40103 this.onSelect(r, index);
40105 if(doFocus !== false && !this.blockFocus){
40106 this.inputEl().focus();
40110 onViewMove : function(e, t)
40112 this.inKeyMode = false;
40115 select : function(index, scrollIntoView)
40117 this.selectedIndex = index;
40118 this.view.select(index);
40119 if(scrollIntoView !== false){
40120 var el = this.view.getNode(index);
40122 this.list.scrollChildIntoView(el, false);
40127 createList : function()
40129 this.list = Roo.get(document.body).createChild({
40131 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40132 style: 'display:none'
40135 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40138 collapseIf : function(e)
40140 var in_combo = e.within(this.el);
40141 var in_list = e.within(this.list);
40142 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40144 if (in_combo || in_list || is_list) {
40150 onSelect : function(record, index)
40152 if(this.fireEvent('beforeselect', this, record, index) !== false){
40154 this.setFlagClass(record.data.iso2);
40155 this.setDialCode(record.data.dialCode);
40156 this.hasFocus = false;
40158 this.fireEvent('select', this, record, index);
40162 flagEl : function()
40164 var flag = this.el.select('div.flag',true).first();
40171 dialCodeHolderEl : function()
40173 var d = this.el.select('input.dial-code-holder',true).first();
40180 setDialCode : function(v)
40182 this.dialCodeHolder.dom.value = '+'+v;
40185 setFlagClass : function(n)
40187 this.flag.dom.className = 'flag '+n;
40190 getValue : function()
40192 var v = this.inputEl().getValue();
40193 if(this.dialCodeHolder) {
40194 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40199 setValue : function(v)
40201 var d = this.getDialCode(v);
40203 //invalid dial code
40204 if(v.length == 0 || !d || d.length == 0) {
40206 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40207 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40213 this.setFlagClass(this.dialCodeMapping[d].iso2);
40214 this.setDialCode(d);
40215 this.inputEl().dom.value = v.replace('+'+d,'');
40216 this.hiddenEl().dom.value = this.getValue();
40221 getDialCode : function(v)
40225 if (v.length == 0) {
40226 return this.dialCodeHolder.dom.value;
40230 if (v.charAt(0) != "+") {
40233 var numericChars = "";
40234 for (var i = 1; i < v.length; i++) {
40235 var c = v.charAt(i);
40238 if (this.dialCodeMapping[numericChars]) {
40239 dialCode = v.substr(1, i);
40241 if (numericChars.length == 4) {
40251 this.setValue(this.defaultDialCode);
40255 hiddenEl : function()
40257 return this.el.select('input.hidden-tel-input',true).first();
40260 onKeyUp : function(e){
40262 var k = e.getKey();
40263 var c = e.getCharCode();
40266 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40267 this.allowed.indexOf(String.fromCharCode(c)) === -1
40272 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40275 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40279 this.setValue(this.getValue());
40284 * @class Roo.bootstrap.MoneyField
40285 * @extends Roo.bootstrap.ComboBox
40286 * Bootstrap MoneyField class
40289 * Create a new MoneyField.
40290 * @param {Object} config Configuration options
40293 Roo.bootstrap.MoneyField = function(config) {
40295 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40299 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40302 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40304 allowDecimals : true,
40306 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40308 decimalSeparator : ".",
40310 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40312 decimalPrecision : 0,
40314 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40316 allowNegative : true,
40318 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40322 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40324 minValue : Number.NEGATIVE_INFINITY,
40326 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40328 maxValue : Number.MAX_VALUE,
40330 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40332 minText : "The minimum value for this field is {0}",
40334 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40336 maxText : "The maximum value for this field is {0}",
40338 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40339 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40341 nanText : "{0} is not a valid number",
40343 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40347 * @cfg {String} defaults currency of the MoneyField
40348 * value should be in lkey
40350 defaultCurrency : false,
40352 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40354 thousandsDelimiter : false,
40364 getAutoCreate : function()
40366 var align = this.labelAlign || this.parentLabelAlign();
40378 cls : 'form-control roo-money-amount-input',
40379 autocomplete: 'new-password'
40382 var hiddenInput = {
40386 cls: 'hidden-number-input'
40390 hiddenInput.name = this.name;
40393 if (this.disabled) {
40394 input.disabled = true;
40397 var clg = 12 - this.inputlg;
40398 var cmd = 12 - this.inputmd;
40399 var csm = 12 - this.inputsm;
40400 var cxs = 12 - this.inputxs;
40404 cls : 'row roo-money-field',
40408 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40412 cls: 'roo-select2-container input-group',
40416 cls : 'form-control roo-money-currency-input',
40417 autocomplete: 'new-password',
40419 name : this.currencyName
40423 cls : 'input-group-addon',
40437 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40441 cls: this.hasFeedback ? 'has-feedback' : '',
40452 if (this.fieldLabel.length) {
40455 tooltip: 'This field is required'
40461 cls: 'control-label',
40467 html: this.fieldLabel
40470 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40476 if(this.indicatorpos == 'right') {
40477 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40484 if(align == 'left') {
40492 if(this.labelWidth > 12){
40493 label.style = "width: " + this.labelWidth + 'px';
40495 if(this.labelWidth < 13 && this.labelmd == 0){
40496 this.labelmd = this.labelWidth;
40498 if(this.labellg > 0){
40499 label.cls += ' col-lg-' + this.labellg;
40500 input.cls += ' col-lg-' + (12 - this.labellg);
40502 if(this.labelmd > 0){
40503 label.cls += ' col-md-' + this.labelmd;
40504 container.cls += ' col-md-' + (12 - this.labelmd);
40506 if(this.labelsm > 0){
40507 label.cls += ' col-sm-' + this.labelsm;
40508 container.cls += ' col-sm-' + (12 - this.labelsm);
40510 if(this.labelxs > 0){
40511 label.cls += ' col-xs-' + this.labelxs;
40512 container.cls += ' col-xs-' + (12 - this.labelxs);
40523 var settings = this;
40525 ['xs','sm','md','lg'].map(function(size){
40526 if (settings[size]) {
40527 cfg.cls += ' col-' + size + '-' + settings[size];
40534 initEvents : function()
40536 this.indicator = this.indicatorEl();
40538 this.initCurrencyEvent();
40540 this.initNumberEvent();
40543 initCurrencyEvent : function()
40546 throw "can not find store for combo";
40549 this.store = Roo.factory(this.store, Roo.data);
40550 this.store.parent = this;
40554 this.triggerEl = this.el.select('.input-group-addon', true).first();
40556 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40561 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40562 _this.list.setWidth(lw);
40565 this.list.on('mouseover', this.onViewOver, this);
40566 this.list.on('mousemove', this.onViewMove, this);
40567 this.list.on('scroll', this.onViewScroll, this);
40570 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40573 this.view = new Roo.View(this.list, this.tpl, {
40574 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40577 this.view.on('click', this.onViewClick, this);
40579 this.store.on('beforeload', this.onBeforeLoad, this);
40580 this.store.on('load', this.onLoad, this);
40581 this.store.on('loadexception', this.onLoadException, this);
40583 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40584 "up" : function(e){
40585 this.inKeyMode = true;
40589 "down" : function(e){
40590 if(!this.isExpanded()){
40591 this.onTriggerClick();
40593 this.inKeyMode = true;
40598 "enter" : function(e){
40601 if(this.fireEvent("specialkey", this, e)){
40602 this.onViewClick(false);
40608 "esc" : function(e){
40612 "tab" : function(e){
40615 if(this.fireEvent("specialkey", this, e)){
40616 this.onViewClick(false);
40624 doRelay : function(foo, bar, hname){
40625 if(hname == 'down' || this.scope.isExpanded()){
40626 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40634 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40638 initNumberEvent : function(e)
40640 this.inputEl().on("keydown" , this.fireKey, this);
40641 this.inputEl().on("focus", this.onFocus, this);
40642 this.inputEl().on("blur", this.onBlur, this);
40644 this.inputEl().relayEvent('keyup', this);
40646 if(this.indicator){
40647 this.indicator.addClass('invisible');
40650 this.originalValue = this.getValue();
40652 if(this.validationEvent == 'keyup'){
40653 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40654 this.inputEl().on('keyup', this.filterValidation, this);
40656 else if(this.validationEvent !== false){
40657 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40660 if(this.selectOnFocus){
40661 this.on("focus", this.preFocus, this);
40664 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40665 this.inputEl().on("keypress", this.filterKeys, this);
40667 this.inputEl().relayEvent('keypress', this);
40670 var allowed = "0123456789";
40672 if(this.allowDecimals){
40673 allowed += this.decimalSeparator;
40676 if(this.allowNegative){
40680 if(this.thousandsDelimiter) {
40684 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40686 var keyPress = function(e){
40688 var k = e.getKey();
40690 var c = e.getCharCode();
40693 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40694 allowed.indexOf(String.fromCharCode(c)) === -1
40700 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40704 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40709 this.inputEl().on("keypress", keyPress, this);
40713 onTriggerClick : function(e)
40720 this.loadNext = false;
40722 if(this.isExpanded()){
40727 this.hasFocus = true;
40729 if(this.triggerAction == 'all') {
40730 this.doQuery(this.allQuery, true);
40734 this.doQuery(this.getRawValue());
40737 getCurrency : function()
40739 var v = this.currencyEl().getValue();
40744 restrictHeight : function()
40746 this.list.alignTo(this.currencyEl(), this.listAlign);
40747 this.list.alignTo(this.currencyEl(), this.listAlign);
40750 onViewClick : function(view, doFocus, el, e)
40752 var index = this.view.getSelectedIndexes()[0];
40754 var r = this.store.getAt(index);
40757 this.onSelect(r, index);
40761 onSelect : function(record, index){
40763 if(this.fireEvent('beforeselect', this, record, index) !== false){
40765 this.setFromCurrencyData(index > -1 ? record.data : false);
40769 this.fireEvent('select', this, record, index);
40773 setFromCurrencyData : function(o)
40777 this.lastCurrency = o;
40779 if (this.currencyField) {
40780 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40782 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40785 this.lastSelectionText = currency;
40787 //setting default currency
40788 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40789 this.setCurrency(this.defaultCurrency);
40793 this.setCurrency(currency);
40796 setFromData : function(o)
40800 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40802 this.setFromCurrencyData(c);
40807 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40809 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40812 this.setValue(value);
40816 setCurrency : function(v)
40818 this.currencyValue = v;
40821 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40826 setValue : function(v)
40828 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40834 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40836 this.inputEl().dom.value = (v == '') ? '' :
40837 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40839 if(!this.allowZero && v === '0') {
40840 this.hiddenEl().dom.value = '';
40841 this.inputEl().dom.value = '';
40848 getRawValue : function()
40850 var v = this.inputEl().getValue();
40855 getValue : function()
40857 return this.fixPrecision(this.parseValue(this.getRawValue()));
40860 parseValue : function(value)
40862 if(this.thousandsDelimiter) {
40864 r = new RegExp(",", "g");
40865 value = value.replace(r, "");
40868 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40869 return isNaN(value) ? '' : value;
40873 fixPrecision : function(value)
40875 if(this.thousandsDelimiter) {
40877 r = new RegExp(",", "g");
40878 value = value.replace(r, "");
40881 var nan = isNaN(value);
40883 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40884 return nan ? '' : value;
40886 return parseFloat(value).toFixed(this.decimalPrecision);
40889 decimalPrecisionFcn : function(v)
40891 return Math.floor(v);
40894 validateValue : function(value)
40896 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40900 var num = this.parseValue(value);
40903 this.markInvalid(String.format(this.nanText, value));
40907 if(num < this.minValue){
40908 this.markInvalid(String.format(this.minText, this.minValue));
40912 if(num > this.maxValue){
40913 this.markInvalid(String.format(this.maxText, this.maxValue));
40920 validate : function()
40922 if(this.disabled || this.allowBlank){
40927 var currency = this.getCurrency();
40929 if(this.validateValue(this.getRawValue()) && currency.length){
40934 this.markInvalid();
40938 getName: function()
40943 beforeBlur : function()
40949 var v = this.parseValue(this.getRawValue());
40956 onBlur : function()
40960 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40961 //this.el.removeClass(this.focusClass);
40964 this.hasFocus = false;
40966 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40970 var v = this.getValue();
40972 if(String(v) !== String(this.startValue)){
40973 this.fireEvent('change', this, v, this.startValue);
40976 this.fireEvent("blur", this);
40979 inputEl : function()
40981 return this.el.select('.roo-money-amount-input', true).first();
40984 currencyEl : function()
40986 return this.el.select('.roo-money-currency-input', true).first();
40989 hiddenEl : function()
40991 return this.el.select('input.hidden-number-input',true).first();