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');
407 * Hide a component - adds 'hidden' class
411 if(!this.getVisibilityEl()){
415 this.getVisibilityEl().addClass('hidden');
428 * @class Roo.bootstrap.Body
429 * @extends Roo.bootstrap.Component
430 * Bootstrap Body class
434 * @param {Object} config The config object
437 Roo.bootstrap.Body = function(config){
439 config = config || {};
441 Roo.bootstrap.Body.superclass.constructor.call(this, config);
442 this.el = Roo.get(config.el ? config.el : document.body );
443 if (this.cls && this.cls.length) {
444 Roo.get(document.body).addClass(this.cls);
448 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
450 is_body : true,// just to make sure it's constructed?
455 onRender : function(ct, position)
457 /* Roo.log("Roo.bootstrap.Body - onRender");
458 if (this.cls && this.cls.length) {
459 Roo.get(document.body).addClass(this.cls);
478 * @class Roo.bootstrap.ButtonGroup
479 * @extends Roo.bootstrap.Component
480 * Bootstrap ButtonGroup class
481 * @cfg {String} size lg | sm | xs (default empty normal)
482 * @cfg {String} align vertical | justified (default none)
483 * @cfg {String} direction up | down (default down)
484 * @cfg {Boolean} toolbar false | true
485 * @cfg {Boolean} btn true | false
490 * @param {Object} config The config object
493 Roo.bootstrap.ButtonGroup = function(config){
494 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
497 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
505 getAutoCreate : function(){
511 cfg.html = this.html || cfg.html;
522 if (['vertical','justified'].indexOf(this.align)!==-1) {
523 cfg.cls = 'btn-group-' + this.align;
525 if (this.align == 'justified') {
526 console.log(this.items);
530 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
531 cfg.cls += ' btn-group-' + this.size;
534 if (this.direction == 'up') {
535 cfg.cls += ' dropup' ;
551 * @class Roo.bootstrap.Button
552 * @extends Roo.bootstrap.Component
553 * Bootstrap Button class
554 * @cfg {String} html The button content
555 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
556 * @cfg {String} size ( lg | sm | xs)
557 * @cfg {String} tag ( a | input | submit)
558 * @cfg {String} href empty or href
559 * @cfg {Boolean} disabled default false;
560 * @cfg {Boolean} isClose default false;
561 * @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)
562 * @cfg {String} badge text for badge
563 * @cfg {String} theme (default|glow)
564 * @cfg {Boolean} inverse dark themed version
565 * @cfg {Boolean} toggle is it a slidy toggle button
566 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
567 * @cfg {String} ontext text for on slidy toggle state
568 * @cfg {String} offtext text for off slidy toggle state
569 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
570 * @cfg {Boolean} removeClass remove the standard class..
571 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
574 * Create a new button
575 * @param {Object} config The config object
579 Roo.bootstrap.Button = function(config){
580 Roo.bootstrap.Button.superclass.constructor.call(this, config);
581 this.weightClass = ["btn-default",
593 * When a butotn is pressed
594 * @param {Roo.bootstrap.Button} btn
595 * @param {Roo.EventObject} e
600 * After the button has been toggles
601 * @param {Roo.bootstrap.Button} btn
602 * @param {Roo.EventObject} e
603 * @param {boolean} pressed (also available as button.pressed)
609 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
627 preventDefault: true,
635 getAutoCreate : function(){
643 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
644 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
651 if (this.toggle == true) {
654 cls: 'slider-frame roo-button',
659 'data-off-text':'OFF',
660 cls: 'slider-button',
666 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667 cfg.cls += ' '+this.weight;
676 cfg["aria-hidden"] = true;
678 cfg.html = "×";
684 if (this.theme==='default') {
685 cfg.cls = 'btn roo-button';
687 //if (this.parentType != 'Navbar') {
688 this.weight = this.weight.length ? this.weight : 'default';
690 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
692 cfg.cls += ' btn-' + this.weight;
694 } else if (this.theme==='glow') {
697 cfg.cls = 'btn-glow roo-button';
699 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701 cfg.cls += ' ' + this.weight;
707 this.cls += ' inverse';
711 if (this.active || this.pressed === true) {
712 cfg.cls += ' active';
716 cfg.disabled = 'disabled';
720 Roo.log('changing to ul' );
722 this.glyphicon = 'caret';
725 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
727 //gsRoo.log(this.parentType);
728 if (this.parentType === 'Navbar' && !this.parent().bar) {
729 Roo.log('changing to li?');
738 href : this.href || '#'
741 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
742 cfg.cls += ' dropdown';
749 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
751 if (this.glyphicon) {
752 cfg.html = ' ' + cfg.html;
757 cls: 'glyphicon glyphicon-' + this.glyphicon
767 // cfg.cls='btn roo-button';
771 var value = cfg.html;
776 cls: 'glyphicon glyphicon-' + this.glyphicon,
795 cfg.cls += ' dropdown';
796 cfg.html = typeof(cfg.html) != 'undefined' ?
797 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
800 if (cfg.tag !== 'a' && this.href !== '') {
801 throw "Tag must be a to set href.";
802 } else if (this.href.length > 0) {
803 cfg.href = this.href;
806 if(this.removeClass){
811 cfg.target = this.target;
816 initEvents: function() {
817 // Roo.log('init events?');
818 // Roo.log(this.el.dom);
821 if (typeof (this.menu) != 'undefined') {
822 this.menu.parentType = this.xtype;
823 this.menu.triggerEl = this.el;
824 this.addxtype(Roo.apply({}, this.menu));
828 if (this.el.hasClass('roo-button')) {
829 this.el.on('click', this.onClick, this);
831 this.el.select('.roo-button').on('click', this.onClick, this);
834 if(this.removeClass){
835 this.el.on('click', this.onClick, this);
838 this.el.enableDisplayMode();
841 onClick : function(e)
847 Roo.log('button on click ');
848 if(this.preventDefault){
852 if (this.pressed === true || this.pressed === false) {
853 this.toggleActive(e);
857 this.fireEvent('click', this, e);
861 * Enables this button
865 this.disabled = false;
866 this.el.removeClass('disabled');
870 * Disable this button
874 this.disabled = true;
875 this.el.addClass('disabled');
878 * sets the active state on/off,
879 * @param {Boolean} state (optional) Force a particular state
881 setActive : function(v) {
883 this.el[v ? 'addClass' : 'removeClass']('active');
887 * toggles the current active state
889 toggleActive : function(e)
891 this.setActive(!this.pressed);
892 this.fireEvent('toggle', this, e, !this.pressed);
895 * get the current active state
896 * @return {boolean} true if it's active
898 isActive : function()
900 return this.el.hasClass('active');
903 * set the text of the first selected button
905 setText : function(str)
907 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
910 * get the text of the first selected button
914 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
925 setWeight : function(str)
927 this.el.removeClass(this.weightClass);
928 this.el.addClass('btn-' + str);
942 * @class Roo.bootstrap.Column
943 * @extends Roo.bootstrap.Component
944 * Bootstrap Column class
945 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
946 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
947 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
948 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
949 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
955 * @cfg {Boolean} hidden (true|false) hide the element
956 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
957 * @cfg {String} fa (ban|check|...) font awesome icon
958 * @cfg {Number} fasize (1|2|....) font awsome size
960 * @cfg {String} icon (info-sign|check|...) glyphicon name
962 * @cfg {String} html content of column.
965 * Create a new Column
966 * @param {Object} config The config object
969 Roo.bootstrap.Column = function(config){
970 Roo.bootstrap.Column.superclass.constructor.call(this, config);
973 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
991 getAutoCreate : function(){
992 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1000 ['xs','sm','md','lg'].map(function(size){
1001 //Roo.log( size + ':' + settings[size]);
1003 if (settings[size+'off'] !== false) {
1004 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1007 if (settings[size] === false) {
1011 if (!settings[size]) { // 0 = hidden
1012 cfg.cls += ' hidden-' + size;
1015 cfg.cls += ' col-' + size + '-' + settings[size];
1020 cfg.cls += ' hidden';
1023 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1024 cfg.cls +=' alert alert-' + this.alert;
1028 if (this.html.length) {
1029 cfg.html = this.html;
1033 if (this.fasize > 1) {
1034 fasize = ' fa-' + this.fasize + 'x';
1036 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1041 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1060 * @class Roo.bootstrap.Container
1061 * @extends Roo.bootstrap.Component
1062 * Bootstrap Container class
1063 * @cfg {Boolean} jumbotron is it a jumbotron element
1064 * @cfg {String} html content of element
1065 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1066 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1067 * @cfg {String} header content of header (for panel)
1068 * @cfg {String} footer content of footer (for panel)
1069 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1070 * @cfg {String} tag (header|aside|section) type of HTML tag.
1071 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1072 * @cfg {String} fa font awesome icon
1073 * @cfg {String} icon (info-sign|check|...) glyphicon name
1074 * @cfg {Boolean} hidden (true|false) hide the element
1075 * @cfg {Boolean} expandable (true|false) default false
1076 * @cfg {Boolean} expanded (true|false) default true
1077 * @cfg {String} rheader contet on the right of header
1078 * @cfg {Boolean} clickable (true|false) default false
1082 * Create a new Container
1083 * @param {Object} config The config object
1086 Roo.bootstrap.Container = function(config){
1087 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1093 * After the panel has been expand
1095 * @param {Roo.bootstrap.Container} this
1100 * After the panel has been collapsed
1102 * @param {Roo.bootstrap.Container} this
1107 * When a element is chick
1108 * @param {Roo.bootstrap.Container} this
1109 * @param {Roo.EventObject} e
1115 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1133 getChildContainer : function() {
1139 if (this.panel.length) {
1140 return this.el.select('.panel-body',true).first();
1147 getAutoCreate : function(){
1150 tag : this.tag || 'div',
1154 if (this.jumbotron) {
1155 cfg.cls = 'jumbotron';
1160 // - this is applied by the parent..
1162 // cfg.cls = this.cls + '';
1165 if (this.sticky.length) {
1167 var bd = Roo.get(document.body);
1168 if (!bd.hasClass('bootstrap-sticky')) {
1169 bd.addClass('bootstrap-sticky');
1170 Roo.select('html',true).setStyle('height', '100%');
1173 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1177 if (this.well.length) {
1178 switch (this.well) {
1181 cfg.cls +=' well well-' +this.well;
1190 cfg.cls += ' hidden';
1194 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1195 cfg.cls +=' alert alert-' + this.alert;
1200 if (this.panel.length) {
1201 cfg.cls += ' panel panel-' + this.panel;
1203 if (this.header.length) {
1207 if(this.expandable){
1209 cfg.cls = cfg.cls + ' expandable';
1213 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1221 cls : 'panel-title',
1222 html : (this.expandable ? ' ' : '') + this.header
1226 cls: 'panel-header-right',
1232 cls : 'panel-heading',
1233 style : this.expandable ? 'cursor: pointer' : '',
1241 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1246 if (this.footer.length) {
1248 cls : 'panel-footer',
1257 body.html = this.html || cfg.html;
1258 // prefix with the icons..
1260 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1263 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1268 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1269 cfg.cls = 'container';
1275 initEvents: function()
1277 if(this.expandable){
1278 var headerEl = this.headerEl();
1281 headerEl.on('click', this.onToggleClick, this);
1286 this.el.on('click', this.onClick, this);
1291 onToggleClick : function()
1293 var headerEl = this.headerEl();
1309 if(this.fireEvent('expand', this)) {
1311 this.expanded = true;
1313 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1315 this.el.select('.panel-body',true).first().removeClass('hide');
1317 var toggleEl = this.toggleEl();
1323 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1328 collapse : function()
1330 if(this.fireEvent('collapse', this)) {
1332 this.expanded = false;
1334 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1335 this.el.select('.panel-body',true).first().addClass('hide');
1337 var toggleEl = this.toggleEl();
1343 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1347 toggleEl : function()
1349 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1353 return this.el.select('.panel-heading .fa',true).first();
1356 headerEl : function()
1358 if(!this.el || !this.panel.length || !this.header.length){
1362 return this.el.select('.panel-heading',true).first()
1367 if(!this.el || !this.panel.length){
1371 return this.el.select('.panel-body',true).first()
1374 titleEl : function()
1376 if(!this.el || !this.panel.length || !this.header.length){
1380 return this.el.select('.panel-title',true).first();
1383 setTitle : function(v)
1385 var titleEl = this.titleEl();
1391 titleEl.dom.innerHTML = v;
1394 getTitle : function()
1397 var titleEl = this.titleEl();
1403 return titleEl.dom.innerHTML;
1406 setRightTitle : function(v)
1408 var t = this.el.select('.panel-header-right',true).first();
1414 t.dom.innerHTML = v;
1417 onClick : function(e)
1421 this.fireEvent('click', this, e);
1434 * @class Roo.bootstrap.Img
1435 * @extends Roo.bootstrap.Component
1436 * Bootstrap Img class
1437 * @cfg {Boolean} imgResponsive false | true
1438 * @cfg {String} border rounded | circle | thumbnail
1439 * @cfg {String} src image source
1440 * @cfg {String} alt image alternative text
1441 * @cfg {String} href a tag href
1442 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1443 * @cfg {String} xsUrl xs image source
1444 * @cfg {String} smUrl sm image source
1445 * @cfg {String} mdUrl md image source
1446 * @cfg {String} lgUrl lg image source
1449 * Create a new Input
1450 * @param {Object} config The config object
1453 Roo.bootstrap.Img = function(config){
1454 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1460 * The img click event for the img.
1461 * @param {Roo.EventObject} e
1467 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1469 imgResponsive: true,
1479 getAutoCreate : function()
1481 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1482 return this.createSingleImg();
1487 cls: 'roo-image-responsive-group',
1492 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1494 if(!_this[size + 'Url']){
1500 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1501 html: _this.html || cfg.html,
1502 src: _this[size + 'Url']
1505 img.cls += ' roo-image-responsive-' + size;
1507 var s = ['xs', 'sm', 'md', 'lg'];
1509 s.splice(s.indexOf(size), 1);
1511 Roo.each(s, function(ss){
1512 img.cls += ' hidden-' + ss;
1515 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1516 cfg.cls += ' img-' + _this.border;
1520 cfg.alt = _this.alt;
1533 a.target = _this.target;
1537 cfg.cn.push((_this.href) ? a : img);
1544 createSingleImg : function()
1548 cls: (this.imgResponsive) ? 'img-responsive' : '',
1550 src : 'about:blank' // just incase src get's set to undefined?!?
1553 cfg.html = this.html || cfg.html;
1555 cfg.src = this.src || cfg.src;
1557 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1558 cfg.cls += ' img-' + this.border;
1575 a.target = this.target;
1580 return (this.href) ? a : cfg;
1583 initEvents: function()
1586 this.el.on('click', this.onClick, this);
1591 onClick : function(e)
1593 Roo.log('img onclick');
1594 this.fireEvent('click', this, e);
1597 * Sets the url of the image - used to update it
1598 * @param {String} url the url of the image
1601 setSrc : function(url)
1605 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1606 this.el.dom.src = url;
1610 this.el.select('img', true).first().dom.src = url;
1626 * @class Roo.bootstrap.Link
1627 * @extends Roo.bootstrap.Component
1628 * Bootstrap Link Class
1629 * @cfg {String} alt image alternative text
1630 * @cfg {String} href a tag href
1631 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1632 * @cfg {String} html the content of the link.
1633 * @cfg {String} anchor name for the anchor link
1634 * @cfg {String} fa - favicon
1636 * @cfg {Boolean} preventDefault (true | false) default false
1640 * Create a new Input
1641 * @param {Object} config The config object
1644 Roo.bootstrap.Link = function(config){
1645 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1651 * The img click event for the img.
1652 * @param {Roo.EventObject} e
1658 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1662 preventDefault: false,
1668 getAutoCreate : function()
1670 var html = this.html || '';
1672 if (this.fa !== false) {
1673 html = '<i class="fa fa-' + this.fa + '"></i>';
1678 // anchor's do not require html/href...
1679 if (this.anchor === false) {
1681 cfg.href = this.href || '#';
1683 cfg.name = this.anchor;
1684 if (this.html !== false || this.fa !== false) {
1687 if (this.href !== false) {
1688 cfg.href = this.href;
1692 if(this.alt !== false){
1697 if(this.target !== false) {
1698 cfg.target = this.target;
1704 initEvents: function() {
1706 if(!this.href || this.preventDefault){
1707 this.el.on('click', this.onClick, this);
1711 onClick : function(e)
1713 if(this.preventDefault){
1716 //Roo.log('img onclick');
1717 this.fireEvent('click', this, e);
1730 * @class Roo.bootstrap.Header
1731 * @extends Roo.bootstrap.Component
1732 * Bootstrap Header class
1733 * @cfg {String} html content of header
1734 * @cfg {Number} level (1|2|3|4|5|6) default 1
1737 * Create a new Header
1738 * @param {Object} config The config object
1742 Roo.bootstrap.Header = function(config){
1743 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1746 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1754 getAutoCreate : function(){
1759 tag: 'h' + (1 *this.level),
1760 html: this.html || ''
1772 * Ext JS Library 1.1.1
1773 * Copyright(c) 2006-2007, Ext JS, LLC.
1775 * Originally Released Under LGPL - original licence link has changed is not relivant.
1778 * <script type="text/javascript">
1782 * @class Roo.bootstrap.MenuMgr
1783 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1786 Roo.bootstrap.MenuMgr = function(){
1787 var menus, active, groups = {}, attached = false, lastShow = new Date();
1789 // private - called when first menu is created
1792 active = new Roo.util.MixedCollection();
1793 Roo.get(document).addKeyListener(27, function(){
1794 if(active.length > 0){
1802 if(active && active.length > 0){
1803 var c = active.clone();
1813 if(active.length < 1){
1814 Roo.get(document).un("mouseup", onMouseDown);
1822 var last = active.last();
1823 lastShow = new Date();
1826 Roo.get(document).on("mouseup", onMouseDown);
1831 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1832 m.parentMenu.activeChild = m;
1833 }else if(last && last.isVisible()){
1834 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1839 function onBeforeHide(m){
1841 m.activeChild.hide();
1843 if(m.autoHideTimer){
1844 clearTimeout(m.autoHideTimer);
1845 delete m.autoHideTimer;
1850 function onBeforeShow(m){
1851 var pm = m.parentMenu;
1852 if(!pm && !m.allowOtherMenus){
1854 }else if(pm && pm.activeChild && active != m){
1855 pm.activeChild.hide();
1859 // private this should really trigger on mouseup..
1860 function onMouseDown(e){
1861 Roo.log("on Mouse Up");
1863 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1864 Roo.log("MenuManager hideAll");
1873 function onBeforeCheck(mi, state){
1875 var g = groups[mi.group];
1876 for(var i = 0, l = g.length; i < l; i++){
1878 g[i].setChecked(false);
1887 * Hides all menus that are currently visible
1889 hideAll : function(){
1894 register : function(menu){
1898 menus[menu.id] = menu;
1899 menu.on("beforehide", onBeforeHide);
1900 menu.on("hide", onHide);
1901 menu.on("beforeshow", onBeforeShow);
1902 menu.on("show", onShow);
1904 if(g && menu.events["checkchange"]){
1908 groups[g].push(menu);
1909 menu.on("checkchange", onCheck);
1914 * Returns a {@link Roo.menu.Menu} object
1915 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1916 * be used to generate and return a new Menu instance.
1918 get : function(menu){
1919 if(typeof menu == "string"){ // menu id
1921 }else if(menu.events){ // menu instance
1924 /*else if(typeof menu.length == 'number'){ // array of menu items?
1925 return new Roo.bootstrap.Menu({items:menu});
1926 }else{ // otherwise, must be a config
1927 return new Roo.bootstrap.Menu(menu);
1934 unregister : function(menu){
1935 delete menus[menu.id];
1936 menu.un("beforehide", onBeforeHide);
1937 menu.un("hide", onHide);
1938 menu.un("beforeshow", onBeforeShow);
1939 menu.un("show", onShow);
1941 if(g && menu.events["checkchange"]){
1942 groups[g].remove(menu);
1943 menu.un("checkchange", onCheck);
1948 registerCheckable : function(menuItem){
1949 var g = menuItem.group;
1954 groups[g].push(menuItem);
1955 menuItem.on("beforecheckchange", onBeforeCheck);
1960 unregisterCheckable : function(menuItem){
1961 var g = menuItem.group;
1963 groups[g].remove(menuItem);
1964 menuItem.un("beforecheckchange", onBeforeCheck);
1976 * @class Roo.bootstrap.Menu
1977 * @extends Roo.bootstrap.Component
1978 * Bootstrap Menu class - container for MenuItems
1979 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1980 * @cfg {bool} hidden if the menu should be hidden when rendered.
1981 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1982 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1986 * @param {Object} config The config object
1990 Roo.bootstrap.Menu = function(config){
1991 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1992 if (this.registerMenu && this.type != 'treeview') {
1993 Roo.bootstrap.MenuMgr.register(this);
1998 * Fires before this menu is displayed
1999 * @param {Roo.menu.Menu} this
2004 * Fires before this menu is hidden
2005 * @param {Roo.menu.Menu} this
2010 * Fires after this menu is displayed
2011 * @param {Roo.menu.Menu} this
2016 * Fires after this menu is hidden
2017 * @param {Roo.menu.Menu} this
2022 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2023 * @param {Roo.menu.Menu} this
2024 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2025 * @param {Roo.EventObject} e
2030 * Fires when the mouse is hovering over this menu
2031 * @param {Roo.menu.Menu} this
2032 * @param {Roo.EventObject} e
2033 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038 * Fires when the mouse exits this menu
2039 * @param {Roo.menu.Menu} this
2040 * @param {Roo.EventObject} e
2041 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2046 * Fires when a menu item contained in this menu is clicked
2047 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2048 * @param {Roo.EventObject} e
2052 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2055 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2059 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2062 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2064 registerMenu : true,
2066 menuItems :false, // stores the menu items..
2076 getChildContainer : function() {
2080 getAutoCreate : function(){
2082 //if (['right'].indexOf(this.align)!==-1) {
2083 // cfg.cn[1].cls += ' pull-right'
2089 cls : 'dropdown-menu' ,
2090 style : 'z-index:1000'
2094 if (this.type === 'submenu') {
2095 cfg.cls = 'submenu active';
2097 if (this.type === 'treeview') {
2098 cfg.cls = 'treeview-menu';
2103 initEvents : function() {
2105 // Roo.log("ADD event");
2106 // Roo.log(this.triggerEl.dom);
2108 this.triggerEl.on('click', this.onTriggerClick, this);
2110 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2112 this.triggerEl.addClass('dropdown-toggle');
2115 this.el.on('touchstart' , this.onTouch, this);
2117 this.el.on('click' , this.onClick, this);
2119 this.el.on("mouseover", this.onMouseOver, this);
2120 this.el.on("mouseout", this.onMouseOut, this);
2124 findTargetItem : function(e)
2126 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2130 //Roo.log(t); Roo.log(t.id);
2132 //Roo.log(this.menuitems);
2133 return this.menuitems.get(t.id);
2135 //return this.items.get(t.menuItemId);
2141 onTouch : function(e)
2143 Roo.log("menu.onTouch");
2144 //e.stopEvent(); this make the user popdown broken
2148 onClick : function(e)
2150 Roo.log("menu.onClick");
2152 var t = this.findTargetItem(e);
2153 if(!t || t.isContainer){
2158 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2159 if(t == this.activeItem && t.shouldDeactivate(e)){
2160 this.activeItem.deactivate();
2161 delete this.activeItem;
2165 this.setActiveItem(t, true);
2173 Roo.log('pass click event');
2177 this.fireEvent("click", this, t, e);
2181 if(!t.href.length || t.href == '#'){
2182 (function() { _this.hide(); }).defer(100);
2187 onMouseOver : function(e){
2188 var t = this.findTargetItem(e);
2191 // if(t.canActivate && !t.disabled){
2192 // this.setActiveItem(t, true);
2196 this.fireEvent("mouseover", this, e, t);
2198 isVisible : function(){
2199 return !this.hidden;
2201 onMouseOut : function(e){
2202 var t = this.findTargetItem(e);
2205 // if(t == this.activeItem && t.shouldDeactivate(e)){
2206 // this.activeItem.deactivate();
2207 // delete this.activeItem;
2210 this.fireEvent("mouseout", this, e, t);
2215 * Displays this menu relative to another element
2216 * @param {String/HTMLElement/Roo.Element} element The element to align to
2217 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2218 * the element (defaults to this.defaultAlign)
2219 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2221 show : function(el, pos, parentMenu){
2222 this.parentMenu = parentMenu;
2226 this.fireEvent("beforeshow", this);
2227 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2230 * Displays this menu at a specific xy position
2231 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2232 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2234 showAt : function(xy, parentMenu, /* private: */_e){
2235 this.parentMenu = parentMenu;
2240 this.fireEvent("beforeshow", this);
2241 //xy = this.el.adjustForConstraints(xy);
2245 this.hideMenuItems();
2246 this.hidden = false;
2247 this.triggerEl.addClass('open');
2249 // reassign x when hitting right
2250 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2251 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2254 // reassign y when hitting bottom
2255 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2256 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2259 // but the list may align on trigger left or trigger top... should it be a properity?
2261 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2266 this.fireEvent("show", this);
2272 this.doFocus.defer(50, this);
2276 doFocus : function(){
2278 this.focusEl.focus();
2283 * Hides this menu and optionally all parent menus
2284 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2286 hide : function(deep)
2289 this.hideMenuItems();
2290 if(this.el && this.isVisible()){
2291 this.fireEvent("beforehide", this);
2292 if(this.activeItem){
2293 this.activeItem.deactivate();
2294 this.activeItem = null;
2296 this.triggerEl.removeClass('open');;
2298 this.fireEvent("hide", this);
2300 if(deep === true && this.parentMenu){
2301 this.parentMenu.hide(true);
2305 onTriggerClick : function(e)
2307 Roo.log('trigger click');
2309 var target = e.getTarget();
2311 Roo.log(target.nodeName.toLowerCase());
2313 if(target.nodeName.toLowerCase() === 'i'){
2319 onTriggerPress : function(e)
2321 Roo.log('trigger press');
2322 //Roo.log(e.getTarget());
2323 // Roo.log(this.triggerEl.dom);
2325 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2326 var pel = Roo.get(e.getTarget());
2327 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2328 Roo.log('is treeview or dropdown?');
2332 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2336 if (this.isVisible()) {
2341 this.show(this.triggerEl, false, false);
2344 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2351 hideMenuItems : function()
2353 Roo.log("hide Menu Items");
2357 //$(backdrop).remove()
2358 this.el.select('.open',true).each(function(aa) {
2360 aa.removeClass('open');
2361 //var parent = getParent($(this))
2362 //var relatedTarget = { relatedTarget: this }
2364 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2365 //if (e.isDefaultPrevented()) return
2366 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2369 addxtypeChild : function (tree, cntr) {
2370 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2372 this.menuitems.add(comp);
2384 this.getEl().dom.innerHTML = '';
2385 this.menuitems.clear();
2399 * @class Roo.bootstrap.MenuItem
2400 * @extends Roo.bootstrap.Component
2401 * Bootstrap MenuItem class
2402 * @cfg {String} html the menu label
2403 * @cfg {String} href the link
2404 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2405 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2406 * @cfg {Boolean} active used on sidebars to highlight active itesm
2407 * @cfg {String} fa favicon to show on left of menu item.
2408 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2412 * Create a new MenuItem
2413 * @param {Object} config The config object
2417 Roo.bootstrap.MenuItem = function(config){
2418 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2423 * The raw click event for the entire grid.
2424 * @param {Roo.bootstrap.MenuItem} this
2425 * @param {Roo.EventObject} e
2431 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2435 preventDefault: false,
2436 isContainer : false,
2440 getAutoCreate : function(){
2442 if(this.isContainer){
2445 cls: 'dropdown-menu-item'
2459 if (this.fa !== false) {
2462 cls : 'fa fa-' + this.fa
2471 cls: 'dropdown-menu-item',
2474 if (this.parent().type == 'treeview') {
2475 cfg.cls = 'treeview-menu';
2478 cfg.cls += ' active';
2483 anc.href = this.href || cfg.cn[0].href ;
2484 ctag.html = this.html || cfg.cn[0].html ;
2488 initEvents: function()
2490 if (this.parent().type == 'treeview') {
2491 this.el.select('a').on('click', this.onClick, this);
2495 this.menu.parentType = this.xtype;
2496 this.menu.triggerEl = this.el;
2497 this.menu = this.addxtype(Roo.apply({}, this.menu));
2501 onClick : function(e)
2503 Roo.log('item on click ');
2505 if(this.preventDefault){
2508 //this.parent().hideMenuItems();
2510 this.fireEvent('click', this, e);
2529 * @class Roo.bootstrap.MenuSeparator
2530 * @extends Roo.bootstrap.Component
2531 * Bootstrap MenuSeparator class
2534 * Create a new MenuItem
2535 * @param {Object} config The config object
2539 Roo.bootstrap.MenuSeparator = function(config){
2540 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2543 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2545 getAutoCreate : function(){
2564 * @class Roo.bootstrap.Modal
2565 * @extends Roo.bootstrap.Component
2566 * Bootstrap Modal class
2567 * @cfg {String} title Title of dialog
2568 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2569 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2570 * @cfg {Boolean} specificTitle default false
2571 * @cfg {Array} buttons Array of buttons or standard button set..
2572 * @cfg {String} buttonPosition (left|right|center) default right
2573 * @cfg {Boolean} animate default true
2574 * @cfg {Boolean} allow_close default true
2575 * @cfg {Boolean} fitwindow default false
2576 * @cfg {String} size (sm|lg) default empty
2580 * Create a new Modal Dialog
2581 * @param {Object} config The config object
2584 Roo.bootstrap.Modal = function(config){
2585 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2590 * The raw btnclick event for the button
2591 * @param {Roo.EventObject} e
2596 * Fire when dialog resize
2597 * @param {Roo.bootstrap.Modal} this
2598 * @param {Roo.EventObject} e
2602 this.buttons = this.buttons || [];
2605 this.tmpl = Roo.factory(this.tmpl);
2610 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2612 title : 'test dialog',
2622 specificTitle: false,
2624 buttonPosition: 'right',
2643 onRender : function(ct, position)
2645 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2648 var cfg = Roo.apply({}, this.getAutoCreate());
2651 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653 //if (!cfg.name.length) {
2657 cfg.cls += ' ' + this.cls;
2660 cfg.style = this.style;
2662 this.el = Roo.get(document.body).createChild(cfg, position);
2664 //var type = this.el.dom.type;
2667 if(this.tabIndex !== undefined){
2668 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2671 this.dialogEl = this.el.select('.modal-dialog',true).first();
2672 this.bodyEl = this.el.select('.modal-body',true).first();
2673 this.closeEl = this.el.select('.modal-header .close', true).first();
2674 this.headerEl = this.el.select('.modal-header',true).first();
2675 this.titleEl = this.el.select('.modal-title',true).first();
2676 this.footerEl = this.el.select('.modal-footer',true).first();
2678 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680 //this.el.addClass("x-dlg-modal");
2682 if (this.buttons.length) {
2683 Roo.each(this.buttons, function(bb) {
2684 var b = Roo.apply({}, bb);
2685 b.xns = b.xns || Roo.bootstrap;
2686 b.xtype = b.xtype || 'Button';
2687 if (typeof(b.listeners) == 'undefined') {
2688 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2691 var btn = Roo.factory(b);
2693 btn.render(this.el.select('.modal-footer div').first());
2697 // render the children.
2700 if(typeof(this.items) != 'undefined'){
2701 var items = this.items;
2704 for(var i =0;i < items.length;i++) {
2705 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2709 this.items = nitems;
2711 // where are these used - they used to be body/close/footer
2715 //this.el.addClass([this.fieldClass, this.cls]);
2719 getAutoCreate : function(){
2724 html : this.html || ''
2729 cls : 'modal-title',
2733 if(this.specificTitle){
2739 if (this.allow_close) {
2751 if(this.size.length){
2752 size = 'modal-' + this.size;
2759 cls: "modal-dialog " + size,
2762 cls : "modal-content",
2765 cls : 'modal-header',
2770 cls : 'modal-footer',
2774 cls: 'btn-' + this.buttonPosition
2791 modal.cls += ' fade';
2797 getChildContainer : function() {
2802 getButtonContainer : function() {
2803 return this.el.select('.modal-footer div',true).first();
2806 initEvents : function()
2808 if (this.allow_close) {
2809 this.closeEl.on('click', this.hide, this);
2811 Roo.EventManager.onWindowResize(this.resize, this, true);
2818 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2819 if (this.fitwindow) {
2820 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2821 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2826 setSize : function(w,h)
2836 if (!this.rendered) {
2840 //this.el.setStyle('display', 'block');
2841 this.el.removeClass('hideing');
2842 this.el.addClass('show');
2844 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2847 this.el.addClass('in');
2850 this.el.addClass('in');
2854 // not sure how we can show data in here..
2856 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2859 Roo.get(document.body).addClass("x-body-masked");
2861 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2862 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2863 this.maskEl.addClass('show');
2867 this.fireEvent('show', this);
2869 // set zindex here - otherwise it appears to be ignored...
2870 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2873 this.items.forEach( function(e) {
2874 e.layout ? e.layout() : false;
2882 if(this.fireEvent("beforehide", this) !== false){
2883 this.maskEl.removeClass('show');
2884 Roo.get(document.body).removeClass("x-body-masked");
2885 this.el.removeClass('in');
2886 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2888 if(this.animate){ // why
2889 this.el.addClass('hideing');
2891 if (!this.el.hasClass('hideing')) {
2892 return; // it's been shown again...
2894 this.el.removeClass('show');
2895 this.el.removeClass('hideing');
2899 this.el.removeClass('show');
2901 this.fireEvent('hide', this);
2904 isVisible : function()
2907 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2911 addButton : function(str, cb)
2915 var b = Roo.apply({}, { html : str } );
2916 b.xns = b.xns || Roo.bootstrap;
2917 b.xtype = b.xtype || 'Button';
2918 if (typeof(b.listeners) == 'undefined') {
2919 b.listeners = { click : cb.createDelegate(this) };
2922 var btn = Roo.factory(b);
2924 btn.render(this.el.select('.modal-footer div').first());
2930 setDefaultButton : function(btn)
2932 //this.el.select('.modal-footer').()
2936 resizeTo: function(w,h)
2940 this.dialogEl.setWidth(w);
2941 if (this.diff === false) {
2942 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2945 this.bodyEl.setHeight(h-this.diff);
2947 this.fireEvent('resize', this);
2950 setContentSize : function(w, h)
2954 onButtonClick: function(btn,e)
2957 this.fireEvent('btnclick', btn.name, e);
2960 * Set the title of the Dialog
2961 * @param {String} str new Title
2963 setTitle: function(str) {
2964 this.titleEl.dom.innerHTML = str;
2967 * Set the body of the Dialog
2968 * @param {String} str new Title
2970 setBody: function(str) {
2971 this.bodyEl.dom.innerHTML = str;
2974 * Set the body of the Dialog using the template
2975 * @param {Obj} data - apply this data to the template and replace the body contents.
2977 applyBody: function(obj)
2980 Roo.log("Error - using apply Body without a template");
2983 this.tmpl.overwrite(this.bodyEl, obj);
2989 Roo.apply(Roo.bootstrap.Modal, {
2991 * Button config that displays a single OK button
3000 * Button config that displays Yes and No buttons
3016 * Button config that displays OK and Cancel buttons
3031 * Button config that displays Yes, No and Cancel buttons
3055 * messagebox - can be used as a replace
3059 * @class Roo.MessageBox
3060 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3064 Roo.Msg.alert('Status', 'Changes saved successfully.');
3066 // Prompt for user data:
3067 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3069 // process text value...
3073 // Show a dialog using config options:
3075 title:'Save Changes?',
3076 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3077 buttons: Roo.Msg.YESNOCANCEL,
3084 Roo.bootstrap.MessageBox = function(){
3085 var dlg, opt, mask, waitTimer;
3086 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3087 var buttons, activeTextEl, bwidth;
3091 var handleButton = function(button){
3093 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3097 var handleHide = function(){
3099 dlg.el.removeClass(opt.cls);
3102 // Roo.TaskMgr.stop(waitTimer);
3103 // waitTimer = null;
3108 var updateButtons = function(b){
3111 buttons["ok"].hide();
3112 buttons["cancel"].hide();
3113 buttons["yes"].hide();
3114 buttons["no"].hide();
3115 //dlg.footer.dom.style.display = 'none';
3118 dlg.footerEl.dom.style.display = '';
3119 for(var k in buttons){
3120 if(typeof buttons[k] != "function"){
3123 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3124 width += buttons[k].el.getWidth()+15;
3134 var handleEsc = function(d, k, e){
3135 if(opt && opt.closable !== false){
3145 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3146 * @return {Roo.BasicDialog} The BasicDialog element
3148 getDialog : function(){
3150 dlg = new Roo.bootstrap.Modal( {
3153 //constraintoviewport:false,
3155 //collapsible : false,
3160 //buttonAlign:"center",
3161 closeClick : function(){
3162 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3165 handleButton("cancel");
3170 dlg.on("hide", handleHide);
3172 //dlg.addKeyListener(27, handleEsc);
3174 this.buttons = buttons;
3175 var bt = this.buttonText;
3176 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3177 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3178 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3179 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3181 bodyEl = dlg.bodyEl.createChild({
3183 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3184 '<textarea class="roo-mb-textarea"></textarea>' +
3185 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3187 msgEl = bodyEl.dom.firstChild;
3188 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3189 textboxEl.enableDisplayMode();
3190 textboxEl.addKeyListener([10,13], function(){
3191 if(dlg.isVisible() && opt && opt.buttons){
3194 }else if(opt.buttons.yes){
3195 handleButton("yes");
3199 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3200 textareaEl.enableDisplayMode();
3201 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3202 progressEl.enableDisplayMode();
3204 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3205 var pf = progressEl.dom.firstChild;
3207 pp = Roo.get(pf.firstChild);
3208 pp.setHeight(pf.offsetHeight);
3216 * Updates the message box body text
3217 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3218 * the XHTML-compliant non-breaking space character '&#160;')
3219 * @return {Roo.MessageBox} This message box
3221 updateText : function(text)
3223 if(!dlg.isVisible() && !opt.width){
3224 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3225 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3227 msgEl.innerHTML = text || ' ';
3229 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3230 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3232 Math.min(opt.width || cw , this.maxWidth),
3233 Math.max(opt.minWidth || this.minWidth, bwidth)
3236 activeTextEl.setWidth(w);
3238 if(dlg.isVisible()){
3239 dlg.fixedcenter = false;
3241 // to big, make it scroll. = But as usual stupid IE does not support
3244 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3245 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3246 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3248 bodyEl.dom.style.height = '';
3249 bodyEl.dom.style.overflowY = '';
3252 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3254 bodyEl.dom.style.overflowX = '';
3257 dlg.setContentSize(w, bodyEl.getHeight());
3258 if(dlg.isVisible()){
3259 dlg.fixedcenter = true;
3265 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3266 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3267 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3268 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3269 * @return {Roo.MessageBox} This message box
3271 updateProgress : function(value, text){
3273 this.updateText(text);
3276 if (pp) { // weird bug on my firefox - for some reason this is not defined
3277 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3278 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3284 * Returns true if the message box is currently displayed
3285 * @return {Boolean} True if the message box is visible, else false
3287 isVisible : function(){
3288 return dlg && dlg.isVisible();
3292 * Hides the message box if it is displayed
3295 if(this.isVisible()){
3301 * Displays a new message box, or reinitializes an existing message box, based on the config options
3302 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3303 * The following config object properties are supported:
3305 Property Type Description
3306 ---------- --------------- ------------------------------------------------------------------------------------
3307 animEl String/Element An id or Element from which the message box should animate as it opens and
3308 closes (defaults to undefined)
3309 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3310 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3311 closable Boolean False to hide the top-right close button (defaults to true). Note that
3312 progress and wait dialogs will ignore this property and always hide the
3313 close button as they can only be closed programmatically.
3314 cls String A custom CSS class to apply to the message box element
3315 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3316 displayed (defaults to 75)
3317 fn Function A callback function to execute after closing the dialog. The arguments to the
3318 function will be btn (the name of the button that was clicked, if applicable,
3319 e.g. "ok"), and text (the value of the active text field, if applicable).
3320 Progress and wait dialogs will ignore this option since they do not respond to
3321 user actions and can only be closed programmatically, so any required function
3322 should be called by the same code after it closes the dialog.
3323 icon String A CSS class that provides a background image to be used as an icon for
3324 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3325 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3326 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3327 modal Boolean False to allow user interaction with the page while the message box is
3328 displayed (defaults to true)
3329 msg String A string that will replace the existing message box body text (defaults
3330 to the XHTML-compliant non-breaking space character ' ')
3331 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3332 progress Boolean True to display a progress bar (defaults to false)
3333 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3334 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3335 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3336 title String The title text
3337 value String The string value to set into the active textbox element if displayed
3338 wait Boolean True to display a progress bar (defaults to false)
3339 width Number The width of the dialog in pixels
3346 msg: 'Please enter your address:',
3348 buttons: Roo.MessageBox.OKCANCEL,
3351 animEl: 'addAddressBtn'
3354 * @param {Object} config Configuration options
3355 * @return {Roo.MessageBox} This message box
3357 show : function(options)
3360 // this causes nightmares if you show one dialog after another
3361 // especially on callbacks..
3363 if(this.isVisible()){
3366 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3367 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3368 Roo.log("New Dialog Message:" + options.msg )
3369 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3370 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3373 var d = this.getDialog();
3375 d.setTitle(opt.title || " ");
3376 d.closeEl.setDisplayed(opt.closable !== false);
3377 activeTextEl = textboxEl;
3378 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3383 textareaEl.setHeight(typeof opt.multiline == "number" ?
3384 opt.multiline : this.defaultTextHeight);
3385 activeTextEl = textareaEl;
3394 progressEl.setDisplayed(opt.progress === true);
3395 this.updateProgress(0);
3396 activeTextEl.dom.value = opt.value || "";
3398 dlg.setDefaultButton(activeTextEl);
3400 var bs = opt.buttons;
3404 }else if(bs && bs.yes){
3405 db = buttons["yes"];
3407 dlg.setDefaultButton(db);
3409 bwidth = updateButtons(opt.buttons);
3410 this.updateText(opt.msg);
3412 d.el.addClass(opt.cls);
3414 d.proxyDrag = opt.proxyDrag === true;
3415 d.modal = opt.modal !== false;
3416 d.mask = opt.modal !== false ? mask : false;
3418 // force it to the end of the z-index stack so it gets a cursor in FF
3419 document.body.appendChild(dlg.el.dom);
3420 d.animateTarget = null;
3421 d.show(options.animEl);
3427 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3428 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3429 * and closing the message box when the process is complete.
3430 * @param {String} title The title bar text
3431 * @param {String} msg The message box body text
3432 * @return {Roo.MessageBox} This message box
3434 progress : function(title, msg){
3441 minWidth: this.minProgressWidth,
3448 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3449 * If a callback function is passed it will be called after the user clicks the button, and the
3450 * id of the button that was clicked will be passed as the only parameter to the callback
3451 * (could also be the top-right close button).
3452 * @param {String} title The title bar text
3453 * @param {String} msg The message box body text
3454 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3455 * @param {Object} scope (optional) The scope of the callback function
3456 * @return {Roo.MessageBox} This message box
3458 alert : function(title, msg, fn, scope)
3473 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3474 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3475 * You are responsible for closing the message box when the process is complete.
3476 * @param {String} msg The message box body text
3477 * @param {String} title (optional) The title bar text
3478 * @return {Roo.MessageBox} This message box
3480 wait : function(msg, title){
3491 waitTimer = Roo.TaskMgr.start({
3493 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3501 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3502 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3503 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3504 * @param {String} title The title bar text
3505 * @param {String} msg The message box body text
3506 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3507 * @param {Object} scope (optional) The scope of the callback function
3508 * @return {Roo.MessageBox} This message box
3510 confirm : function(title, msg, fn, scope){
3514 buttons: this.YESNO,
3523 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3524 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3525 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3526 * (could also be the top-right close button) and the text that was entered will be passed as the two
3527 * parameters to the callback.
3528 * @param {String} title The title bar text
3529 * @param {String} msg The message box body text
3530 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3531 * @param {Object} scope (optional) The scope of the callback function
3532 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3533 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3534 * @return {Roo.MessageBox} This message box
3536 prompt : function(title, msg, fn, scope, multiline){
3540 buttons: this.OKCANCEL,
3545 multiline: multiline,
3552 * Button config that displays a single OK button
3557 * Button config that displays Yes and No buttons
3560 YESNO : {yes:true, no:true},
3562 * Button config that displays OK and Cancel buttons
3565 OKCANCEL : {ok:true, cancel:true},
3567 * Button config that displays Yes, No and Cancel buttons
3570 YESNOCANCEL : {yes:true, no:true, cancel:true},
3573 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3576 defaultTextHeight : 75,
3578 * The maximum width in pixels of the message box (defaults to 600)
3583 * The minimum width in pixels of the message box (defaults to 100)
3588 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3589 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3592 minProgressWidth : 250,
3594 * An object containing the default button text strings that can be overriden for localized language support.
3595 * Supported properties are: ok, cancel, yes and no.
3596 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3609 * Shorthand for {@link Roo.MessageBox}
3611 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3612 Roo.Msg = Roo.Msg || Roo.MessageBox;
3621 * @class Roo.bootstrap.Navbar
3622 * @extends Roo.bootstrap.Component
3623 * Bootstrap Navbar class
3626 * Create a new Navbar
3627 * @param {Object} config The config object
3631 Roo.bootstrap.Navbar = function(config){
3632 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3636 * @event beforetoggle
3637 * Fire before toggle the menu
3638 * @param {Roo.EventObject} e
3640 "beforetoggle" : true
3644 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3653 getAutoCreate : function(){
3656 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3660 initEvents :function ()
3662 //Roo.log(this.el.select('.navbar-toggle',true));
3663 this.el.select('.navbar-toggle',true).on('click', function() {
3664 if(this.fireEvent('beforetoggle', this) !== false){
3665 this.el.select('.navbar-collapse',true).toggleClass('in');
3675 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3677 var size = this.el.getSize();
3678 this.maskEl.setSize(size.width, size.height);
3679 this.maskEl.enableDisplayMode("block");
3688 getChildContainer : function()
3690 if (this.el.select('.collapse').getCount()) {
3691 return this.el.select('.collapse',true).first();
3724 * @class Roo.bootstrap.NavSimplebar
3725 * @extends Roo.bootstrap.Navbar
3726 * Bootstrap Sidebar class
3728 * @cfg {Boolean} inverse is inverted color
3730 * @cfg {String} type (nav | pills | tabs)
3731 * @cfg {Boolean} arrangement stacked | justified
3732 * @cfg {String} align (left | right) alignment
3734 * @cfg {Boolean} main (true|false) main nav bar? default false
3735 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3737 * @cfg {String} tag (header|footer|nav|div) default is nav
3743 * Create a new Sidebar
3744 * @param {Object} config The config object
3748 Roo.bootstrap.NavSimplebar = function(config){
3749 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3752 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3768 getAutoCreate : function(){
3772 tag : this.tag || 'div',
3785 this.type = this.type || 'nav';
3786 if (['tabs','pills'].indexOf(this.type)!==-1) {
3787 cfg.cn[0].cls += ' nav-' + this.type
3791 if (this.type!=='nav') {
3792 Roo.log('nav type must be nav/tabs/pills')
3794 cfg.cn[0].cls += ' navbar-nav'
3800 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3801 cfg.cn[0].cls += ' nav-' + this.arrangement;
3805 if (this.align === 'right') {
3806 cfg.cn[0].cls += ' navbar-right';
3810 cfg.cls += ' navbar-inverse';
3837 * @class Roo.bootstrap.NavHeaderbar
3838 * @extends Roo.bootstrap.NavSimplebar
3839 * Bootstrap Sidebar class
3841 * @cfg {String} brand what is brand
3842 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3843 * @cfg {String} brand_href href of the brand
3844 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3845 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3846 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3847 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3850 * Create a new Sidebar
3851 * @param {Object} config The config object
3855 Roo.bootstrap.NavHeaderbar = function(config){
3856 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3860 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3867 desktopCenter : false,
3870 getAutoCreate : function(){
3873 tag: this.nav || 'nav',
3880 if (this.desktopCenter) {
3881 cn.push({cls : 'container', cn : []});
3888 cls: 'navbar-header',
3893 cls: 'navbar-toggle',
3894 'data-toggle': 'collapse',
3899 html: 'Toggle navigation'
3921 cls: 'collapse navbar-collapse',
3925 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3927 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3928 cfg.cls += ' navbar-' + this.position;
3930 // tag can override this..
3932 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3935 if (this.brand !== '') {
3938 href: this.brand_href ? this.brand_href : '#',
3939 cls: 'navbar-brand',
3947 cfg.cls += ' main-nav';
3955 getHeaderChildContainer : function()
3957 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3958 return this.el.select('.navbar-header',true).first();
3961 return this.getChildContainer();
3965 initEvents : function()
3967 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3969 if (this.autohide) {
3974 Roo.get(document).on('scroll',function(e) {
3975 var ns = Roo.get(document).getScroll().top;
3976 var os = prevScroll;
3980 ft.removeClass('slideDown');
3981 ft.addClass('slideUp');
3984 ft.removeClass('slideUp');
3985 ft.addClass('slideDown');
4006 * @class Roo.bootstrap.NavSidebar
4007 * @extends Roo.bootstrap.Navbar
4008 * Bootstrap Sidebar class
4011 * Create a new Sidebar
4012 * @param {Object} config The config object
4016 Roo.bootstrap.NavSidebar = function(config){
4017 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4020 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4022 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4024 getAutoCreate : function(){
4029 cls: 'sidebar sidebar-nav'
4051 * @class Roo.bootstrap.NavGroup
4052 * @extends Roo.bootstrap.Component
4053 * Bootstrap NavGroup class
4054 * @cfg {String} align (left|right)
4055 * @cfg {Boolean} inverse
4056 * @cfg {String} type (nav|pills|tab) default nav
4057 * @cfg {String} navId - reference Id for navbar.
4061 * Create a new nav group
4062 * @param {Object} config The config object
4065 Roo.bootstrap.NavGroup = function(config){
4066 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4069 Roo.bootstrap.NavGroup.register(this);
4073 * Fires when the active item changes
4074 * @param {Roo.bootstrap.NavGroup} this
4075 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4076 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4083 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4094 getAutoCreate : function()
4096 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4103 if (['tabs','pills'].indexOf(this.type)!==-1) {
4104 cfg.cls += ' nav-' + this.type
4106 if (this.type!=='nav') {
4107 Roo.log('nav type must be nav/tabs/pills')
4109 cfg.cls += ' navbar-nav'
4112 if (this.parent() && this.parent().sidebar) {
4115 cls: 'dashboard-menu sidebar-menu'
4121 if (this.form === true) {
4127 if (this.align === 'right') {
4128 cfg.cls += ' navbar-right';
4130 cfg.cls += ' navbar-left';
4134 if (this.align === 'right') {
4135 cfg.cls += ' navbar-right';
4139 cfg.cls += ' navbar-inverse';
4147 * sets the active Navigation item
4148 * @param {Roo.bootstrap.NavItem} the new current navitem
4150 setActiveItem : function(item)
4153 Roo.each(this.navItems, function(v){
4158 v.setActive(false, true);
4165 item.setActive(true, true);
4166 this.fireEvent('changed', this, item, prev);
4171 * gets the active Navigation item
4172 * @return {Roo.bootstrap.NavItem} the current navitem
4174 getActive : function()
4178 Roo.each(this.navItems, function(v){
4189 indexOfNav : function()
4193 Roo.each(this.navItems, function(v,i){
4204 * adds a Navigation item
4205 * @param {Roo.bootstrap.NavItem} the navitem to add
4207 addItem : function(cfg)
4209 var cn = new Roo.bootstrap.NavItem(cfg);
4211 cn.parentId = this.id;
4212 cn.onRender(this.el, null);
4216 * register a Navigation item
4217 * @param {Roo.bootstrap.NavItem} the navitem to add
4219 register : function(item)
4221 this.navItems.push( item);
4222 item.navId = this.navId;
4227 * clear all the Navigation item
4230 clearAll : function()
4233 this.el.dom.innerHTML = '';
4236 getNavItem: function(tabId)
4239 Roo.each(this.navItems, function(e) {
4240 if (e.tabId == tabId) {
4250 setActiveNext : function()
4252 var i = this.indexOfNav(this.getActive());
4253 if (i > this.navItems.length) {
4256 this.setActiveItem(this.navItems[i+1]);
4258 setActivePrev : function()
4260 var i = this.indexOfNav(this.getActive());
4264 this.setActiveItem(this.navItems[i-1]);
4266 clearWasActive : function(except) {
4267 Roo.each(this.navItems, function(e) {
4268 if (e.tabId != except.tabId && e.was_active) {
4269 e.was_active = false;
4276 getWasActive : function ()
4279 Roo.each(this.navItems, function(e) {
4294 Roo.apply(Roo.bootstrap.NavGroup, {
4298 * register a Navigation Group
4299 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4301 register : function(navgrp)
4303 this.groups[navgrp.navId] = navgrp;
4307 * fetch a Navigation Group based on the navigation ID
4308 * @param {string} the navgroup to add
4309 * @returns {Roo.bootstrap.NavGroup} the navgroup
4311 get: function(navId) {
4312 if (typeof(this.groups[navId]) == 'undefined') {
4314 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4316 return this.groups[navId] ;
4331 * @class Roo.bootstrap.NavItem
4332 * @extends Roo.bootstrap.Component
4333 * Bootstrap Navbar.NavItem class
4334 * @cfg {String} href link to
4335 * @cfg {String} html content of button
4336 * @cfg {String} badge text inside badge
4337 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4338 * @cfg {String} glyphicon name of glyphicon
4339 * @cfg {String} icon name of font awesome icon
4340 * @cfg {Boolean} active Is item active
4341 * @cfg {Boolean} disabled Is item disabled
4343 * @cfg {Boolean} preventDefault (true | false) default false
4344 * @cfg {String} tabId the tab that this item activates.
4345 * @cfg {String} tagtype (a|span) render as a href or span?
4346 * @cfg {Boolean} animateRef (true|false) link to element default false
4349 * Create a new Navbar Item
4350 * @param {Object} config The config object
4352 Roo.bootstrap.NavItem = function(config){
4353 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4358 * The raw click event for the entire grid.
4359 * @param {Roo.EventObject} e
4364 * Fires when the active item active state changes
4365 * @param {Roo.bootstrap.NavItem} this
4366 * @param {boolean} state the new state
4372 * Fires when scroll to element
4373 * @param {Roo.bootstrap.NavItem} this
4374 * @param {Object} options
4375 * @param {Roo.EventObject} e
4383 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4391 preventDefault : false,
4398 getAutoCreate : function(){
4407 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4409 if (this.disabled) {
4410 cfg.cls += ' disabled';
4413 if (this.href || this.html || this.glyphicon || this.icon) {
4417 href : this.href || "#",
4418 html: this.html || ''
4423 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4426 if(this.glyphicon) {
4427 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4432 cfg.cn[0].html += " <span class='caret'></span>";
4436 if (this.badge !== '') {
4438 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4446 initEvents: function()
4448 if (typeof (this.menu) != 'undefined') {
4449 this.menu.parentType = this.xtype;
4450 this.menu.triggerEl = this.el;
4451 this.menu = this.addxtype(Roo.apply({}, this.menu));
4454 this.el.select('a',true).on('click', this.onClick, this);
4456 if(this.tagtype == 'span'){
4457 this.el.select('span',true).on('click', this.onClick, this);
4460 // at this point parent should be available..
4461 this.parent().register(this);
4464 onClick : function(e)
4466 if (e.getTarget('.dropdown-menu-item')) {
4467 // did you click on a menu itemm.... - then don't trigger onclick..
4472 this.preventDefault ||
4475 Roo.log("NavItem - prevent Default?");
4479 if (this.disabled) {
4483 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4484 if (tg && tg.transition) {
4485 Roo.log("waiting for the transitionend");
4491 //Roo.log("fire event clicked");
4492 if(this.fireEvent('click', this, e) === false){
4496 if(this.tagtype == 'span'){
4500 //Roo.log(this.href);
4501 var ael = this.el.select('a',true).first();
4504 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4505 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4506 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4507 return; // ignore... - it's a 'hash' to another page.
4509 Roo.log("NavItem - prevent Default?");
4511 this.scrollToElement(e);
4515 var p = this.parent();
4517 if (['tabs','pills'].indexOf(p.type)!==-1) {
4518 if (typeof(p.setActiveItem) !== 'undefined') {
4519 p.setActiveItem(this);
4523 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4524 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4525 // remove the collapsed menu expand...
4526 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4530 isActive: function () {
4533 setActive : function(state, fire, is_was_active)
4535 if (this.active && !state && this.navId) {
4536 this.was_active = true;
4537 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4539 nv.clearWasActive(this);
4543 this.active = state;
4546 this.el.removeClass('active');
4547 } else if (!this.el.hasClass('active')) {
4548 this.el.addClass('active');
4551 this.fireEvent('changed', this, state);
4554 // show a panel if it's registered and related..
4556 if (!this.navId || !this.tabId || !state || is_was_active) {
4560 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4564 var pan = tg.getPanelByName(this.tabId);
4568 // if we can not flip to new panel - go back to old nav highlight..
4569 if (false == tg.showPanel(pan)) {
4570 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4572 var onav = nv.getWasActive();
4574 onav.setActive(true, false, true);
4583 // this should not be here...
4584 setDisabled : function(state)
4586 this.disabled = state;
4588 this.el.removeClass('disabled');
4589 } else if (!this.el.hasClass('disabled')) {
4590 this.el.addClass('disabled');
4596 * Fetch the element to display the tooltip on.
4597 * @return {Roo.Element} defaults to this.el
4599 tooltipEl : function()
4601 return this.el.select('' + this.tagtype + '', true).first();
4604 scrollToElement : function(e)
4606 var c = document.body;
4609 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4611 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4612 c = document.documentElement;
4615 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4621 var o = target.calcOffsetsTo(c);
4628 this.fireEvent('scrollto', this, options, e);
4630 Roo.get(c).scrollTo('top', options.value, true);
4643 * <span> icon </span>
4644 * <span> text </span>
4645 * <span>badge </span>
4649 * @class Roo.bootstrap.NavSidebarItem
4650 * @extends Roo.bootstrap.NavItem
4651 * Bootstrap Navbar.NavSidebarItem class
4652 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4653 * {Boolean} open is the menu open
4654 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4655 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4656 * {String} buttonSize (sm|md|lg)the extra classes for the button
4657 * {Boolean} showArrow show arrow next to the text (default true)
4659 * Create a new Navbar Button
4660 * @param {Object} config The config object
4662 Roo.bootstrap.NavSidebarItem = function(config){
4663 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4668 * The raw click event for the entire grid.
4669 * @param {Roo.EventObject} e
4674 * Fires when the active item active state changes
4675 * @param {Roo.bootstrap.NavSidebarItem} this
4676 * @param {boolean} state the new state
4684 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4686 badgeWeight : 'default',
4692 buttonWeight : 'default',
4698 getAutoCreate : function(){
4703 href : this.href || '#',
4709 if(this.buttonView){
4712 href : this.href || '#',
4713 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4726 cfg.cls += ' active';
4729 if (this.disabled) {
4730 cfg.cls += ' disabled';
4733 cfg.cls += ' open x-open';
4736 if (this.glyphicon || this.icon) {
4737 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4738 a.cn.push({ tag : 'i', cls : c }) ;
4741 if(!this.buttonView){
4744 html : this.html || ''
4751 if (this.badge !== '') {
4752 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4758 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4761 a.cls += ' dropdown-toggle treeview' ;
4767 initEvents : function()
4769 if (typeof (this.menu) != 'undefined') {
4770 this.menu.parentType = this.xtype;
4771 this.menu.triggerEl = this.el;
4772 this.menu = this.addxtype(Roo.apply({}, this.menu));
4775 this.el.on('click', this.onClick, this);
4777 if(this.badge !== ''){
4778 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4783 onClick : function(e)
4790 if(this.preventDefault){
4794 this.fireEvent('click', this);
4797 disable : function()
4799 this.setDisabled(true);
4804 this.setDisabled(false);
4807 setDisabled : function(state)
4809 if(this.disabled == state){
4813 this.disabled = state;
4816 this.el.addClass('disabled');
4820 this.el.removeClass('disabled');
4825 setActive : function(state)
4827 if(this.active == state){
4831 this.active = state;
4834 this.el.addClass('active');
4838 this.el.removeClass('active');
4843 isActive: function ()
4848 setBadge : function(str)
4854 this.badgeEl.dom.innerHTML = str;
4871 * @class Roo.bootstrap.Row
4872 * @extends Roo.bootstrap.Component
4873 * Bootstrap Row class (contains columns...)
4877 * @param {Object} config The config object
4880 Roo.bootstrap.Row = function(config){
4881 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4884 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4886 getAutoCreate : function(){
4905 * @class Roo.bootstrap.Element
4906 * @extends Roo.bootstrap.Component
4907 * Bootstrap Element class
4908 * @cfg {String} html contents of the element
4909 * @cfg {String} tag tag of the element
4910 * @cfg {String} cls class of the element
4911 * @cfg {Boolean} preventDefault (true|false) default false
4912 * @cfg {Boolean} clickable (true|false) default false
4915 * Create a new Element
4916 * @param {Object} config The config object
4919 Roo.bootstrap.Element = function(config){
4920 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4926 * When a element is chick
4927 * @param {Roo.bootstrap.Element} this
4928 * @param {Roo.EventObject} e
4934 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4939 preventDefault: false,
4942 getAutoCreate : function(){
4946 // cls: this.cls, double assign in parent class Component.js :: onRender
4953 initEvents: function()
4955 Roo.bootstrap.Element.superclass.initEvents.call(this);
4958 this.el.on('click', this.onClick, this);
4963 onClick : function(e)
4965 if(this.preventDefault){
4969 this.fireEvent('click', this, e);
4972 getValue : function()
4974 return this.el.dom.innerHTML;
4977 setValue : function(value)
4979 this.el.dom.innerHTML = value;
4994 * @class Roo.bootstrap.Pagination
4995 * @extends Roo.bootstrap.Component
4996 * Bootstrap Pagination class
4997 * @cfg {String} size xs | sm | md | lg
4998 * @cfg {Boolean} inverse false | true
5001 * Create a new Pagination
5002 * @param {Object} config The config object
5005 Roo.bootstrap.Pagination = function(config){
5006 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5009 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5015 getAutoCreate : function(){
5021 cfg.cls += ' inverse';
5027 cfg.cls += " " + this.cls;
5045 * @class Roo.bootstrap.PaginationItem
5046 * @extends Roo.bootstrap.Component
5047 * Bootstrap PaginationItem class
5048 * @cfg {String} html text
5049 * @cfg {String} href the link
5050 * @cfg {Boolean} preventDefault (true | false) default true
5051 * @cfg {Boolean} active (true | false) default false
5052 * @cfg {Boolean} disabled default false
5056 * Create a new PaginationItem
5057 * @param {Object} config The config object
5061 Roo.bootstrap.PaginationItem = function(config){
5062 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5067 * The raw click event for the entire grid.
5068 * @param {Roo.EventObject} e
5074 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5078 preventDefault: true,
5083 getAutoCreate : function(){
5089 href : this.href ? this.href : '#',
5090 html : this.html ? this.html : ''
5100 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5104 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5110 initEvents: function() {
5112 this.el.on('click', this.onClick, this);
5115 onClick : function(e)
5117 Roo.log('PaginationItem on click ');
5118 if(this.preventDefault){
5126 this.fireEvent('click', this, e);
5142 * @class Roo.bootstrap.Slider
5143 * @extends Roo.bootstrap.Component
5144 * Bootstrap Slider class
5147 * Create a new Slider
5148 * @param {Object} config The config object
5151 Roo.bootstrap.Slider = function(config){
5152 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5155 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5157 getAutoCreate : function(){
5161 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5165 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5177 * Ext JS Library 1.1.1
5178 * Copyright(c) 2006-2007, Ext JS, LLC.
5180 * Originally Released Under LGPL - original licence link has changed is not relivant.
5183 * <script type="text/javascript">
5188 * @class Roo.grid.ColumnModel
5189 * @extends Roo.util.Observable
5190 * This is the default implementation of a ColumnModel used by the Grid. It defines
5191 * the columns in the grid.
5194 var colModel = new Roo.grid.ColumnModel([
5195 {header: "Ticker", width: 60, sortable: true, locked: true},
5196 {header: "Company Name", width: 150, sortable: true},
5197 {header: "Market Cap.", width: 100, sortable: true},
5198 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5199 {header: "Employees", width: 100, sortable: true, resizable: false}
5204 * The config options listed for this class are options which may appear in each
5205 * individual column definition.
5206 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5208 * @param {Object} config An Array of column config objects. See this class's
5209 * config objects for details.
5211 Roo.grid.ColumnModel = function(config){
5213 * The config passed into the constructor
5215 this.config = config;
5218 // if no id, create one
5219 // if the column does not have a dataIndex mapping,
5220 // map it to the order it is in the config
5221 for(var i = 0, len = config.length; i < len; i++){
5223 if(typeof c.dataIndex == "undefined"){
5226 if(typeof c.renderer == "string"){
5227 c.renderer = Roo.util.Format[c.renderer];
5229 if(typeof c.id == "undefined"){
5232 if(c.editor && c.editor.xtype){
5233 c.editor = Roo.factory(c.editor, Roo.grid);
5235 if(c.editor && c.editor.isFormField){
5236 c.editor = new Roo.grid.GridEditor(c.editor);
5238 this.lookup[c.id] = c;
5242 * The width of columns which have no width specified (defaults to 100)
5245 this.defaultWidth = 100;
5248 * Default sortable of columns which have no sortable specified (defaults to false)
5251 this.defaultSortable = false;
5255 * @event widthchange
5256 * Fires when the width of a column changes.
5257 * @param {ColumnModel} this
5258 * @param {Number} columnIndex The column index
5259 * @param {Number} newWidth The new width
5261 "widthchange": true,
5263 * @event headerchange
5264 * Fires when the text of a header changes.
5265 * @param {ColumnModel} this
5266 * @param {Number} columnIndex The column index
5267 * @param {Number} newText The new header text
5269 "headerchange": true,
5271 * @event hiddenchange
5272 * Fires when a column is hidden or "unhidden".
5273 * @param {ColumnModel} this
5274 * @param {Number} columnIndex The column index
5275 * @param {Boolean} hidden true if hidden, false otherwise
5277 "hiddenchange": true,
5279 * @event columnmoved
5280 * Fires when a column is moved.
5281 * @param {ColumnModel} this
5282 * @param {Number} oldIndex
5283 * @param {Number} newIndex
5285 "columnmoved" : true,
5287 * @event columlockchange
5288 * Fires when a column's locked state is changed
5289 * @param {ColumnModel} this
5290 * @param {Number} colIndex
5291 * @param {Boolean} locked true if locked
5293 "columnlockchange" : true
5295 Roo.grid.ColumnModel.superclass.constructor.call(this);
5297 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5299 * @cfg {String} header The header text to display in the Grid view.
5302 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5303 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5304 * specified, the column's index is used as an index into the Record's data Array.
5307 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5308 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5311 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5312 * Defaults to the value of the {@link #defaultSortable} property.
5313 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5316 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5319 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5322 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5325 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5328 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5329 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5330 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5331 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5334 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5337 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5340 * @cfg {String} cursor (Optional)
5343 * @cfg {String} tooltip (Optional)
5346 * @cfg {Number} xs (Optional)
5349 * @cfg {Number} sm (Optional)
5352 * @cfg {Number} md (Optional)
5355 * @cfg {Number} lg (Optional)
5358 * Returns the id of the column at the specified index.
5359 * @param {Number} index The column index
5360 * @return {String} the id
5362 getColumnId : function(index){
5363 return this.config[index].id;
5367 * Returns the column for a specified id.
5368 * @param {String} id The column id
5369 * @return {Object} the column
5371 getColumnById : function(id){
5372 return this.lookup[id];
5377 * Returns the column for a specified dataIndex.
5378 * @param {String} dataIndex The column dataIndex
5379 * @return {Object|Boolean} the column or false if not found
5381 getColumnByDataIndex: function(dataIndex){
5382 var index = this.findColumnIndex(dataIndex);
5383 return index > -1 ? this.config[index] : false;
5387 * Returns the index for a specified column id.
5388 * @param {String} id The column id
5389 * @return {Number} the index, or -1 if not found
5391 getIndexById : function(id){
5392 for(var i = 0, len = this.config.length; i < len; i++){
5393 if(this.config[i].id == id){
5401 * Returns the index for a specified column dataIndex.
5402 * @param {String} dataIndex The column dataIndex
5403 * @return {Number} the index, or -1 if not found
5406 findColumnIndex : function(dataIndex){
5407 for(var i = 0, len = this.config.length; i < len; i++){
5408 if(this.config[i].dataIndex == dataIndex){
5416 moveColumn : function(oldIndex, newIndex){
5417 var c = this.config[oldIndex];
5418 this.config.splice(oldIndex, 1);
5419 this.config.splice(newIndex, 0, c);
5420 this.dataMap = null;
5421 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5424 isLocked : function(colIndex){
5425 return this.config[colIndex].locked === true;
5428 setLocked : function(colIndex, value, suppressEvent){
5429 if(this.isLocked(colIndex) == value){
5432 this.config[colIndex].locked = value;
5434 this.fireEvent("columnlockchange", this, colIndex, value);
5438 getTotalLockedWidth : function(){
5440 for(var i = 0; i < this.config.length; i++){
5441 if(this.isLocked(i) && !this.isHidden(i)){
5442 this.totalWidth += this.getColumnWidth(i);
5448 getLockedCount : function(){
5449 for(var i = 0, len = this.config.length; i < len; i++){
5450 if(!this.isLocked(i)){
5455 return this.config.length;
5459 * Returns the number of columns.
5462 getColumnCount : function(visibleOnly){
5463 if(visibleOnly === true){
5465 for(var i = 0, len = this.config.length; i < len; i++){
5466 if(!this.isHidden(i)){
5472 return this.config.length;
5476 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5477 * @param {Function} fn
5478 * @param {Object} scope (optional)
5479 * @return {Array} result
5481 getColumnsBy : function(fn, scope){
5483 for(var i = 0, len = this.config.length; i < len; i++){
5484 var c = this.config[i];
5485 if(fn.call(scope||this, c, i) === true){
5493 * Returns true if the specified column is sortable.
5494 * @param {Number} col The column index
5497 isSortable : function(col){
5498 if(typeof this.config[col].sortable == "undefined"){
5499 return this.defaultSortable;
5501 return this.config[col].sortable;
5505 * Returns the rendering (formatting) function defined for the column.
5506 * @param {Number} col The column index.
5507 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5509 getRenderer : function(col){
5510 if(!this.config[col].renderer){
5511 return Roo.grid.ColumnModel.defaultRenderer;
5513 return this.config[col].renderer;
5517 * Sets the rendering (formatting) function for a column.
5518 * @param {Number} col The column index
5519 * @param {Function} fn The function to use to process the cell's raw data
5520 * to return HTML markup for the grid view. The render function is called with
5521 * the following parameters:<ul>
5522 * <li>Data value.</li>
5523 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5524 * <li>css A CSS style string to apply to the table cell.</li>
5525 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5526 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5527 * <li>Row index</li>
5528 * <li>Column index</li>
5529 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5531 setRenderer : function(col, fn){
5532 this.config[col].renderer = fn;
5536 * Returns the width for the specified column.
5537 * @param {Number} col The column index
5540 getColumnWidth : function(col){
5541 return this.config[col].width * 1 || this.defaultWidth;
5545 * Sets the width for a column.
5546 * @param {Number} col The column index
5547 * @param {Number} width The new width
5549 setColumnWidth : function(col, width, suppressEvent){
5550 this.config[col].width = width;
5551 this.totalWidth = null;
5553 this.fireEvent("widthchange", this, col, width);
5558 * Returns the total width of all columns.
5559 * @param {Boolean} includeHidden True to include hidden column widths
5562 getTotalWidth : function(includeHidden){
5563 if(!this.totalWidth){
5564 this.totalWidth = 0;
5565 for(var i = 0, len = this.config.length; i < len; i++){
5566 if(includeHidden || !this.isHidden(i)){
5567 this.totalWidth += this.getColumnWidth(i);
5571 return this.totalWidth;
5575 * Returns the header for the specified column.
5576 * @param {Number} col The column index
5579 getColumnHeader : function(col){
5580 return this.config[col].header;
5584 * Sets the header for a column.
5585 * @param {Number} col The column index
5586 * @param {String} header The new header
5588 setColumnHeader : function(col, header){
5589 this.config[col].header = header;
5590 this.fireEvent("headerchange", this, col, header);
5594 * Returns the tooltip for the specified column.
5595 * @param {Number} col The column index
5598 getColumnTooltip : function(col){
5599 return this.config[col].tooltip;
5602 * Sets the tooltip for a column.
5603 * @param {Number} col The column index
5604 * @param {String} tooltip The new tooltip
5606 setColumnTooltip : function(col, tooltip){
5607 this.config[col].tooltip = tooltip;
5611 * Returns the dataIndex for the specified column.
5612 * @param {Number} col The column index
5615 getDataIndex : function(col){
5616 return this.config[col].dataIndex;
5620 * Sets the dataIndex for a column.
5621 * @param {Number} col The column index
5622 * @param {Number} dataIndex The new dataIndex
5624 setDataIndex : function(col, dataIndex){
5625 this.config[col].dataIndex = dataIndex;
5631 * Returns true if the cell is editable.
5632 * @param {Number} colIndex The column index
5633 * @param {Number} rowIndex The row index - this is nto actually used..?
5636 isCellEditable : function(colIndex, rowIndex){
5637 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5641 * Returns the editor defined for the cell/column.
5642 * return false or null to disable editing.
5643 * @param {Number} colIndex The column index
5644 * @param {Number} rowIndex The row index
5647 getCellEditor : function(colIndex, rowIndex){
5648 return this.config[colIndex].editor;
5652 * Sets if a column is editable.
5653 * @param {Number} col The column index
5654 * @param {Boolean} editable True if the column is editable
5656 setEditable : function(col, editable){
5657 this.config[col].editable = editable;
5662 * Returns true if the column is hidden.
5663 * @param {Number} colIndex The column index
5666 isHidden : function(colIndex){
5667 return this.config[colIndex].hidden;
5672 * Returns true if the column width cannot be changed
5674 isFixed : function(colIndex){
5675 return this.config[colIndex].fixed;
5679 * Returns true if the column can be resized
5682 isResizable : function(colIndex){
5683 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5686 * Sets if a column is hidden.
5687 * @param {Number} colIndex The column index
5688 * @param {Boolean} hidden True if the column is hidden
5690 setHidden : function(colIndex, hidden){
5691 this.config[colIndex].hidden = hidden;
5692 this.totalWidth = null;
5693 this.fireEvent("hiddenchange", this, colIndex, hidden);
5697 * Sets the editor for a column.
5698 * @param {Number} col The column index
5699 * @param {Object} editor The editor object
5701 setEditor : function(col, editor){
5702 this.config[col].editor = editor;
5706 Roo.grid.ColumnModel.defaultRenderer = function(value)
5708 if(typeof value == "object") {
5711 if(typeof value == "string" && value.length < 1){
5715 return String.format("{0}", value);
5718 // Alias for backwards compatibility
5719 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5722 * Ext JS Library 1.1.1
5723 * Copyright(c) 2006-2007, Ext JS, LLC.
5725 * Originally Released Under LGPL - original licence link has changed is not relivant.
5728 * <script type="text/javascript">
5732 * @class Roo.LoadMask
5733 * A simple utility class for generically masking elements while loading data. If the element being masked has
5734 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5735 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5736 * element's UpdateManager load indicator and will be destroyed after the initial load.
5738 * Create a new LoadMask
5739 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5740 * @param {Object} config The config object
5742 Roo.LoadMask = function(el, config){
5743 this.el = Roo.get(el);
5744 Roo.apply(this, config);
5746 this.store.on('beforeload', this.onBeforeLoad, this);
5747 this.store.on('load', this.onLoad, this);
5748 this.store.on('loadexception', this.onLoadException, this);
5749 this.removeMask = false;
5751 var um = this.el.getUpdateManager();
5752 um.showLoadIndicator = false; // disable the default indicator
5753 um.on('beforeupdate', this.onBeforeLoad, this);
5754 um.on('update', this.onLoad, this);
5755 um.on('failure', this.onLoad, this);
5756 this.removeMask = true;
5760 Roo.LoadMask.prototype = {
5762 * @cfg {Boolean} removeMask
5763 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5764 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5768 * The text to display in a centered loading message box (defaults to 'Loading...')
5772 * @cfg {String} msgCls
5773 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5775 msgCls : 'x-mask-loading',
5778 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5784 * Disables the mask to prevent it from being displayed
5786 disable : function(){
5787 this.disabled = true;
5791 * Enables the mask so that it can be displayed
5793 enable : function(){
5794 this.disabled = false;
5797 onLoadException : function()
5801 if (typeof(arguments[3]) != 'undefined') {
5802 Roo.MessageBox.alert("Error loading",arguments[3]);
5806 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5807 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5814 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5819 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5823 onBeforeLoad : function(){
5825 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5830 destroy : function(){
5832 this.store.un('beforeload', this.onBeforeLoad, this);
5833 this.store.un('load', this.onLoad, this);
5834 this.store.un('loadexception', this.onLoadException, this);
5836 var um = this.el.getUpdateManager();
5837 um.un('beforeupdate', this.onBeforeLoad, this);
5838 um.un('update', this.onLoad, this);
5839 um.un('failure', this.onLoad, this);
5850 * @class Roo.bootstrap.Table
5851 * @extends Roo.bootstrap.Component
5852 * Bootstrap Table class
5853 * @cfg {String} cls table class
5854 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5855 * @cfg {String} bgcolor Specifies the background color for a table
5856 * @cfg {Number} border Specifies whether the table cells should have borders or not
5857 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5858 * @cfg {Number} cellspacing Specifies the space between cells
5859 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5860 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5861 * @cfg {String} sortable Specifies that the table should be sortable
5862 * @cfg {String} summary Specifies a summary of the content of a table
5863 * @cfg {Number} width Specifies the width of a table
5864 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5866 * @cfg {boolean} striped Should the rows be alternative striped
5867 * @cfg {boolean} bordered Add borders to the table
5868 * @cfg {boolean} hover Add hover highlighting
5869 * @cfg {boolean} condensed Format condensed
5870 * @cfg {boolean} responsive Format condensed
5871 * @cfg {Boolean} loadMask (true|false) default false
5872 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5873 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5874 * @cfg {Boolean} rowSelection (true|false) default false
5875 * @cfg {Boolean} cellSelection (true|false) default false
5876 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5877 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5878 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5882 * Create a new Table
5883 * @param {Object} config The config object
5886 Roo.bootstrap.Table = function(config){
5887 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5892 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5893 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5894 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5895 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5897 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5899 this.sm.grid = this;
5900 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5901 this.sm = this.selModel;
5902 this.sm.xmodule = this.xmodule || false;
5905 if (this.cm && typeof(this.cm.config) == 'undefined') {
5906 this.colModel = new Roo.grid.ColumnModel(this.cm);
5907 this.cm = this.colModel;
5908 this.cm.xmodule = this.xmodule || false;
5911 this.store= Roo.factory(this.store, Roo.data);
5912 this.ds = this.store;
5913 this.ds.xmodule = this.xmodule || false;
5916 if (this.footer && this.store) {
5917 this.footer.dataSource = this.ds;
5918 this.footer = Roo.factory(this.footer);
5925 * Fires when a cell is clicked
5926 * @param {Roo.bootstrap.Table} this
5927 * @param {Roo.Element} el
5928 * @param {Number} rowIndex
5929 * @param {Number} columnIndex
5930 * @param {Roo.EventObject} e
5934 * @event celldblclick
5935 * Fires when a cell is double clicked
5936 * @param {Roo.bootstrap.Table} this
5937 * @param {Roo.Element} el
5938 * @param {Number} rowIndex
5939 * @param {Number} columnIndex
5940 * @param {Roo.EventObject} e
5942 "celldblclick" : true,
5945 * Fires when a row is clicked
5946 * @param {Roo.bootstrap.Table} this
5947 * @param {Roo.Element} el
5948 * @param {Number} rowIndex
5949 * @param {Roo.EventObject} e
5953 * @event rowdblclick
5954 * Fires when a row is double clicked
5955 * @param {Roo.bootstrap.Table} this
5956 * @param {Roo.Element} el
5957 * @param {Number} rowIndex
5958 * @param {Roo.EventObject} e
5960 "rowdblclick" : true,
5963 * Fires when a mouseover occur
5964 * @param {Roo.bootstrap.Table} this
5965 * @param {Roo.Element} el
5966 * @param {Number} rowIndex
5967 * @param {Number} columnIndex
5968 * @param {Roo.EventObject} e
5973 * Fires when a mouseout occur
5974 * @param {Roo.bootstrap.Table} this
5975 * @param {Roo.Element} el
5976 * @param {Number} rowIndex
5977 * @param {Number} columnIndex
5978 * @param {Roo.EventObject} e
5983 * Fires when a row is rendered, so you can change add a style to it.
5984 * @param {Roo.bootstrap.Table} this
5985 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5989 * @event rowsrendered
5990 * Fires when all the rows have been rendered
5991 * @param {Roo.bootstrap.Table} this
5993 'rowsrendered' : true,
5995 * @event contextmenu
5996 * The raw contextmenu event for the entire grid.
5997 * @param {Roo.EventObject} e
5999 "contextmenu" : true,
6001 * @event rowcontextmenu
6002 * Fires when a row is right clicked
6003 * @param {Roo.bootstrap.Table} this
6004 * @param {Number} rowIndex
6005 * @param {Roo.EventObject} e
6007 "rowcontextmenu" : true,
6009 * @event cellcontextmenu
6010 * Fires when a cell is right clicked
6011 * @param {Roo.bootstrap.Table} this
6012 * @param {Number} rowIndex
6013 * @param {Number} cellIndex
6014 * @param {Roo.EventObject} e
6016 "cellcontextmenu" : true,
6018 * @event headercontextmenu
6019 * Fires when a header is right clicked
6020 * @param {Roo.bootstrap.Table} this
6021 * @param {Number} columnIndex
6022 * @param {Roo.EventObject} e
6024 "headercontextmenu" : true
6028 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6054 rowSelection : false,
6055 cellSelection : false,
6058 // Roo.Element - the tbody
6060 // Roo.Element - thead element
6063 container: false, // used by gridpanel...
6069 getAutoCreate : function()
6071 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6078 if (this.scrollBody) {
6079 cfg.cls += ' table-body-fixed';
6082 cfg.cls += ' table-striped';
6086 cfg.cls += ' table-hover';
6088 if (this.bordered) {
6089 cfg.cls += ' table-bordered';
6091 if (this.condensed) {
6092 cfg.cls += ' table-condensed';
6094 if (this.responsive) {
6095 cfg.cls += ' table-responsive';
6099 cfg.cls+= ' ' +this.cls;
6102 // this lot should be simplifed...
6105 cfg.align=this.align;
6108 cfg.bgcolor=this.bgcolor;
6111 cfg.border=this.border;
6113 if (this.cellpadding) {
6114 cfg.cellpadding=this.cellpadding;
6116 if (this.cellspacing) {
6117 cfg.cellspacing=this.cellspacing;
6120 cfg.frame=this.frame;
6123 cfg.rules=this.rules;
6125 if (this.sortable) {
6126 cfg.sortable=this.sortable;
6129 cfg.summary=this.summary;
6132 cfg.width=this.width;
6135 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6138 if(this.store || this.cm){
6139 if(this.headerShow){
6140 cfg.cn.push(this.renderHeader());
6143 cfg.cn.push(this.renderBody());
6145 if(this.footerShow){
6146 cfg.cn.push(this.renderFooter());
6148 // where does this come from?
6149 //cfg.cls+= ' TableGrid';
6152 return { cn : [ cfg ] };
6155 initEvents : function()
6157 if(!this.store || !this.cm){
6160 if (this.selModel) {
6161 this.selModel.initEvents();
6165 //Roo.log('initEvents with ds!!!!');
6167 this.mainBody = this.el.select('tbody', true).first();
6168 this.mainHead = this.el.select('thead', true).first();
6175 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6176 e.on('click', _this.sort, _this);
6179 this.mainBody.on("click", this.onClick, this);
6180 this.mainBody.on("dblclick", this.onDblClick, this);
6182 // why is this done????? = it breaks dialogs??
6183 //this.parent().el.setStyle('position', 'relative');
6187 this.footer.parentId = this.id;
6188 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6191 this.el.select('tfoot tr td').first().addClass('hide');
6196 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6199 this.store.on('load', this.onLoad, this);
6200 this.store.on('beforeload', this.onBeforeLoad, this);
6201 this.store.on('update', this.onUpdate, this);
6202 this.store.on('add', this.onAdd, this);
6203 this.store.on("clear", this.clear, this);
6205 this.el.on("contextmenu", this.onContextMenu, this);
6207 this.mainBody.on('scroll', this.onBodyScroll, this);
6209 this.cm.on("headerchange", this.onHeaderChange, this);
6211 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6215 onContextMenu : function(e, t)
6217 this.processEvent("contextmenu", e);
6220 processEvent : function(name, e)
6222 if (name != 'touchstart' ) {
6223 this.fireEvent(name, e);
6226 var t = e.getTarget();
6228 var cell = Roo.get(t);
6234 if(cell.findParent('tfoot', false, true)){
6238 if(cell.findParent('thead', false, true)){
6240 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6241 cell = Roo.get(t).findParent('th', false, true);
6243 Roo.log("failed to find th in thead?");
6244 Roo.log(e.getTarget());
6249 var cellIndex = cell.dom.cellIndex;
6251 var ename = name == 'touchstart' ? 'click' : name;
6252 this.fireEvent("header" + ename, this, cellIndex, e);
6257 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6258 cell = Roo.get(t).findParent('td', false, true);
6260 Roo.log("failed to find th in tbody?");
6261 Roo.log(e.getTarget());
6266 var row = cell.findParent('tr', false, true);
6267 var cellIndex = cell.dom.cellIndex;
6268 var rowIndex = row.dom.rowIndex - 1;
6272 this.fireEvent("row" + name, this, rowIndex, e);
6276 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6282 onMouseover : function(e, el)
6284 var cell = Roo.get(el);
6290 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6291 cell = cell.findParent('td', false, true);
6294 var row = cell.findParent('tr', false, true);
6295 var cellIndex = cell.dom.cellIndex;
6296 var rowIndex = row.dom.rowIndex - 1; // start from 0
6298 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6302 onMouseout : function(e, el)
6304 var cell = Roo.get(el);
6310 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6311 cell = cell.findParent('td', false, true);
6314 var row = cell.findParent('tr', false, true);
6315 var cellIndex = cell.dom.cellIndex;
6316 var rowIndex = row.dom.rowIndex - 1; // start from 0
6318 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6322 onClick : function(e, el)
6324 var cell = Roo.get(el);
6326 if(!cell || (!this.cellSelection && !this.rowSelection)){
6330 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6331 cell = cell.findParent('td', false, true);
6334 if(!cell || typeof(cell) == 'undefined'){
6338 var row = cell.findParent('tr', false, true);
6340 if(!row || typeof(row) == 'undefined'){
6344 var cellIndex = cell.dom.cellIndex;
6345 var rowIndex = this.getRowIndex(row);
6347 // why??? - should these not be based on SelectionModel?
6348 if(this.cellSelection){
6349 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6352 if(this.rowSelection){
6353 this.fireEvent('rowclick', this, row, rowIndex, e);
6359 onDblClick : function(e,el)
6361 var cell = Roo.get(el);
6363 if(!cell || (!this.cellSelection && !this.rowSelection)){
6367 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6368 cell = cell.findParent('td', false, true);
6371 if(!cell || typeof(cell) == 'undefined'){
6375 var row = cell.findParent('tr', false, true);
6377 if(!row || typeof(row) == 'undefined'){
6381 var cellIndex = cell.dom.cellIndex;
6382 var rowIndex = this.getRowIndex(row);
6384 if(this.cellSelection){
6385 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6388 if(this.rowSelection){
6389 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6393 sort : function(e,el)
6395 var col = Roo.get(el);
6397 if(!col.hasClass('sortable')){
6401 var sort = col.attr('sort');
6404 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6408 this.store.sortInfo = {field : sort, direction : dir};
6411 Roo.log("calling footer first");
6412 this.footer.onClick('first');
6415 this.store.load({ params : { start : 0 } });
6419 renderHeader : function()
6427 this.totalWidth = 0;
6429 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6431 var config = cm.config[i];
6435 cls : 'x-hcol-' + i,
6437 html: cm.getColumnHeader(i)
6442 if(typeof(config.sortable) != 'undefined' && config.sortable){
6444 c.html = '<i class="glyphicon"></i>' + c.html;
6447 if(typeof(config.lgHeader) != 'undefined'){
6448 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6451 if(typeof(config.mdHeader) != 'undefined'){
6452 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6455 if(typeof(config.smHeader) != 'undefined'){
6456 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6459 if(typeof(config.xsHeader) != 'undefined'){
6460 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6467 if(typeof(config.tooltip) != 'undefined'){
6468 c.tooltip = config.tooltip;
6471 if(typeof(config.colspan) != 'undefined'){
6472 c.colspan = config.colspan;
6475 if(typeof(config.hidden) != 'undefined' && config.hidden){
6476 c.style += ' display:none;';
6479 if(typeof(config.dataIndex) != 'undefined'){
6480 c.sort = config.dataIndex;
6485 if(typeof(config.align) != 'undefined' && config.align.length){
6486 c.style += ' text-align:' + config.align + ';';
6489 if(typeof(config.width) != 'undefined'){
6490 c.style += ' width:' + config.width + 'px;';
6491 this.totalWidth += config.width;
6493 this.totalWidth += 100; // assume minimum of 100 per column?
6496 if(typeof(config.cls) != 'undefined'){
6497 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6500 ['xs','sm','md','lg'].map(function(size){
6502 if(typeof(config[size]) == 'undefined'){
6506 if (!config[size]) { // 0 = hidden
6507 c.cls += ' hidden-' + size;
6511 c.cls += ' col-' + size + '-' + config[size];
6521 renderBody : function()
6531 colspan : this.cm.getColumnCount()
6541 renderFooter : function()
6551 colspan : this.cm.getColumnCount()
6565 // Roo.log('ds onload');
6570 var ds = this.store;
6572 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6573 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6574 if (_this.store.sortInfo) {
6576 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6577 e.select('i', true).addClass(['glyphicon-arrow-up']);
6580 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6581 e.select('i', true).addClass(['glyphicon-arrow-down']);
6586 var tbody = this.mainBody;
6588 if(ds.getCount() > 0){
6589 ds.data.each(function(d,rowIndex){
6590 var row = this.renderRow(cm, ds, rowIndex);
6592 tbody.createChild(row);
6596 if(row.cellObjects.length){
6597 Roo.each(row.cellObjects, function(r){
6598 _this.renderCellObject(r);
6605 Roo.each(this.el.select('tbody td', true).elements, function(e){
6606 e.on('mouseover', _this.onMouseover, _this);
6609 Roo.each(this.el.select('tbody td', true).elements, function(e){
6610 e.on('mouseout', _this.onMouseout, _this);
6612 this.fireEvent('rowsrendered', this);
6618 onUpdate : function(ds,record)
6620 this.refreshRow(record);
6624 onRemove : function(ds, record, index, isUpdate){
6625 if(isUpdate !== true){
6626 this.fireEvent("beforerowremoved", this, index, record);
6628 var bt = this.mainBody.dom;
6630 var rows = this.el.select('tbody > tr', true).elements;
6632 if(typeof(rows[index]) != 'undefined'){
6633 bt.removeChild(rows[index].dom);
6636 // if(bt.rows[index]){
6637 // bt.removeChild(bt.rows[index]);
6640 if(isUpdate !== true){
6641 //this.stripeRows(index);
6642 //this.syncRowHeights(index, index);
6644 this.fireEvent("rowremoved", this, index, record);
6648 onAdd : function(ds, records, rowIndex)
6650 //Roo.log('on Add called');
6651 // - note this does not handle multiple adding very well..
6652 var bt = this.mainBody.dom;
6653 for (var i =0 ; i < records.length;i++) {
6654 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6655 //Roo.log(records[i]);
6656 //Roo.log(this.store.getAt(rowIndex+i));
6657 this.insertRow(this.store, rowIndex + i, false);
6664 refreshRow : function(record){
6665 var ds = this.store, index;
6666 if(typeof record == 'number'){
6668 record = ds.getAt(index);
6670 index = ds.indexOf(record);
6672 this.insertRow(ds, index, true);
6674 this.onRemove(ds, record, index+1, true);
6676 //this.syncRowHeights(index, index);
6678 this.fireEvent("rowupdated", this, index, record);
6681 insertRow : function(dm, rowIndex, isUpdate){
6684 this.fireEvent("beforerowsinserted", this, rowIndex);
6686 //var s = this.getScrollState();
6687 var row = this.renderRow(this.cm, this.store, rowIndex);
6688 // insert before rowIndex..
6689 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6693 if(row.cellObjects.length){
6694 Roo.each(row.cellObjects, function(r){
6695 _this.renderCellObject(r);
6700 this.fireEvent("rowsinserted", this, rowIndex);
6701 //this.syncRowHeights(firstRow, lastRow);
6702 //this.stripeRows(firstRow);
6709 getRowDom : function(rowIndex)
6711 var rows = this.el.select('tbody > tr', true).elements;
6713 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6716 // returns the object tree for a tr..
6719 renderRow : function(cm, ds, rowIndex)
6721 var d = ds.getAt(rowIndex);
6725 cls : 'x-row-' + rowIndex,
6729 var cellObjects = [];
6731 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6732 var config = cm.config[i];
6734 var renderer = cm.getRenderer(i);
6738 if(typeof(renderer) !== 'undefined'){
6739 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6741 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6742 // and are rendered into the cells after the row is rendered - using the id for the element.
6744 if(typeof(value) === 'object'){
6754 rowIndex : rowIndex,
6759 this.fireEvent('rowclass', this, rowcfg);
6763 cls : rowcfg.rowClass + ' x-col-' + i,
6765 html: (typeof(value) === 'object') ? '' : value
6772 if(typeof(config.colspan) != 'undefined'){
6773 td.colspan = config.colspan;
6776 if(typeof(config.hidden) != 'undefined' && config.hidden){
6777 td.style += ' display:none;';
6780 if(typeof(config.align) != 'undefined' && config.align.length){
6781 td.style += ' text-align:' + config.align + ';';
6784 if(typeof(config.width) != 'undefined'){
6785 td.style += ' width:' + config.width + 'px;';
6788 if(typeof(config.cursor) != 'undefined'){
6789 td.style += ' cursor:' + config.cursor + ';';
6792 if(typeof(config.cls) != 'undefined'){
6793 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6796 ['xs','sm','md','lg'].map(function(size){
6798 if(typeof(config[size]) == 'undefined'){
6802 if (!config[size]) { // 0 = hidden
6803 td.cls += ' hidden-' + size;
6807 td.cls += ' col-' + size + '-' + config[size];
6815 row.cellObjects = cellObjects;
6823 onBeforeLoad : function()
6832 this.el.select('tbody', true).first().dom.innerHTML = '';
6835 * Show or hide a row.
6836 * @param {Number} rowIndex to show or hide
6837 * @param {Boolean} state hide
6839 setRowVisibility : function(rowIndex, state)
6841 var bt = this.mainBody.dom;
6843 var rows = this.el.select('tbody > tr', true).elements;
6845 if(typeof(rows[rowIndex]) == 'undefined'){
6848 rows[rowIndex].dom.style.display = state ? '' : 'none';
6852 getSelectionModel : function(){
6854 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6856 return this.selModel;
6859 * Render the Roo.bootstrap object from renderder
6861 renderCellObject : function(r)
6865 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6867 var t = r.cfg.render(r.container);
6870 Roo.each(r.cfg.cn, function(c){
6872 container: t.getChildContainer(),
6875 _this.renderCellObject(child);
6880 getRowIndex : function(row)
6884 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6895 * Returns the grid's underlying element = used by panel.Grid
6896 * @return {Element} The element
6898 getGridEl : function(){
6902 * Forces a resize - used by panel.Grid
6903 * @return {Element} The element
6905 autoSize : function()
6907 //var ctr = Roo.get(this.container.dom.parentElement);
6908 var ctr = Roo.get(this.el.dom);
6910 var thd = this.getGridEl().select('thead',true).first();
6911 var tbd = this.getGridEl().select('tbody', true).first();
6912 var tfd = this.getGridEl().select('tfoot', true).first();
6914 var cw = ctr.getWidth();
6918 tbd.setSize(ctr.getWidth(),
6919 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6921 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6924 cw = Math.max(cw, this.totalWidth);
6925 this.getGridEl().select('tr',true).setWidth(cw);
6926 // resize 'expandable coloumn?
6928 return; // we doe not have a view in this design..
6931 onBodyScroll: function()
6933 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6935 this.mainHead.setStyle({
6936 'position' : 'relative',
6937 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6943 var scrollHeight = this.mainBody.dom.scrollHeight;
6945 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6947 var height = this.mainBody.getHeight();
6949 if(scrollHeight - height == scrollTop) {
6951 var total = this.ds.getTotalCount();
6953 if(this.footer.cursor + this.footer.pageSize < total){
6955 this.footer.ds.load({
6957 start : this.footer.cursor + this.footer.pageSize,
6958 limit : this.footer.pageSize
6968 onHeaderChange : function()
6970 var header = this.renderHeader();
6971 var table = this.el.select('table', true).first();
6973 this.mainHead.remove();
6974 this.mainHead = table.createChild(header, this.mainBody, false);
6977 onHiddenChange : function(colModel, colIndex, hidden)
6979 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6980 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6982 this.CSS.updateRule(thSelector, "display", "");
6983 this.CSS.updateRule(tdSelector, "display", "");
6986 this.CSS.updateRule(thSelector, "display", "none");
6987 this.CSS.updateRule(tdSelector, "display", "none");
6990 this.onHeaderChange();
7007 * @class Roo.bootstrap.TableCell
7008 * @extends Roo.bootstrap.Component
7009 * Bootstrap TableCell class
7010 * @cfg {String} html cell contain text
7011 * @cfg {String} cls cell class
7012 * @cfg {String} tag cell tag (td|th) default td
7013 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7014 * @cfg {String} align Aligns the content in a cell
7015 * @cfg {String} axis Categorizes cells
7016 * @cfg {String} bgcolor Specifies the background color of a cell
7017 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7018 * @cfg {Number} colspan Specifies the number of columns a cell should span
7019 * @cfg {String} headers Specifies one or more header cells a cell is related to
7020 * @cfg {Number} height Sets the height of a cell
7021 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7022 * @cfg {Number} rowspan Sets the number of rows a cell should span
7023 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7024 * @cfg {String} valign Vertical aligns the content in a cell
7025 * @cfg {Number} width Specifies the width of a cell
7028 * Create a new TableCell
7029 * @param {Object} config The config object
7032 Roo.bootstrap.TableCell = function(config){
7033 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7036 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7056 getAutoCreate : function(){
7057 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7077 cfg.align=this.align
7083 cfg.bgcolor=this.bgcolor
7086 cfg.charoff=this.charoff
7089 cfg.colspan=this.colspan
7092 cfg.headers=this.headers
7095 cfg.height=this.height
7098 cfg.nowrap=this.nowrap
7101 cfg.rowspan=this.rowspan
7104 cfg.scope=this.scope
7107 cfg.valign=this.valign
7110 cfg.width=this.width
7129 * @class Roo.bootstrap.TableRow
7130 * @extends Roo.bootstrap.Component
7131 * Bootstrap TableRow class
7132 * @cfg {String} cls row class
7133 * @cfg {String} align Aligns the content in a table row
7134 * @cfg {String} bgcolor Specifies a background color for a table row
7135 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7136 * @cfg {String} valign Vertical aligns the content in a table row
7139 * Create a new TableRow
7140 * @param {Object} config The config object
7143 Roo.bootstrap.TableRow = function(config){
7144 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7147 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7155 getAutoCreate : function(){
7156 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7166 cfg.align = this.align;
7169 cfg.bgcolor = this.bgcolor;
7172 cfg.charoff = this.charoff;
7175 cfg.valign = this.valign;
7193 * @class Roo.bootstrap.TableBody
7194 * @extends Roo.bootstrap.Component
7195 * Bootstrap TableBody class
7196 * @cfg {String} cls element class
7197 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7198 * @cfg {String} align Aligns the content inside the element
7199 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7200 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7203 * Create a new TableBody
7204 * @param {Object} config The config object
7207 Roo.bootstrap.TableBody = function(config){
7208 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7211 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7219 getAutoCreate : function(){
7220 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7234 cfg.align = this.align;
7237 cfg.charoff = this.charoff;
7240 cfg.valign = this.valign;
7247 // initEvents : function()
7254 // this.store = Roo.factory(this.store, Roo.data);
7255 // this.store.on('load', this.onLoad, this);
7257 // this.store.load();
7261 // onLoad: function ()
7263 // this.fireEvent('load', this);
7273 * Ext JS Library 1.1.1
7274 * Copyright(c) 2006-2007, Ext JS, LLC.
7276 * Originally Released Under LGPL - original licence link has changed is not relivant.
7279 * <script type="text/javascript">
7282 // as we use this in bootstrap.
7283 Roo.namespace('Roo.form');
7285 * @class Roo.form.Action
7286 * Internal Class used to handle form actions
7288 * @param {Roo.form.BasicForm} el The form element or its id
7289 * @param {Object} config Configuration options
7294 // define the action interface
7295 Roo.form.Action = function(form, options){
7297 this.options = options || {};
7300 * Client Validation Failed
7303 Roo.form.Action.CLIENT_INVALID = 'client';
7305 * Server Validation Failed
7308 Roo.form.Action.SERVER_INVALID = 'server';
7310 * Connect to Server Failed
7313 Roo.form.Action.CONNECT_FAILURE = 'connect';
7315 * Reading Data from Server Failed
7318 Roo.form.Action.LOAD_FAILURE = 'load';
7320 Roo.form.Action.prototype = {
7322 failureType : undefined,
7323 response : undefined,
7327 run : function(options){
7332 success : function(response){
7337 handleResponse : function(response){
7341 // default connection failure
7342 failure : function(response){
7344 this.response = response;
7345 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7346 this.form.afterAction(this, false);
7349 processResponse : function(response){
7350 this.response = response;
7351 if(!response.responseText){
7354 this.result = this.handleResponse(response);
7358 // utility functions used internally
7359 getUrl : function(appendParams){
7360 var url = this.options.url || this.form.url || this.form.el.dom.action;
7362 var p = this.getParams();
7364 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7370 getMethod : function(){
7371 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7374 getParams : function(){
7375 var bp = this.form.baseParams;
7376 var p = this.options.params;
7378 if(typeof p == "object"){
7379 p = Roo.urlEncode(Roo.applyIf(p, bp));
7380 }else if(typeof p == 'string' && bp){
7381 p += '&' + Roo.urlEncode(bp);
7384 p = Roo.urlEncode(bp);
7389 createCallback : function(){
7391 success: this.success,
7392 failure: this.failure,
7394 timeout: (this.form.timeout*1000),
7395 upload: this.form.fileUpload ? this.success : undefined
7400 Roo.form.Action.Submit = function(form, options){
7401 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7404 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7407 haveProgress : false,
7408 uploadComplete : false,
7410 // uploadProgress indicator.
7411 uploadProgress : function()
7413 if (!this.form.progressUrl) {
7417 if (!this.haveProgress) {
7418 Roo.MessageBox.progress("Uploading", "Uploading");
7420 if (this.uploadComplete) {
7421 Roo.MessageBox.hide();
7425 this.haveProgress = true;
7427 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7429 var c = new Roo.data.Connection();
7431 url : this.form.progressUrl,
7436 success : function(req){
7437 //console.log(data);
7441 rdata = Roo.decode(req.responseText)
7443 Roo.log("Invalid data from server..");
7447 if (!rdata || !rdata.success) {
7449 Roo.MessageBox.alert(Roo.encode(rdata));
7452 var data = rdata.data;
7454 if (this.uploadComplete) {
7455 Roo.MessageBox.hide();
7460 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7461 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7464 this.uploadProgress.defer(2000,this);
7467 failure: function(data) {
7468 Roo.log('progress url failed ');
7479 // run get Values on the form, so it syncs any secondary forms.
7480 this.form.getValues();
7482 var o = this.options;
7483 var method = this.getMethod();
7484 var isPost = method == 'POST';
7485 if(o.clientValidation === false || this.form.isValid()){
7487 if (this.form.progressUrl) {
7488 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7489 (new Date() * 1) + '' + Math.random());
7494 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7495 form:this.form.el.dom,
7496 url:this.getUrl(!isPost),
7498 params:isPost ? this.getParams() : null,
7499 isUpload: this.form.fileUpload
7502 this.uploadProgress();
7504 }else if (o.clientValidation !== false){ // client validation failed
7505 this.failureType = Roo.form.Action.CLIENT_INVALID;
7506 this.form.afterAction(this, false);
7510 success : function(response)
7512 this.uploadComplete= true;
7513 if (this.haveProgress) {
7514 Roo.MessageBox.hide();
7518 var result = this.processResponse(response);
7519 if(result === true || result.success){
7520 this.form.afterAction(this, true);
7524 this.form.markInvalid(result.errors);
7525 this.failureType = Roo.form.Action.SERVER_INVALID;
7527 this.form.afterAction(this, false);
7529 failure : function(response)
7531 this.uploadComplete= true;
7532 if (this.haveProgress) {
7533 Roo.MessageBox.hide();
7536 this.response = response;
7537 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7538 this.form.afterAction(this, false);
7541 handleResponse : function(response){
7542 if(this.form.errorReader){
7543 var rs = this.form.errorReader.read(response);
7546 for(var i = 0, len = rs.records.length; i < len; i++) {
7547 var r = rs.records[i];
7551 if(errors.length < 1){
7555 success : rs.success,
7561 ret = Roo.decode(response.responseText);
7565 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7575 Roo.form.Action.Load = function(form, options){
7576 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7577 this.reader = this.form.reader;
7580 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7585 Roo.Ajax.request(Roo.apply(
7586 this.createCallback(), {
7587 method:this.getMethod(),
7588 url:this.getUrl(false),
7589 params:this.getParams()
7593 success : function(response){
7595 var result = this.processResponse(response);
7596 if(result === true || !result.success || !result.data){
7597 this.failureType = Roo.form.Action.LOAD_FAILURE;
7598 this.form.afterAction(this, false);
7601 this.form.clearInvalid();
7602 this.form.setValues(result.data);
7603 this.form.afterAction(this, true);
7606 handleResponse : function(response){
7607 if(this.form.reader){
7608 var rs = this.form.reader.read(response);
7609 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7611 success : rs.success,
7615 return Roo.decode(response.responseText);
7619 Roo.form.Action.ACTION_TYPES = {
7620 'load' : Roo.form.Action.Load,
7621 'submit' : Roo.form.Action.Submit
7630 * @class Roo.bootstrap.Form
7631 * @extends Roo.bootstrap.Component
7632 * Bootstrap Form class
7633 * @cfg {String} method GET | POST (default POST)
7634 * @cfg {String} labelAlign top | left (default top)
7635 * @cfg {String} align left | right - for navbars
7636 * @cfg {Boolean} loadMask load mask when submit (default true)
7641 * @param {Object} config The config object
7645 Roo.bootstrap.Form = function(config){
7647 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7649 Roo.bootstrap.Form.popover.apply();
7653 * @event clientvalidation
7654 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7655 * @param {Form} this
7656 * @param {Boolean} valid true if the form has passed client-side validation
7658 clientvalidation: true,
7660 * @event beforeaction
7661 * Fires before any action is performed. Return false to cancel the action.
7662 * @param {Form} this
7663 * @param {Action} action The action to be performed
7667 * @event actionfailed
7668 * Fires when an action fails.
7669 * @param {Form} this
7670 * @param {Action} action The action that failed
7672 actionfailed : true,
7674 * @event actioncomplete
7675 * Fires when an action is completed.
7676 * @param {Form} this
7677 * @param {Action} action The action that completed
7679 actioncomplete : true
7683 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7686 * @cfg {String} method
7687 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7692 * The URL to use for form actions if one isn't supplied in the action options.
7695 * @cfg {Boolean} fileUpload
7696 * Set to true if this form is a file upload.
7700 * @cfg {Object} baseParams
7701 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7705 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7709 * @cfg {Sting} align (left|right) for navbar forms
7714 activeAction : null,
7717 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7718 * element by passing it or its id or mask the form itself by passing in true.
7721 waitMsgTarget : false,
7726 * @cfg {Boolean} errorMask (true|false) default false
7731 * @cfg {Number} maskOffset Default 100
7736 * @cfg {Boolean} maskBody
7740 getAutoCreate : function(){
7744 method : this.method || 'POST',
7745 id : this.id || Roo.id(),
7748 if (this.parent().xtype.match(/^Nav/)) {
7749 cfg.cls = 'navbar-form navbar-' + this.align;
7753 if (this.labelAlign == 'left' ) {
7754 cfg.cls += ' form-horizontal';
7760 initEvents : function()
7762 this.el.on('submit', this.onSubmit, this);
7763 // this was added as random key presses on the form where triggering form submit.
7764 this.el.on('keypress', function(e) {
7765 if (e.getCharCode() != 13) {
7768 // we might need to allow it for textareas.. and some other items.
7769 // check e.getTarget().
7771 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7775 Roo.log("keypress blocked");
7783 onSubmit : function(e){
7788 * Returns true if client-side validation on the form is successful.
7791 isValid : function(){
7792 var items = this.getItems();
7796 items.each(function(f){
7804 if(!target && f.el.isVisible(true)){
7810 if(this.errorMask && !valid){
7811 Roo.bootstrap.Form.popover.mask(this, target);
7818 * Returns true if any fields in this form have changed since their original load.
7821 isDirty : function(){
7823 var items = this.getItems();
7824 items.each(function(f){
7834 * Performs a predefined action (submit or load) or custom actions you define on this form.
7835 * @param {String} actionName The name of the action type
7836 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7837 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7838 * accept other config options):
7840 Property Type Description
7841 ---------------- --------------- ----------------------------------------------------------------------------------
7842 url String The url for the action (defaults to the form's url)
7843 method String The form method to use (defaults to the form's method, or POST if not defined)
7844 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7845 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7846 validate the form on the client (defaults to false)
7848 * @return {BasicForm} this
7850 doAction : function(action, options){
7851 if(typeof action == 'string'){
7852 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7854 if(this.fireEvent('beforeaction', this, action) !== false){
7855 this.beforeAction(action);
7856 action.run.defer(100, action);
7862 beforeAction : function(action){
7863 var o = action.options;
7868 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7870 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7873 // not really supported yet.. ??
7875 //if(this.waitMsgTarget === true){
7876 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7877 //}else if(this.waitMsgTarget){
7878 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7879 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7881 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7887 afterAction : function(action, success){
7888 this.activeAction = null;
7889 var o = action.options;
7894 Roo.get(document.body).unmask();
7900 //if(this.waitMsgTarget === true){
7901 // this.el.unmask();
7902 //}else if(this.waitMsgTarget){
7903 // this.waitMsgTarget.unmask();
7905 // Roo.MessageBox.updateProgress(1);
7906 // Roo.MessageBox.hide();
7913 Roo.callback(o.success, o.scope, [this, action]);
7914 this.fireEvent('actioncomplete', this, action);
7918 // failure condition..
7919 // we have a scenario where updates need confirming.
7920 // eg. if a locking scenario exists..
7921 // we look for { errors : { needs_confirm : true }} in the response.
7923 (typeof(action.result) != 'undefined') &&
7924 (typeof(action.result.errors) != 'undefined') &&
7925 (typeof(action.result.errors.needs_confirm) != 'undefined')
7928 Roo.log("not supported yet");
7931 Roo.MessageBox.confirm(
7932 "Change requires confirmation",
7933 action.result.errorMsg,
7938 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7948 Roo.callback(o.failure, o.scope, [this, action]);
7949 // show an error message if no failed handler is set..
7950 if (!this.hasListener('actionfailed')) {
7951 Roo.log("need to add dialog support");
7953 Roo.MessageBox.alert("Error",
7954 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7955 action.result.errorMsg :
7956 "Saving Failed, please check your entries or try again"
7961 this.fireEvent('actionfailed', this, action);
7966 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7967 * @param {String} id The value to search for
7970 findField : function(id){
7971 var items = this.getItems();
7972 var field = items.get(id);
7974 items.each(function(f){
7975 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7982 return field || null;
7985 * Mark fields in this form invalid in bulk.
7986 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7987 * @return {BasicForm} this
7989 markInvalid : function(errors){
7990 if(errors instanceof Array){
7991 for(var i = 0, len = errors.length; i < len; i++){
7992 var fieldError = errors[i];
7993 var f = this.findField(fieldError.id);
7995 f.markInvalid(fieldError.msg);
8001 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8002 field.markInvalid(errors[id]);
8006 //Roo.each(this.childForms || [], function (f) {
8007 // f.markInvalid(errors);
8014 * Set values for fields in this form in bulk.
8015 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8016 * @return {BasicForm} this
8018 setValues : function(values){
8019 if(values instanceof Array){ // array of objects
8020 for(var i = 0, len = values.length; i < len; i++){
8022 var f = this.findField(v.id);
8024 f.setValue(v.value);
8025 if(this.trackResetOnLoad){
8026 f.originalValue = f.getValue();
8030 }else{ // object hash
8033 if(typeof values[id] != 'function' && (field = this.findField(id))){
8035 if (field.setFromData &&
8037 field.displayField &&
8038 // combos' with local stores can
8039 // be queried via setValue()
8040 // to set their value..
8041 (field.store && !field.store.isLocal)
8045 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8046 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8047 field.setFromData(sd);
8049 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8051 field.setFromData(values);
8054 field.setValue(values[id]);
8058 if(this.trackResetOnLoad){
8059 field.originalValue = field.getValue();
8065 //Roo.each(this.childForms || [], function (f) {
8066 // f.setValues(values);
8073 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8074 * they are returned as an array.
8075 * @param {Boolean} asString
8078 getValues : function(asString){
8079 //if (this.childForms) {
8080 // copy values from the child forms
8081 // Roo.each(this.childForms, function (f) {
8082 // this.setValues(f.getValues());
8088 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8089 if(asString === true){
8092 return Roo.urlDecode(fs);
8096 * Returns the fields in this form as an object with key/value pairs.
8097 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8100 getFieldValues : function(with_hidden)
8102 var items = this.getItems();
8104 items.each(function(f){
8110 var v = f.getValue();
8112 if (f.inputType =='radio') {
8113 if (typeof(ret[f.getName()]) == 'undefined') {
8114 ret[f.getName()] = ''; // empty..
8117 if (!f.el.dom.checked) {
8125 if(f.xtype == 'MoneyField'){
8126 ret[f.currencyName] = f.getCurrency();
8129 // not sure if this supported any more..
8130 if ((typeof(v) == 'object') && f.getRawValue) {
8131 v = f.getRawValue() ; // dates..
8133 // combo boxes where name != hiddenName...
8134 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8135 ret[f.name] = f.getRawValue();
8137 ret[f.getName()] = v;
8144 * Clears all invalid messages in this form.
8145 * @return {BasicForm} this
8147 clearInvalid : function(){
8148 var items = this.getItems();
8150 items.each(function(f){
8159 * @return {BasicForm} this
8162 var items = this.getItems();
8163 items.each(function(f){
8167 Roo.each(this.childForms || [], function (f) {
8175 getItems : function()
8177 var r=new Roo.util.MixedCollection(false, function(o){
8178 return o.id || (o.id = Roo.id());
8180 var iter = function(el) {
8187 Roo.each(el.items,function(e) {
8196 hideFields : function(items)
8198 Roo.each(items, function(i){
8200 var f = this.findField(i);
8206 if(f.xtype == 'DateField'){
8207 f.setVisible(false);
8216 showFields : function(items)
8218 Roo.each(items, function(i){
8220 var f = this.findField(i);
8226 if(f.xtype == 'DateField'){
8238 Roo.apply(Roo.bootstrap.Form, {
8265 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8266 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8267 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8268 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8271 this.maskEl.top.enableDisplayMode("block");
8272 this.maskEl.left.enableDisplayMode("block");
8273 this.maskEl.bottom.enableDisplayMode("block");
8274 this.maskEl.right.enableDisplayMode("block");
8276 this.toolTip = new Roo.bootstrap.Tooltip({
8277 cls : 'roo-form-error-popover',
8279 'left' : ['r-l', [-2,0], 'right'],
8280 'right' : ['l-r', [2,0], 'left'],
8281 'bottom' : ['tl-bl', [0,2], 'top'],
8282 'top' : [ 'bl-tl', [0,-2], 'bottom']
8286 this.toolTip.render(Roo.get(document.body));
8288 this.toolTip.el.enableDisplayMode("block");
8290 Roo.get(document.body).on('click', function(){
8294 Roo.get(document.body).on('touchstart', function(){
8298 this.isApplied = true
8301 mask : function(form, target)
8305 this.target = target;
8307 if(!this.form.errorMask || !target.el){
8311 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8313 Roo.log(scrollable);
8315 var ot = this.target.el.calcOffsetsTo(scrollable);
8317 var scrollTo = ot[1] - this.form.maskOffset;
8319 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8321 scrollable.scrollTo('top', scrollTo);
8323 var box = this.target.el.getBox();
8325 var zIndex = Roo.bootstrap.Modal.zIndex++;
8328 this.maskEl.top.setStyle('position', 'absolute');
8329 this.maskEl.top.setStyle('z-index', zIndex);
8330 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8331 this.maskEl.top.setLeft(0);
8332 this.maskEl.top.setTop(0);
8333 this.maskEl.top.show();
8335 this.maskEl.left.setStyle('position', 'absolute');
8336 this.maskEl.left.setStyle('z-index', zIndex);
8337 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8338 this.maskEl.left.setLeft(0);
8339 this.maskEl.left.setTop(box.y - this.padding);
8340 this.maskEl.left.show();
8342 this.maskEl.bottom.setStyle('position', 'absolute');
8343 this.maskEl.bottom.setStyle('z-index', zIndex);
8344 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8345 this.maskEl.bottom.setLeft(0);
8346 this.maskEl.bottom.setTop(box.bottom + this.padding);
8347 this.maskEl.bottom.show();
8349 this.maskEl.right.setStyle('position', 'absolute');
8350 this.maskEl.right.setStyle('z-index', zIndex);
8351 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8352 this.maskEl.right.setLeft(box.right + this.padding);
8353 this.maskEl.right.setTop(box.y - this.padding);
8354 this.maskEl.right.show();
8356 this.toolTip.bindEl = this.target.el;
8358 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8360 var tip = this.target.blankText;
8362 if(this.target.getValue() !== '' ) {
8364 if (this.target.invalidText.length) {
8365 tip = this.target.invalidText;
8366 } else if (this.target.regexText.length){
8367 tip = this.target.regexText;
8371 this.toolTip.show(tip);
8373 this.intervalID = window.setInterval(function() {
8374 Roo.bootstrap.Form.popover.unmask();
8377 window.onwheel = function(){ return false;};
8379 (function(){ this.isMasked = true; }).defer(500, this);
8385 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8389 this.maskEl.top.setStyle('position', 'absolute');
8390 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8391 this.maskEl.top.hide();
8393 this.maskEl.left.setStyle('position', 'absolute');
8394 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8395 this.maskEl.left.hide();
8397 this.maskEl.bottom.setStyle('position', 'absolute');
8398 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8399 this.maskEl.bottom.hide();
8401 this.maskEl.right.setStyle('position', 'absolute');
8402 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8403 this.maskEl.right.hide();
8405 this.toolTip.hide();
8407 this.toolTip.el.hide();
8409 window.onwheel = function(){ return true;};
8411 if(this.intervalID){
8412 window.clearInterval(this.intervalID);
8413 this.intervalID = false;
8416 this.isMasked = false;
8426 * Ext JS Library 1.1.1
8427 * Copyright(c) 2006-2007, Ext JS, LLC.
8429 * Originally Released Under LGPL - original licence link has changed is not relivant.
8432 * <script type="text/javascript">
8435 * @class Roo.form.VTypes
8436 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8439 Roo.form.VTypes = function(){
8440 // closure these in so they are only created once.
8441 var alpha = /^[a-zA-Z_]+$/;
8442 var alphanum = /^[a-zA-Z0-9_]+$/;
8443 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8444 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8446 // All these messages and functions are configurable
8449 * The function used to validate email addresses
8450 * @param {String} value The email address
8452 'email' : function(v){
8453 return email.test(v);
8456 * The error text to display when the email validation function returns false
8459 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8461 * The keystroke filter mask to be applied on email input
8464 'emailMask' : /[a-z0-9_\.\-@]/i,
8467 * The function used to validate URLs
8468 * @param {String} value The URL
8470 'url' : function(v){
8474 * The error text to display when the url validation function returns false
8477 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8480 * The function used to validate alpha values
8481 * @param {String} value The value
8483 'alpha' : function(v){
8484 return alpha.test(v);
8487 * The error text to display when the alpha validation function returns false
8490 'alphaText' : 'This field should only contain letters and _',
8492 * The keystroke filter mask to be applied on alpha input
8495 'alphaMask' : /[a-z_]/i,
8498 * The function used to validate alphanumeric values
8499 * @param {String} value The value
8501 'alphanum' : function(v){
8502 return alphanum.test(v);
8505 * The error text to display when the alphanumeric validation function returns false
8508 'alphanumText' : 'This field should only contain letters, numbers and _',
8510 * The keystroke filter mask to be applied on alphanumeric input
8513 'alphanumMask' : /[a-z0-9_]/i
8523 * @class Roo.bootstrap.Input
8524 * @extends Roo.bootstrap.Component
8525 * Bootstrap Input class
8526 * @cfg {Boolean} disabled is it disabled
8527 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8528 * @cfg {String} name name of the input
8529 * @cfg {string} fieldLabel - the label associated
8530 * @cfg {string} placeholder - placeholder to put in text.
8531 * @cfg {string} before - input group add on before
8532 * @cfg {string} after - input group add on after
8533 * @cfg {string} size - (lg|sm) or leave empty..
8534 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8535 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8536 * @cfg {Number} md colspan out of 12 for computer-sized screens
8537 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8538 * @cfg {string} value default value of the input
8539 * @cfg {Number} labelWidth set the width of label
8540 * @cfg {Number} labellg set the width of label (1-12)
8541 * @cfg {Number} labelmd set the width of label (1-12)
8542 * @cfg {Number} labelsm set the width of label (1-12)
8543 * @cfg {Number} labelxs set the width of label (1-12)
8544 * @cfg {String} labelAlign (top|left)
8545 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8546 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8547 * @cfg {String} indicatorpos (left|right) default left
8548 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8549 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8551 * @cfg {String} align (left|center|right) Default left
8552 * @cfg {Boolean} forceFeedback (true|false) Default false
8555 * Create a new Input
8556 * @param {Object} config The config object
8559 Roo.bootstrap.Input = function(config){
8561 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8566 * Fires when this field receives input focus.
8567 * @param {Roo.form.Field} this
8572 * Fires when this field loses input focus.
8573 * @param {Roo.form.Field} this
8578 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8579 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8580 * @param {Roo.form.Field} this
8581 * @param {Roo.EventObject} e The event object
8586 * Fires just before the field blurs if the field value has changed.
8587 * @param {Roo.form.Field} this
8588 * @param {Mixed} newValue The new value
8589 * @param {Mixed} oldValue The original value
8594 * Fires after the field has been marked as invalid.
8595 * @param {Roo.form.Field} this
8596 * @param {String} msg The validation message
8601 * Fires after the field has been validated with no errors.
8602 * @param {Roo.form.Field} this
8607 * Fires after the key up
8608 * @param {Roo.form.Field} this
8609 * @param {Roo.EventObject} e The event Object
8615 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8617 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8618 automatic validation (defaults to "keyup").
8620 validationEvent : "keyup",
8622 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8624 validateOnBlur : true,
8626 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8628 validationDelay : 250,
8630 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8632 focusClass : "x-form-focus", // not needed???
8636 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8638 invalidClass : "has-warning",
8641 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8643 validClass : "has-success",
8646 * @cfg {Boolean} hasFeedback (true|false) default true
8651 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8653 invalidFeedbackClass : "glyphicon-warning-sign",
8656 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8658 validFeedbackClass : "glyphicon-ok",
8661 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8663 selectOnFocus : false,
8666 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8670 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8675 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8677 disableKeyFilter : false,
8680 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8684 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8688 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8690 blankText : "Please complete this mandatory field",
8693 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8697 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8699 maxLength : Number.MAX_VALUE,
8701 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8703 minLengthText : "The minimum length for this field is {0}",
8705 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8707 maxLengthText : "The maximum length for this field is {0}",
8711 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8712 * If available, this function will be called only after the basic validators all return true, and will be passed the
8713 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8717 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8718 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8719 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8723 * @cfg {String} regexText -- Depricated - use Invalid Text
8728 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8734 autocomplete: false,
8753 formatedValue : false,
8754 forceFeedback : false,
8756 indicatorpos : 'left',
8766 parentLabelAlign : function()
8769 while (parent.parent()) {
8770 parent = parent.parent();
8771 if (typeof(parent.labelAlign) !='undefined') {
8772 return parent.labelAlign;
8779 getAutoCreate : function()
8781 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8787 if(this.inputType != 'hidden'){
8788 cfg.cls = 'form-group' //input-group
8794 type : this.inputType,
8796 cls : 'form-control',
8797 placeholder : this.placeholder || '',
8798 autocomplete : this.autocomplete || 'new-password'
8801 if(this.capture.length){
8802 input.capture = this.capture;
8805 if(this.accept.length){
8806 input.accept = this.accept + "/*";
8810 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8813 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8814 input.maxLength = this.maxLength;
8817 if (this.disabled) {
8818 input.disabled=true;
8821 if (this.readOnly) {
8822 input.readonly=true;
8826 input.name = this.name;
8830 input.cls += ' input-' + this.size;
8834 ['xs','sm','md','lg'].map(function(size){
8835 if (settings[size]) {
8836 cfg.cls += ' col-' + size + '-' + settings[size];
8840 var inputblock = input;
8844 cls: 'glyphicon form-control-feedback'
8847 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8850 cls : 'has-feedback',
8858 if (this.before || this.after) {
8861 cls : 'input-group',
8865 if (this.before && typeof(this.before) == 'string') {
8867 inputblock.cn.push({
8869 cls : 'roo-input-before input-group-addon',
8873 if (this.before && typeof(this.before) == 'object') {
8874 this.before = Roo.factory(this.before);
8876 inputblock.cn.push({
8878 cls : 'roo-input-before input-group-' +
8879 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8883 inputblock.cn.push(input);
8885 if (this.after && typeof(this.after) == 'string') {
8886 inputblock.cn.push({
8888 cls : 'roo-input-after input-group-addon',
8892 if (this.after && typeof(this.after) == 'object') {
8893 this.after = Roo.factory(this.after);
8895 inputblock.cn.push({
8897 cls : 'roo-input-after input-group-' +
8898 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8902 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8903 inputblock.cls += ' has-feedback';
8904 inputblock.cn.push(feedback);
8908 if (align ==='left' && this.fieldLabel.length) {
8910 cfg.cls += ' roo-form-group-label-left';
8915 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8916 tooltip : 'This field is required'
8921 cls : 'control-label',
8922 html : this.fieldLabel
8933 var labelCfg = cfg.cn[1];
8934 var contentCfg = cfg.cn[2];
8936 if(this.indicatorpos == 'right'){
8941 cls : 'control-label',
8945 html : this.fieldLabel
8949 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8950 tooltip : 'This field is required'
8963 labelCfg = cfg.cn[0];
8964 contentCfg = cfg.cn[1];
8968 if(this.labelWidth > 12){
8969 labelCfg.style = "width: " + this.labelWidth + 'px';
8972 if(this.labelWidth < 13 && this.labelmd == 0){
8973 this.labelmd = this.labelWidth;
8976 if(this.labellg > 0){
8977 labelCfg.cls += ' col-lg-' + this.labellg;
8978 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8981 if(this.labelmd > 0){
8982 labelCfg.cls += ' col-md-' + this.labelmd;
8983 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8986 if(this.labelsm > 0){
8987 labelCfg.cls += ' col-sm-' + this.labelsm;
8988 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8991 if(this.labelxs > 0){
8992 labelCfg.cls += ' col-xs-' + this.labelxs;
8993 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8997 } else if ( this.fieldLabel.length) {
9002 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9003 tooltip : 'This field is required'
9007 //cls : 'input-group-addon',
9008 html : this.fieldLabel
9016 if(this.indicatorpos == 'right'){
9021 //cls : 'input-group-addon',
9022 html : this.fieldLabel
9027 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9028 tooltip : 'This field is required'
9048 if (this.parentType === 'Navbar' && this.parent().bar) {
9049 cfg.cls += ' navbar-form';
9052 if (this.parentType === 'NavGroup') {
9053 cfg.cls += ' navbar-form';
9061 * return the real input element.
9063 inputEl: function ()
9065 return this.el.select('input.form-control',true).first();
9068 tooltipEl : function()
9070 return this.inputEl();
9073 indicatorEl : function()
9075 var indicator = this.el.select('i.roo-required-indicator',true).first();
9085 setDisabled : function(v)
9087 var i = this.inputEl().dom;
9089 i.removeAttribute('disabled');
9093 i.setAttribute('disabled','true');
9095 initEvents : function()
9098 this.inputEl().on("keydown" , this.fireKey, this);
9099 this.inputEl().on("focus", this.onFocus, this);
9100 this.inputEl().on("blur", this.onBlur, this);
9102 this.inputEl().relayEvent('keyup', this);
9104 this.indicator = this.indicatorEl();
9107 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9110 // reference to original value for reset
9111 this.originalValue = this.getValue();
9112 //Roo.form.TextField.superclass.initEvents.call(this);
9113 if(this.validationEvent == 'keyup'){
9114 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9115 this.inputEl().on('keyup', this.filterValidation, this);
9117 else if(this.validationEvent !== false){
9118 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9121 if(this.selectOnFocus){
9122 this.on("focus", this.preFocus, this);
9125 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9126 this.inputEl().on("keypress", this.filterKeys, this);
9128 this.inputEl().relayEvent('keypress', this);
9131 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9132 this.el.on("click", this.autoSize, this);
9135 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9136 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9139 if (typeof(this.before) == 'object') {
9140 this.before.render(this.el.select('.roo-input-before',true).first());
9142 if (typeof(this.after) == 'object') {
9143 this.after.render(this.el.select('.roo-input-after',true).first());
9146 this.inputEl().on('change', this.onChange, this);
9149 filterValidation : function(e){
9150 if(!e.isNavKeyPress()){
9151 this.validationTask.delay(this.validationDelay);
9155 * Validates the field value
9156 * @return {Boolean} True if the value is valid, else false
9158 validate : function(){
9159 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9160 if(this.disabled || this.validateValue(this.getRawValue())){
9171 * Validates a value according to the field's validation rules and marks the field as invalid
9172 * if the validation fails
9173 * @param {Mixed} value The value to validate
9174 * @return {Boolean} True if the value is valid, else false
9176 validateValue : function(value)
9178 if(this.getVisibilityEl().hasClass('hidden')){
9182 if(value.length < 1) { // if it's blank
9183 if(this.allowBlank){
9189 if(value.length < this.minLength){
9192 if(value.length > this.maxLength){
9196 var vt = Roo.form.VTypes;
9197 if(!vt[this.vtype](value, this)){
9201 if(typeof this.validator == "function"){
9202 var msg = this.validator(value);
9206 if (typeof(msg) == 'string') {
9207 this.invalidText = msg;
9211 if(this.regex && !this.regex.test(value)){
9219 fireKey : function(e){
9220 //Roo.log('field ' + e.getKey());
9221 if(e.isNavKeyPress()){
9222 this.fireEvent("specialkey", this, e);
9225 focus : function (selectText){
9227 this.inputEl().focus();
9228 if(selectText === true){
9229 this.inputEl().dom.select();
9235 onFocus : function(){
9236 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9237 // this.el.addClass(this.focusClass);
9240 this.hasFocus = true;
9241 this.startValue = this.getValue();
9242 this.fireEvent("focus", this);
9246 beforeBlur : Roo.emptyFn,
9250 onBlur : function(){
9252 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9253 //this.el.removeClass(this.focusClass);
9255 this.hasFocus = false;
9256 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9259 var v = this.getValue();
9260 if(String(v) !== String(this.startValue)){
9261 this.fireEvent('change', this, v, this.startValue);
9263 this.fireEvent("blur", this);
9266 onChange : function(e)
9268 var v = this.getValue();
9269 if(String(v) !== String(this.startValue)){
9270 this.fireEvent('change', this, v, this.startValue);
9276 * Resets the current field value to the originally loaded value and clears any validation messages
9279 this.setValue(this.originalValue);
9283 * Returns the name of the field
9284 * @return {Mixed} name The name field
9286 getName: function(){
9290 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9291 * @return {Mixed} value The field value
9293 getValue : function(){
9295 var v = this.inputEl().getValue();
9300 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9301 * @return {Mixed} value The field value
9303 getRawValue : function(){
9304 var v = this.inputEl().getValue();
9310 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9311 * @param {Mixed} value The value to set
9313 setRawValue : function(v){
9314 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9317 selectText : function(start, end){
9318 var v = this.getRawValue();
9320 start = start === undefined ? 0 : start;
9321 end = end === undefined ? v.length : end;
9322 var d = this.inputEl().dom;
9323 if(d.setSelectionRange){
9324 d.setSelectionRange(start, end);
9325 }else if(d.createTextRange){
9326 var range = d.createTextRange();
9327 range.moveStart("character", start);
9328 range.moveEnd("character", v.length-end);
9335 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9336 * @param {Mixed} value The value to set
9338 setValue : function(v){
9341 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9347 processValue : function(value){
9348 if(this.stripCharsRe){
9349 var newValue = value.replace(this.stripCharsRe, '');
9350 if(newValue !== value){
9351 this.setRawValue(newValue);
9358 preFocus : function(){
9360 if(this.selectOnFocus){
9361 this.inputEl().dom.select();
9364 filterKeys : function(e){
9366 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9369 var c = e.getCharCode(), cc = String.fromCharCode(c);
9370 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9373 if(!this.maskRe.test(cc)){
9378 * Clear any invalid styles/messages for this field
9380 clearInvalid : function(){
9382 if(!this.el || this.preventMark){ // not rendered
9387 this.el.removeClass(this.invalidClass);
9389 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9391 var feedback = this.el.select('.form-control-feedback', true).first();
9394 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9399 this.fireEvent('valid', this);
9403 * Mark this field as valid
9405 markValid : function()
9407 if(!this.el || this.preventMark){ // not rendered...
9411 this.el.removeClass([this.invalidClass, this.validClass]);
9413 var feedback = this.el.select('.form-control-feedback', true).first();
9416 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9420 this.indicator.removeClass('visible');
9421 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9428 if(this.allowBlank && !this.getRawValue().length){
9432 this.el.addClass(this.validClass);
9434 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9436 var feedback = this.el.select('.form-control-feedback', true).first();
9439 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9440 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9445 this.fireEvent('valid', this);
9449 * Mark this field as invalid
9450 * @param {String} msg The validation message
9452 markInvalid : function(msg)
9454 if(!this.el || this.preventMark){ // not rendered
9458 this.el.removeClass([this.invalidClass, this.validClass]);
9460 var feedback = this.el.select('.form-control-feedback', true).first();
9463 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9470 if(this.allowBlank && !this.getRawValue().length){
9475 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9476 this.indicator.addClass('visible');
9479 this.el.addClass(this.invalidClass);
9481 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9483 var feedback = this.el.select('.form-control-feedback', true).first();
9486 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9488 if(this.getValue().length || this.forceFeedback){
9489 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9496 this.fireEvent('invalid', this, msg);
9499 SafariOnKeyDown : function(event)
9501 // this is a workaround for a password hang bug on chrome/ webkit.
9502 if (this.inputEl().dom.type != 'password') {
9506 var isSelectAll = false;
9508 if(this.inputEl().dom.selectionEnd > 0){
9509 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9511 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9512 event.preventDefault();
9517 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9519 event.preventDefault();
9520 // this is very hacky as keydown always get's upper case.
9522 var cc = String.fromCharCode(event.getCharCode());
9523 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9527 adjustWidth : function(tag, w){
9528 tag = tag.toLowerCase();
9529 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9530 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9534 if(tag == 'textarea'){
9537 }else if(Roo.isOpera){
9541 if(tag == 'textarea'){
9549 setFieldLabel : function(v)
9556 var ar = this.el.select('label > span',true);
9558 if (ar.elements.length) {
9559 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9560 this.fieldLabel = v;
9564 var br = this.el.select('label',true);
9566 if(br.elements.length) {
9567 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9568 this.fieldLabel = v;
9572 Roo.log('Cannot Found any of label > span || label in input');
9576 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9577 this.fieldLabel = v;
9592 * @class Roo.bootstrap.TextArea
9593 * @extends Roo.bootstrap.Input
9594 * Bootstrap TextArea class
9595 * @cfg {Number} cols Specifies the visible width of a text area
9596 * @cfg {Number} rows Specifies the visible number of lines in a text area
9597 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9598 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9599 * @cfg {string} html text
9602 * Create a new TextArea
9603 * @param {Object} config The config object
9606 Roo.bootstrap.TextArea = function(config){
9607 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9611 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9621 getAutoCreate : function(){
9623 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9629 if(this.inputType != 'hidden'){
9630 cfg.cls = 'form-group' //input-group
9638 value : this.value || '',
9639 html: this.html || '',
9640 cls : 'form-control',
9641 placeholder : this.placeholder || ''
9645 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9646 input.maxLength = this.maxLength;
9650 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9654 input.cols = this.cols;
9657 if (this.readOnly) {
9658 input.readonly = true;
9662 input.name = this.name;
9666 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9670 ['xs','sm','md','lg'].map(function(size){
9671 if (settings[size]) {
9672 cfg.cls += ' col-' + size + '-' + settings[size];
9676 var inputblock = input;
9678 if(this.hasFeedback && !this.allowBlank){
9682 cls: 'glyphicon form-control-feedback'
9686 cls : 'has-feedback',
9695 if (this.before || this.after) {
9698 cls : 'input-group',
9702 inputblock.cn.push({
9704 cls : 'input-group-addon',
9709 inputblock.cn.push(input);
9711 if(this.hasFeedback && !this.allowBlank){
9712 inputblock.cls += ' has-feedback';
9713 inputblock.cn.push(feedback);
9717 inputblock.cn.push({
9719 cls : 'input-group-addon',
9726 if (align ==='left' && this.fieldLabel.length) {
9731 cls : 'control-label',
9732 html : this.fieldLabel
9743 if(this.labelWidth > 12){
9744 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9747 if(this.labelWidth < 13 && this.labelmd == 0){
9748 this.labelmd = this.labelWidth;
9751 if(this.labellg > 0){
9752 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9753 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9756 if(this.labelmd > 0){
9757 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9758 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9761 if(this.labelsm > 0){
9762 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9763 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9766 if(this.labelxs > 0){
9767 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9768 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9771 } else if ( this.fieldLabel.length) {
9776 //cls : 'input-group-addon',
9777 html : this.fieldLabel
9795 if (this.disabled) {
9796 input.disabled=true;
9803 * return the real textarea element.
9805 inputEl: function ()
9807 return this.el.select('textarea.form-control',true).first();
9811 * Clear any invalid styles/messages for this field
9813 clearInvalid : function()
9816 if(!this.el || this.preventMark){ // not rendered
9820 var label = this.el.select('label', true).first();
9821 var icon = this.el.select('i.fa-star', true).first();
9827 this.el.removeClass(this.invalidClass);
9829 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9831 var feedback = this.el.select('.form-control-feedback', true).first();
9834 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9839 this.fireEvent('valid', this);
9843 * Mark this field as valid
9845 markValid : function()
9847 if(!this.el || this.preventMark){ // not rendered
9851 this.el.removeClass([this.invalidClass, this.validClass]);
9853 var feedback = this.el.select('.form-control-feedback', true).first();
9856 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9859 if(this.disabled || this.allowBlank){
9863 var label = this.el.select('label', true).first();
9864 var icon = this.el.select('i.fa-star', true).first();
9870 this.el.addClass(this.validClass);
9872 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9874 var feedback = this.el.select('.form-control-feedback', true).first();
9877 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9878 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9883 this.fireEvent('valid', this);
9887 * Mark this field as invalid
9888 * @param {String} msg The validation message
9890 markInvalid : function(msg)
9892 if(!this.el || this.preventMark){ // not rendered
9896 this.el.removeClass([this.invalidClass, this.validClass]);
9898 var feedback = this.el.select('.form-control-feedback', true).first();
9901 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9904 if(this.disabled || this.allowBlank){
9908 var label = this.el.select('label', true).first();
9909 var icon = this.el.select('i.fa-star', true).first();
9911 if(!this.getValue().length && label && !icon){
9912 this.el.createChild({
9914 cls : 'text-danger fa fa-lg fa-star',
9915 tooltip : 'This field is required',
9916 style : 'margin-right:5px;'
9920 this.el.addClass(this.invalidClass);
9922 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9924 var feedback = this.el.select('.form-control-feedback', true).first();
9927 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9929 if(this.getValue().length || this.forceFeedback){
9930 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9937 this.fireEvent('invalid', this, msg);
9945 * trigger field - base class for combo..
9950 * @class Roo.bootstrap.TriggerField
9951 * @extends Roo.bootstrap.Input
9952 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9953 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9954 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9955 * for which you can provide a custom implementation. For example:
9957 var trigger = new Roo.bootstrap.TriggerField();
9958 trigger.onTriggerClick = myTriggerFn;
9959 trigger.applyTo('my-field');
9962 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9963 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9964 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9965 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9966 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9969 * Create a new TriggerField.
9970 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9971 * to the base TextField)
9973 Roo.bootstrap.TriggerField = function(config){
9974 this.mimicing = false;
9975 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9978 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9980 * @cfg {String} triggerClass A CSS class to apply to the trigger
9983 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9988 * @cfg {Boolean} removable (true|false) special filter default false
9992 /** @cfg {Boolean} grow @hide */
9993 /** @cfg {Number} growMin @hide */
9994 /** @cfg {Number} growMax @hide */
10000 autoSize: Roo.emptyFn,
10004 deferHeight : true,
10007 actionMode : 'wrap',
10012 getAutoCreate : function(){
10014 var align = this.labelAlign || this.parentLabelAlign();
10019 cls: 'form-group' //input-group
10026 type : this.inputType,
10027 cls : 'form-control',
10028 autocomplete: 'new-password',
10029 placeholder : this.placeholder || ''
10033 input.name = this.name;
10036 input.cls += ' input-' + this.size;
10039 if (this.disabled) {
10040 input.disabled=true;
10043 var inputblock = input;
10045 if(this.hasFeedback && !this.allowBlank){
10049 cls: 'glyphicon form-control-feedback'
10052 if(this.removable && !this.editable && !this.tickable){
10054 cls : 'has-feedback',
10060 cls : 'roo-combo-removable-btn close'
10067 cls : 'has-feedback',
10076 if(this.removable && !this.editable && !this.tickable){
10078 cls : 'roo-removable',
10084 cls : 'roo-combo-removable-btn close'
10091 if (this.before || this.after) {
10094 cls : 'input-group',
10098 inputblock.cn.push({
10100 cls : 'input-group-addon',
10105 inputblock.cn.push(input);
10107 if(this.hasFeedback && !this.allowBlank){
10108 inputblock.cls += ' has-feedback';
10109 inputblock.cn.push(feedback);
10113 inputblock.cn.push({
10115 cls : 'input-group-addon',
10128 cls: 'form-hidden-field'
10142 cls: 'form-hidden-field'
10146 cls: 'roo-select2-choices',
10150 cls: 'roo-select2-search-field',
10163 cls: 'roo-select2-container input-group',
10168 // cls: 'typeahead typeahead-long dropdown-menu',
10169 // style: 'display:none'
10174 if(!this.multiple && this.showToggleBtn){
10180 if (this.caret != false) {
10183 cls: 'fa fa-' + this.caret
10190 cls : 'input-group-addon btn dropdown-toggle',
10195 cls: 'combobox-clear',
10209 combobox.cls += ' roo-select2-container-multi';
10212 if (align ==='left' && this.fieldLabel.length) {
10214 cfg.cls += ' roo-form-group-label-left';
10219 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10220 tooltip : 'This field is required'
10225 cls : 'control-label',
10226 html : this.fieldLabel
10238 var labelCfg = cfg.cn[1];
10239 var contentCfg = cfg.cn[2];
10241 if(this.indicatorpos == 'right'){
10246 cls : 'control-label',
10250 html : this.fieldLabel
10254 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10255 tooltip : 'This field is required'
10268 labelCfg = cfg.cn[0];
10269 contentCfg = cfg.cn[1];
10272 if(this.labelWidth > 12){
10273 labelCfg.style = "width: " + this.labelWidth + 'px';
10276 if(this.labelWidth < 13 && this.labelmd == 0){
10277 this.labelmd = this.labelWidth;
10280 if(this.labellg > 0){
10281 labelCfg.cls += ' col-lg-' + this.labellg;
10282 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10285 if(this.labelmd > 0){
10286 labelCfg.cls += ' col-md-' + this.labelmd;
10287 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10290 if(this.labelsm > 0){
10291 labelCfg.cls += ' col-sm-' + this.labelsm;
10292 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10295 if(this.labelxs > 0){
10296 labelCfg.cls += ' col-xs-' + this.labelxs;
10297 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10300 } else if ( this.fieldLabel.length) {
10301 // Roo.log(" label");
10305 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10306 tooltip : 'This field is required'
10310 //cls : 'input-group-addon',
10311 html : this.fieldLabel
10319 if(this.indicatorpos == 'right'){
10327 html : this.fieldLabel
10331 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10332 tooltip : 'This field is required'
10345 // Roo.log(" no label && no align");
10352 ['xs','sm','md','lg'].map(function(size){
10353 if (settings[size]) {
10354 cfg.cls += ' col-' + size + '-' + settings[size];
10365 onResize : function(w, h){
10366 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10367 // if(typeof w == 'number'){
10368 // var x = w - this.trigger.getWidth();
10369 // this.inputEl().setWidth(this.adjustWidth('input', x));
10370 // this.trigger.setStyle('left', x+'px');
10375 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10378 getResizeEl : function(){
10379 return this.inputEl();
10383 getPositionEl : function(){
10384 return this.inputEl();
10388 alignErrorIcon : function(){
10389 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10393 initEvents : function(){
10397 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10398 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10399 if(!this.multiple && this.showToggleBtn){
10400 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10401 if(this.hideTrigger){
10402 this.trigger.setDisplayed(false);
10404 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10408 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10411 if(this.removable && !this.editable && !this.tickable){
10412 var close = this.closeTriggerEl();
10415 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10416 close.on('click', this.removeBtnClick, this, close);
10420 //this.trigger.addClassOnOver('x-form-trigger-over');
10421 //this.trigger.addClassOnClick('x-form-trigger-click');
10424 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10428 closeTriggerEl : function()
10430 var close = this.el.select('.roo-combo-removable-btn', true).first();
10431 return close ? close : false;
10434 removeBtnClick : function(e, h, el)
10436 e.preventDefault();
10438 if(this.fireEvent("remove", this) !== false){
10440 this.fireEvent("afterremove", this)
10444 createList : function()
10446 this.list = Roo.get(document.body).createChild({
10448 cls: 'typeahead typeahead-long dropdown-menu',
10449 style: 'display:none'
10452 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10457 initTrigger : function(){
10462 onDestroy : function(){
10464 this.trigger.removeAllListeners();
10465 // this.trigger.remove();
10468 // this.wrap.remove();
10470 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10474 onFocus : function(){
10475 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10477 if(!this.mimicing){
10478 this.wrap.addClass('x-trigger-wrap-focus');
10479 this.mimicing = true;
10480 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10481 if(this.monitorTab){
10482 this.el.on("keydown", this.checkTab, this);
10489 checkTab : function(e){
10490 if(e.getKey() == e.TAB){
10491 this.triggerBlur();
10496 onBlur : function(){
10501 mimicBlur : function(e, t){
10503 if(!this.wrap.contains(t) && this.validateBlur()){
10504 this.triggerBlur();
10510 triggerBlur : function(){
10511 this.mimicing = false;
10512 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10513 if(this.monitorTab){
10514 this.el.un("keydown", this.checkTab, this);
10516 //this.wrap.removeClass('x-trigger-wrap-focus');
10517 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10521 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10522 validateBlur : function(e, t){
10527 onDisable : function(){
10528 this.inputEl().dom.disabled = true;
10529 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10531 // this.wrap.addClass('x-item-disabled');
10536 onEnable : function(){
10537 this.inputEl().dom.disabled = false;
10538 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10540 // this.el.removeClass('x-item-disabled');
10545 onShow : function(){
10546 var ae = this.getActionEl();
10549 ae.dom.style.display = '';
10550 ae.dom.style.visibility = 'visible';
10556 onHide : function(){
10557 var ae = this.getActionEl();
10558 ae.dom.style.display = 'none';
10562 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10563 * by an implementing function.
10565 * @param {EventObject} e
10567 onTriggerClick : Roo.emptyFn
10571 * Ext JS Library 1.1.1
10572 * Copyright(c) 2006-2007, Ext JS, LLC.
10574 * Originally Released Under LGPL - original licence link has changed is not relivant.
10577 * <script type="text/javascript">
10582 * @class Roo.data.SortTypes
10584 * Defines the default sorting (casting?) comparison functions used when sorting data.
10586 Roo.data.SortTypes = {
10588 * Default sort that does nothing
10589 * @param {Mixed} s The value being converted
10590 * @return {Mixed} The comparison value
10592 none : function(s){
10597 * The regular expression used to strip tags
10601 stripTagsRE : /<\/?[^>]+>/gi,
10604 * Strips all HTML tags to sort on text only
10605 * @param {Mixed} s The value being converted
10606 * @return {String} The comparison value
10608 asText : function(s){
10609 return String(s).replace(this.stripTagsRE, "");
10613 * Strips all HTML tags to sort on text only - Case insensitive
10614 * @param {Mixed} s The value being converted
10615 * @return {String} The comparison value
10617 asUCText : function(s){
10618 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10622 * Case insensitive string
10623 * @param {Mixed} s The value being converted
10624 * @return {String} The comparison value
10626 asUCString : function(s) {
10627 return String(s).toUpperCase();
10632 * @param {Mixed} s The value being converted
10633 * @return {Number} The comparison value
10635 asDate : function(s) {
10639 if(s instanceof Date){
10640 return s.getTime();
10642 return Date.parse(String(s));
10647 * @param {Mixed} s The value being converted
10648 * @return {Float} The comparison value
10650 asFloat : function(s) {
10651 var val = parseFloat(String(s).replace(/,/g, ""));
10660 * @param {Mixed} s The value being converted
10661 * @return {Number} The comparison value
10663 asInt : function(s) {
10664 var val = parseInt(String(s).replace(/,/g, ""));
10672 * Ext JS Library 1.1.1
10673 * Copyright(c) 2006-2007, Ext JS, LLC.
10675 * Originally Released Under LGPL - original licence link has changed is not relivant.
10678 * <script type="text/javascript">
10682 * @class Roo.data.Record
10683 * Instances of this class encapsulate both record <em>definition</em> information, and record
10684 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10685 * to access Records cached in an {@link Roo.data.Store} object.<br>
10687 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10688 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10691 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10693 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10694 * {@link #create}. The parameters are the same.
10695 * @param {Array} data An associative Array of data values keyed by the field name.
10696 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10697 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10698 * not specified an integer id is generated.
10700 Roo.data.Record = function(data, id){
10701 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10706 * Generate a constructor for a specific record layout.
10707 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10708 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10709 * Each field definition object may contain the following properties: <ul>
10710 * <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,
10711 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10712 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10713 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10714 * is being used, then this is a string containing the javascript expression to reference the data relative to
10715 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10716 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10717 * this may be omitted.</p></li>
10718 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10719 * <ul><li>auto (Default, implies no conversion)</li>
10724 * <li>date</li></ul></p></li>
10725 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10726 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10727 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10728 * by the Reader into an object that will be stored in the Record. It is passed the
10729 * following parameters:<ul>
10730 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10732 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10734 * <br>usage:<br><pre><code>
10735 var TopicRecord = Roo.data.Record.create(
10736 {name: 'title', mapping: 'topic_title'},
10737 {name: 'author', mapping: 'username'},
10738 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10739 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10740 {name: 'lastPoster', mapping: 'user2'},
10741 {name: 'excerpt', mapping: 'post_text'}
10744 var myNewRecord = new TopicRecord({
10745 title: 'Do my job please',
10748 lastPost: new Date(),
10749 lastPoster: 'Animal',
10750 excerpt: 'No way dude!'
10752 myStore.add(myNewRecord);
10757 Roo.data.Record.create = function(o){
10758 var f = function(){
10759 f.superclass.constructor.apply(this, arguments);
10761 Roo.extend(f, Roo.data.Record);
10762 var p = f.prototype;
10763 p.fields = new Roo.util.MixedCollection(false, function(field){
10766 for(var i = 0, len = o.length; i < len; i++){
10767 p.fields.add(new Roo.data.Field(o[i]));
10769 f.getField = function(name){
10770 return p.fields.get(name);
10775 Roo.data.Record.AUTO_ID = 1000;
10776 Roo.data.Record.EDIT = 'edit';
10777 Roo.data.Record.REJECT = 'reject';
10778 Roo.data.Record.COMMIT = 'commit';
10780 Roo.data.Record.prototype = {
10782 * Readonly flag - true if this record has been modified.
10791 join : function(store){
10792 this.store = store;
10796 * Set the named field to the specified value.
10797 * @param {String} name The name of the field to set.
10798 * @param {Object} value The value to set the field to.
10800 set : function(name, value){
10801 if(this.data[name] == value){
10805 if(!this.modified){
10806 this.modified = {};
10808 if(typeof this.modified[name] == 'undefined'){
10809 this.modified[name] = this.data[name];
10811 this.data[name] = value;
10812 if(!this.editing && this.store){
10813 this.store.afterEdit(this);
10818 * Get the value of the named field.
10819 * @param {String} name The name of the field to get the value of.
10820 * @return {Object} The value of the field.
10822 get : function(name){
10823 return this.data[name];
10827 beginEdit : function(){
10828 this.editing = true;
10829 this.modified = {};
10833 cancelEdit : function(){
10834 this.editing = false;
10835 delete this.modified;
10839 endEdit : function(){
10840 this.editing = false;
10841 if(this.dirty && this.store){
10842 this.store.afterEdit(this);
10847 * Usually called by the {@link Roo.data.Store} which owns the Record.
10848 * Rejects all changes made to the Record since either creation, or the last commit operation.
10849 * Modified fields are reverted to their original values.
10851 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10852 * of reject operations.
10854 reject : function(){
10855 var m = this.modified;
10857 if(typeof m[n] != "function"){
10858 this.data[n] = m[n];
10861 this.dirty = false;
10862 delete this.modified;
10863 this.editing = false;
10865 this.store.afterReject(this);
10870 * Usually called by the {@link Roo.data.Store} which owns the Record.
10871 * Commits all changes made to the Record since either creation, or the last commit operation.
10873 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10874 * of commit operations.
10876 commit : function(){
10877 this.dirty = false;
10878 delete this.modified;
10879 this.editing = false;
10881 this.store.afterCommit(this);
10886 hasError : function(){
10887 return this.error != null;
10891 clearError : function(){
10896 * Creates a copy of this record.
10897 * @param {String} id (optional) A new record id if you don't want to use this record's id
10900 copy : function(newId) {
10901 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10905 * Ext JS Library 1.1.1
10906 * Copyright(c) 2006-2007, Ext JS, LLC.
10908 * Originally Released Under LGPL - original licence link has changed is not relivant.
10911 * <script type="text/javascript">
10917 * @class Roo.data.Store
10918 * @extends Roo.util.Observable
10919 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10920 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10922 * 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
10923 * has no knowledge of the format of the data returned by the Proxy.<br>
10925 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10926 * instances from the data object. These records are cached and made available through accessor functions.
10928 * Creates a new Store.
10929 * @param {Object} config A config object containing the objects needed for the Store to access data,
10930 * and read the data into Records.
10932 Roo.data.Store = function(config){
10933 this.data = new Roo.util.MixedCollection(false);
10934 this.data.getKey = function(o){
10937 this.baseParams = {};
10939 this.paramNames = {
10944 "multisort" : "_multisort"
10947 if(config && config.data){
10948 this.inlineData = config.data;
10949 delete config.data;
10952 Roo.apply(this, config);
10954 if(this.reader){ // reader passed
10955 this.reader = Roo.factory(this.reader, Roo.data);
10956 this.reader.xmodule = this.xmodule || false;
10957 if(!this.recordType){
10958 this.recordType = this.reader.recordType;
10960 if(this.reader.onMetaChange){
10961 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10965 if(this.recordType){
10966 this.fields = this.recordType.prototype.fields;
10968 this.modified = [];
10972 * @event datachanged
10973 * Fires when the data cache has changed, and a widget which is using this Store
10974 * as a Record cache should refresh its view.
10975 * @param {Store} this
10977 datachanged : true,
10979 * @event metachange
10980 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10981 * @param {Store} this
10982 * @param {Object} meta The JSON metadata
10987 * Fires when Records have been added to the Store
10988 * @param {Store} this
10989 * @param {Roo.data.Record[]} records The array of Records added
10990 * @param {Number} index The index at which the record(s) were added
10995 * Fires when a Record has been removed from the Store
10996 * @param {Store} this
10997 * @param {Roo.data.Record} record The Record that was removed
10998 * @param {Number} index The index at which the record was removed
11003 * Fires when a Record has been updated
11004 * @param {Store} this
11005 * @param {Roo.data.Record} record The Record that was updated
11006 * @param {String} operation The update operation being performed. Value may be one of:
11008 Roo.data.Record.EDIT
11009 Roo.data.Record.REJECT
11010 Roo.data.Record.COMMIT
11016 * Fires when the data cache has been cleared.
11017 * @param {Store} this
11021 * @event beforeload
11022 * Fires before a request is made for a new data object. If the beforeload handler returns false
11023 * the load action will be canceled.
11024 * @param {Store} this
11025 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11029 * @event beforeloadadd
11030 * Fires after a new set of Records has been loaded.
11031 * @param {Store} this
11032 * @param {Roo.data.Record[]} records The Records that were loaded
11033 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11035 beforeloadadd : true,
11038 * Fires after a new set of Records has been loaded, before they are added to the store.
11039 * @param {Store} this
11040 * @param {Roo.data.Record[]} records The Records that were loaded
11041 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11042 * @params {Object} return from reader
11046 * @event loadexception
11047 * Fires if an exception occurs in the Proxy during loading.
11048 * Called with the signature of the Proxy's "loadexception" event.
11049 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11052 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11053 * @param {Object} load options
11054 * @param {Object} jsonData from your request (normally this contains the Exception)
11056 loadexception : true
11060 this.proxy = Roo.factory(this.proxy, Roo.data);
11061 this.proxy.xmodule = this.xmodule || false;
11062 this.relayEvents(this.proxy, ["loadexception"]);
11064 this.sortToggle = {};
11065 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11067 Roo.data.Store.superclass.constructor.call(this);
11069 if(this.inlineData){
11070 this.loadData(this.inlineData);
11071 delete this.inlineData;
11075 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11077 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11078 * without a remote query - used by combo/forms at present.
11082 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11085 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11088 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11089 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11092 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11093 * on any HTTP request
11096 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11099 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11103 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11104 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11106 remoteSort : false,
11109 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11110 * loaded or when a record is removed. (defaults to false).
11112 pruneModifiedRecords : false,
11115 lastOptions : null,
11118 * Add Records to the Store and fires the add event.
11119 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11121 add : function(records){
11122 records = [].concat(records);
11123 for(var i = 0, len = records.length; i < len; i++){
11124 records[i].join(this);
11126 var index = this.data.length;
11127 this.data.addAll(records);
11128 this.fireEvent("add", this, records, index);
11132 * Remove a Record from the Store and fires the remove event.
11133 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11135 remove : function(record){
11136 var index = this.data.indexOf(record);
11137 this.data.removeAt(index);
11139 if(this.pruneModifiedRecords){
11140 this.modified.remove(record);
11142 this.fireEvent("remove", this, record, index);
11146 * Remove all Records from the Store and fires the clear event.
11148 removeAll : function(){
11150 if(this.pruneModifiedRecords){
11151 this.modified = [];
11153 this.fireEvent("clear", this);
11157 * Inserts Records to the Store at the given index and fires the add event.
11158 * @param {Number} index The start index at which to insert the passed Records.
11159 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11161 insert : function(index, records){
11162 records = [].concat(records);
11163 for(var i = 0, len = records.length; i < len; i++){
11164 this.data.insert(index, records[i]);
11165 records[i].join(this);
11167 this.fireEvent("add", this, records, index);
11171 * Get the index within the cache of the passed Record.
11172 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11173 * @return {Number} The index of the passed Record. Returns -1 if not found.
11175 indexOf : function(record){
11176 return this.data.indexOf(record);
11180 * Get the index within the cache of the Record with the passed id.
11181 * @param {String} id The id of the Record to find.
11182 * @return {Number} The index of the Record. Returns -1 if not found.
11184 indexOfId : function(id){
11185 return this.data.indexOfKey(id);
11189 * Get the Record with the specified id.
11190 * @param {String} id The id of the Record to find.
11191 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11193 getById : function(id){
11194 return this.data.key(id);
11198 * Get the Record at the specified index.
11199 * @param {Number} index The index of the Record to find.
11200 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11202 getAt : function(index){
11203 return this.data.itemAt(index);
11207 * Returns a range of Records between specified indices.
11208 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11209 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11210 * @return {Roo.data.Record[]} An array of Records
11212 getRange : function(start, end){
11213 return this.data.getRange(start, end);
11217 storeOptions : function(o){
11218 o = Roo.apply({}, o);
11221 this.lastOptions = o;
11225 * Loads the Record cache from the configured Proxy using the configured Reader.
11227 * If using remote paging, then the first load call must specify the <em>start</em>
11228 * and <em>limit</em> properties in the options.params property to establish the initial
11229 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11231 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11232 * and this call will return before the new data has been loaded. Perform any post-processing
11233 * in a callback function, or in a "load" event handler.</strong>
11235 * @param {Object} options An object containing properties which control loading options:<ul>
11236 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11237 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11238 * passed the following arguments:<ul>
11239 * <li>r : Roo.data.Record[]</li>
11240 * <li>options: Options object from the load call</li>
11241 * <li>success: Boolean success indicator</li></ul></li>
11242 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11243 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11246 load : function(options){
11247 options = options || {};
11248 if(this.fireEvent("beforeload", this, options) !== false){
11249 this.storeOptions(options);
11250 var p = Roo.apply(options.params || {}, this.baseParams);
11251 // if meta was not loaded from remote source.. try requesting it.
11252 if (!this.reader.metaFromRemote) {
11253 p._requestMeta = 1;
11255 if(this.sortInfo && this.remoteSort){
11256 var pn = this.paramNames;
11257 p[pn["sort"]] = this.sortInfo.field;
11258 p[pn["dir"]] = this.sortInfo.direction;
11260 if (this.multiSort) {
11261 var pn = this.paramNames;
11262 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11265 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11270 * Reloads the Record cache from the configured Proxy using the configured Reader and
11271 * the options from the last load operation performed.
11272 * @param {Object} options (optional) An object containing properties which may override the options
11273 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11274 * the most recently used options are reused).
11276 reload : function(options){
11277 this.load(Roo.applyIf(options||{}, this.lastOptions));
11281 // Called as a callback by the Reader during a load operation.
11282 loadRecords : function(o, options, success){
11283 if(!o || success === false){
11284 if(success !== false){
11285 this.fireEvent("load", this, [], options, o);
11287 if(options.callback){
11288 options.callback.call(options.scope || this, [], options, false);
11292 // if data returned failure - throw an exception.
11293 if (o.success === false) {
11294 // show a message if no listener is registered.
11295 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11296 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11298 // loadmask wil be hooked into this..
11299 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11302 var r = o.records, t = o.totalRecords || r.length;
11304 this.fireEvent("beforeloadadd", this, r, options, o);
11306 if(!options || options.add !== true){
11307 if(this.pruneModifiedRecords){
11308 this.modified = [];
11310 for(var i = 0, len = r.length; i < len; i++){
11314 this.data = this.snapshot;
11315 delete this.snapshot;
11318 this.data.addAll(r);
11319 this.totalLength = t;
11321 this.fireEvent("datachanged", this);
11323 this.totalLength = Math.max(t, this.data.length+r.length);
11327 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11329 var e = new Roo.data.Record({});
11331 e.set(this.parent.displayField, this.parent.emptyTitle);
11332 e.set(this.parent.valueField, '');
11337 this.fireEvent("load", this, r, options, o);
11338 if(options.callback){
11339 options.callback.call(options.scope || this, r, options, true);
11345 * Loads data from a passed data block. A Reader which understands the format of the data
11346 * must have been configured in the constructor.
11347 * @param {Object} data The data block from which to read the Records. The format of the data expected
11348 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11349 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11351 loadData : function(o, append){
11352 var r = this.reader.readRecords(o);
11353 this.loadRecords(r, {add: append}, true);
11357 * Gets the number of cached records.
11359 * <em>If using paging, this may not be the total size of the dataset. If the data object
11360 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11361 * the data set size</em>
11363 getCount : function(){
11364 return this.data.length || 0;
11368 * Gets the total number of records in the dataset as returned by the server.
11370 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11371 * the dataset size</em>
11373 getTotalCount : function(){
11374 return this.totalLength || 0;
11378 * Returns the sort state of the Store as an object with two properties:
11380 field {String} The name of the field by which the Records are sorted
11381 direction {String} The sort order, "ASC" or "DESC"
11384 getSortState : function(){
11385 return this.sortInfo;
11389 applySort : function(){
11390 if(this.sortInfo && !this.remoteSort){
11391 var s = this.sortInfo, f = s.field;
11392 var st = this.fields.get(f).sortType;
11393 var fn = function(r1, r2){
11394 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11395 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11397 this.data.sort(s.direction, fn);
11398 if(this.snapshot && this.snapshot != this.data){
11399 this.snapshot.sort(s.direction, fn);
11405 * Sets the default sort column and order to be used by the next load operation.
11406 * @param {String} fieldName The name of the field to sort by.
11407 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11409 setDefaultSort : function(field, dir){
11410 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11414 * Sort the Records.
11415 * If remote sorting is used, the sort is performed on the server, and the cache is
11416 * reloaded. If local sorting is used, the cache is sorted internally.
11417 * @param {String} fieldName The name of the field to sort by.
11418 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11420 sort : function(fieldName, dir){
11421 var f = this.fields.get(fieldName);
11423 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11425 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11426 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11431 this.sortToggle[f.name] = dir;
11432 this.sortInfo = {field: f.name, direction: dir};
11433 if(!this.remoteSort){
11435 this.fireEvent("datachanged", this);
11437 this.load(this.lastOptions);
11442 * Calls the specified function for each of the Records in the cache.
11443 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11444 * Returning <em>false</em> aborts and exits the iteration.
11445 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11447 each : function(fn, scope){
11448 this.data.each(fn, scope);
11452 * Gets all records modified since the last commit. Modified records are persisted across load operations
11453 * (e.g., during paging).
11454 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11456 getModifiedRecords : function(){
11457 return this.modified;
11461 createFilterFn : function(property, value, anyMatch){
11462 if(!value.exec){ // not a regex
11463 value = String(value);
11464 if(value.length == 0){
11467 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11469 return function(r){
11470 return value.test(r.data[property]);
11475 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11476 * @param {String} property A field on your records
11477 * @param {Number} start The record index to start at (defaults to 0)
11478 * @param {Number} end The last record index to include (defaults to length - 1)
11479 * @return {Number} The sum
11481 sum : function(property, start, end){
11482 var rs = this.data.items, v = 0;
11483 start = start || 0;
11484 end = (end || end === 0) ? end : rs.length-1;
11486 for(var i = start; i <= end; i++){
11487 v += (rs[i].data[property] || 0);
11493 * Filter the records by a specified property.
11494 * @param {String} field A field on your records
11495 * @param {String/RegExp} value Either a string that the field
11496 * should start with or a RegExp to test against the field
11497 * @param {Boolean} anyMatch True to match any part not just the beginning
11499 filter : function(property, value, anyMatch){
11500 var fn = this.createFilterFn(property, value, anyMatch);
11501 return fn ? this.filterBy(fn) : this.clearFilter();
11505 * Filter by a function. The specified function will be called with each
11506 * record in this data source. If the function returns true the record is included,
11507 * otherwise it is filtered.
11508 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11509 * @param {Object} scope (optional) The scope of the function (defaults to this)
11511 filterBy : function(fn, scope){
11512 this.snapshot = this.snapshot || this.data;
11513 this.data = this.queryBy(fn, scope||this);
11514 this.fireEvent("datachanged", this);
11518 * Query the records by a specified property.
11519 * @param {String} field A field on your records
11520 * @param {String/RegExp} value Either a string that the field
11521 * should start with or a RegExp to test against the field
11522 * @param {Boolean} anyMatch True to match any part not just the beginning
11523 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11525 query : function(property, value, anyMatch){
11526 var fn = this.createFilterFn(property, value, anyMatch);
11527 return fn ? this.queryBy(fn) : this.data.clone();
11531 * Query by a function. The specified function will be called with each
11532 * record in this data source. If the function returns true the record is included
11534 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11535 * @param {Object} scope (optional) The scope of the function (defaults to this)
11536 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11538 queryBy : function(fn, scope){
11539 var data = this.snapshot || this.data;
11540 return data.filterBy(fn, scope||this);
11544 * Collects unique values for a particular dataIndex from this store.
11545 * @param {String} dataIndex The property to collect
11546 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11547 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11548 * @return {Array} An array of the unique values
11550 collect : function(dataIndex, allowNull, bypassFilter){
11551 var d = (bypassFilter === true && this.snapshot) ?
11552 this.snapshot.items : this.data.items;
11553 var v, sv, r = [], l = {};
11554 for(var i = 0, len = d.length; i < len; i++){
11555 v = d[i].data[dataIndex];
11557 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11566 * Revert to a view of the Record cache with no filtering applied.
11567 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11569 clearFilter : function(suppressEvent){
11570 if(this.snapshot && this.snapshot != this.data){
11571 this.data = this.snapshot;
11572 delete this.snapshot;
11573 if(suppressEvent !== true){
11574 this.fireEvent("datachanged", this);
11580 afterEdit : function(record){
11581 if(this.modified.indexOf(record) == -1){
11582 this.modified.push(record);
11584 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11588 afterReject : function(record){
11589 this.modified.remove(record);
11590 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11594 afterCommit : function(record){
11595 this.modified.remove(record);
11596 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11600 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11601 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11603 commitChanges : function(){
11604 var m = this.modified.slice(0);
11605 this.modified = [];
11606 for(var i = 0, len = m.length; i < len; i++){
11612 * Cancel outstanding changes on all changed records.
11614 rejectChanges : function(){
11615 var m = this.modified.slice(0);
11616 this.modified = [];
11617 for(var i = 0, len = m.length; i < len; i++){
11622 onMetaChange : function(meta, rtype, o){
11623 this.recordType = rtype;
11624 this.fields = rtype.prototype.fields;
11625 delete this.snapshot;
11626 this.sortInfo = meta.sortInfo || this.sortInfo;
11627 this.modified = [];
11628 this.fireEvent('metachange', this, this.reader.meta);
11631 moveIndex : function(data, type)
11633 var index = this.indexOf(data);
11635 var newIndex = index + type;
11639 this.insert(newIndex, data);
11644 * Ext JS Library 1.1.1
11645 * Copyright(c) 2006-2007, Ext JS, LLC.
11647 * Originally Released Under LGPL - original licence link has changed is not relivant.
11650 * <script type="text/javascript">
11654 * @class Roo.data.SimpleStore
11655 * @extends Roo.data.Store
11656 * Small helper class to make creating Stores from Array data easier.
11657 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11658 * @cfg {Array} fields An array of field definition objects, or field name strings.
11659 * @cfg {Array} data The multi-dimensional array of data
11661 * @param {Object} config
11663 Roo.data.SimpleStore = function(config){
11664 Roo.data.SimpleStore.superclass.constructor.call(this, {
11666 reader: new Roo.data.ArrayReader({
11669 Roo.data.Record.create(config.fields)
11671 proxy : new Roo.data.MemoryProxy(config.data)
11675 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11677 * Ext JS Library 1.1.1
11678 * Copyright(c) 2006-2007, Ext JS, LLC.
11680 * Originally Released Under LGPL - original licence link has changed is not relivant.
11683 * <script type="text/javascript">
11688 * @extends Roo.data.Store
11689 * @class Roo.data.JsonStore
11690 * Small helper class to make creating Stores for JSON data easier. <br/>
11692 var store = new Roo.data.JsonStore({
11693 url: 'get-images.php',
11695 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11698 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11699 * JsonReader and HttpProxy (unless inline data is provided).</b>
11700 * @cfg {Array} fields An array of field definition objects, or field name strings.
11702 * @param {Object} config
11704 Roo.data.JsonStore = function(c){
11705 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11706 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11707 reader: new Roo.data.JsonReader(c, c.fields)
11710 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11712 * Ext JS Library 1.1.1
11713 * Copyright(c) 2006-2007, Ext JS, LLC.
11715 * Originally Released Under LGPL - original licence link has changed is not relivant.
11718 * <script type="text/javascript">
11722 Roo.data.Field = function(config){
11723 if(typeof config == "string"){
11724 config = {name: config};
11726 Roo.apply(this, config);
11729 this.type = "auto";
11732 var st = Roo.data.SortTypes;
11733 // named sortTypes are supported, here we look them up
11734 if(typeof this.sortType == "string"){
11735 this.sortType = st[this.sortType];
11738 // set default sortType for strings and dates
11739 if(!this.sortType){
11742 this.sortType = st.asUCString;
11745 this.sortType = st.asDate;
11748 this.sortType = st.none;
11753 var stripRe = /[\$,%]/g;
11755 // prebuilt conversion function for this field, instead of
11756 // switching every time we're reading a value
11758 var cv, dateFormat = this.dateFormat;
11763 cv = function(v){ return v; };
11766 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11770 return v !== undefined && v !== null && v !== '' ?
11771 parseInt(String(v).replace(stripRe, ""), 10) : '';
11776 return v !== undefined && v !== null && v !== '' ?
11777 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11782 cv = function(v){ return v === true || v === "true" || v == 1; };
11789 if(v instanceof Date){
11793 if(dateFormat == "timestamp"){
11794 return new Date(v*1000);
11796 return Date.parseDate(v, dateFormat);
11798 var parsed = Date.parse(v);
11799 return parsed ? new Date(parsed) : null;
11808 Roo.data.Field.prototype = {
11816 * Ext JS Library 1.1.1
11817 * Copyright(c) 2006-2007, Ext JS, LLC.
11819 * Originally Released Under LGPL - original licence link has changed is not relivant.
11822 * <script type="text/javascript">
11825 // Base class for reading structured data from a data source. This class is intended to be
11826 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11829 * @class Roo.data.DataReader
11830 * Base class for reading structured data from a data source. This class is intended to be
11831 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11834 Roo.data.DataReader = function(meta, recordType){
11838 this.recordType = recordType instanceof Array ?
11839 Roo.data.Record.create(recordType) : recordType;
11842 Roo.data.DataReader.prototype = {
11844 * Create an empty record
11845 * @param {Object} data (optional) - overlay some values
11846 * @return {Roo.data.Record} record created.
11848 newRow : function(d) {
11850 this.recordType.prototype.fields.each(function(c) {
11852 case 'int' : da[c.name] = 0; break;
11853 case 'date' : da[c.name] = new Date(); break;
11854 case 'float' : da[c.name] = 0.0; break;
11855 case 'boolean' : da[c.name] = false; break;
11856 default : da[c.name] = ""; break;
11860 return new this.recordType(Roo.apply(da, d));
11865 * Ext JS Library 1.1.1
11866 * Copyright(c) 2006-2007, Ext JS, LLC.
11868 * Originally Released Under LGPL - original licence link has changed is not relivant.
11871 * <script type="text/javascript">
11875 * @class Roo.data.DataProxy
11876 * @extends Roo.data.Observable
11877 * This class is an abstract base class for implementations which provide retrieval of
11878 * unformatted data objects.<br>
11880 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11881 * (of the appropriate type which knows how to parse the data object) to provide a block of
11882 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11884 * Custom implementations must implement the load method as described in
11885 * {@link Roo.data.HttpProxy#load}.
11887 Roo.data.DataProxy = function(){
11890 * @event beforeload
11891 * Fires before a network request is made to retrieve a data object.
11892 * @param {Object} This DataProxy object.
11893 * @param {Object} params The params parameter to the load function.
11898 * Fires before the load method's callback is called.
11899 * @param {Object} This DataProxy object.
11900 * @param {Object} o The data object.
11901 * @param {Object} arg The callback argument object passed to the load function.
11905 * @event loadexception
11906 * Fires if an Exception occurs during data retrieval.
11907 * @param {Object} This DataProxy object.
11908 * @param {Object} o The data object.
11909 * @param {Object} arg The callback argument object passed to the load function.
11910 * @param {Object} e The Exception.
11912 loadexception : true
11914 Roo.data.DataProxy.superclass.constructor.call(this);
11917 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11920 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11924 * Ext JS Library 1.1.1
11925 * Copyright(c) 2006-2007, Ext JS, LLC.
11927 * Originally Released Under LGPL - original licence link has changed is not relivant.
11930 * <script type="text/javascript">
11933 * @class Roo.data.MemoryProxy
11934 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11935 * to the Reader when its load method is called.
11937 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11939 Roo.data.MemoryProxy = function(data){
11943 Roo.data.MemoryProxy.superclass.constructor.call(this);
11947 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11950 * Load data from the requested source (in this case an in-memory
11951 * data object passed to the constructor), read the data object into
11952 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11953 * process that block using the passed callback.
11954 * @param {Object} params This parameter is not used by the MemoryProxy class.
11955 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11956 * object into a block of Roo.data.Records.
11957 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11958 * The function must be passed <ul>
11959 * <li>The Record block object</li>
11960 * <li>The "arg" argument from the load function</li>
11961 * <li>A boolean success indicator</li>
11963 * @param {Object} scope The scope in which to call the callback
11964 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11966 load : function(params, reader, callback, scope, arg){
11967 params = params || {};
11970 result = reader.readRecords(this.data);
11972 this.fireEvent("loadexception", this, arg, null, e);
11973 callback.call(scope, null, arg, false);
11976 callback.call(scope, result, arg, true);
11980 update : function(params, records){
11985 * Ext JS Library 1.1.1
11986 * Copyright(c) 2006-2007, Ext JS, LLC.
11988 * Originally Released Under LGPL - original licence link has changed is not relivant.
11991 * <script type="text/javascript">
11994 * @class Roo.data.HttpProxy
11995 * @extends Roo.data.DataProxy
11996 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11997 * configured to reference a certain URL.<br><br>
11999 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12000 * from which the running page was served.<br><br>
12002 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12004 * Be aware that to enable the browser to parse an XML document, the server must set
12005 * the Content-Type header in the HTTP response to "text/xml".
12007 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12008 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12009 * will be used to make the request.
12011 Roo.data.HttpProxy = function(conn){
12012 Roo.data.HttpProxy.superclass.constructor.call(this);
12013 // is conn a conn config or a real conn?
12015 this.useAjax = !conn || !conn.events;
12019 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12020 // thse are take from connection...
12023 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12026 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12027 * extra parameters to each request made by this object. (defaults to undefined)
12030 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12031 * to each request made by this object. (defaults to undefined)
12034 * @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)
12037 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12040 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12046 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12050 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12051 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12052 * a finer-grained basis than the DataProxy events.
12054 getConnection : function(){
12055 return this.useAjax ? Roo.Ajax : this.conn;
12059 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12060 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12061 * process that block using the passed callback.
12062 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12063 * for the request to the remote server.
12064 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12065 * object into a block of Roo.data.Records.
12066 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12067 * The function must be passed <ul>
12068 * <li>The Record block object</li>
12069 * <li>The "arg" argument from the load function</li>
12070 * <li>A boolean success indicator</li>
12072 * @param {Object} scope The scope in which to call the callback
12073 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12075 load : function(params, reader, callback, scope, arg){
12076 if(this.fireEvent("beforeload", this, params) !== false){
12078 params : params || {},
12080 callback : callback,
12085 callback : this.loadResponse,
12089 Roo.applyIf(o, this.conn);
12090 if(this.activeRequest){
12091 Roo.Ajax.abort(this.activeRequest);
12093 this.activeRequest = Roo.Ajax.request(o);
12095 this.conn.request(o);
12098 callback.call(scope||this, null, arg, false);
12103 loadResponse : function(o, success, response){
12104 delete this.activeRequest;
12106 this.fireEvent("loadexception", this, o, response);
12107 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12112 result = o.reader.read(response);
12114 this.fireEvent("loadexception", this, o, response, e);
12115 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12119 this.fireEvent("load", this, o, o.request.arg);
12120 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12124 update : function(dataSet){
12129 updateResponse : function(dataSet){
12134 * Ext JS Library 1.1.1
12135 * Copyright(c) 2006-2007, Ext JS, LLC.
12137 * Originally Released Under LGPL - original licence link has changed is not relivant.
12140 * <script type="text/javascript">
12144 * @class Roo.data.ScriptTagProxy
12145 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12146 * other than the originating domain of the running page.<br><br>
12148 * <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
12149 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12151 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12152 * source code that is used as the source inside a <script> tag.<br><br>
12154 * In order for the browser to process the returned data, the server must wrap the data object
12155 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12156 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12157 * depending on whether the callback name was passed:
12160 boolean scriptTag = false;
12161 String cb = request.getParameter("callback");
12164 response.setContentType("text/javascript");
12166 response.setContentType("application/x-json");
12168 Writer out = response.getWriter();
12170 out.write(cb + "(");
12172 out.print(dataBlock.toJsonString());
12179 * @param {Object} config A configuration object.
12181 Roo.data.ScriptTagProxy = function(config){
12182 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12183 Roo.apply(this, config);
12184 this.head = document.getElementsByTagName("head")[0];
12187 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12189 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12191 * @cfg {String} url The URL from which to request the data object.
12194 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12198 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12199 * the server the name of the callback function set up by the load call to process the returned data object.
12200 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12201 * javascript output which calls this named function passing the data object as its only parameter.
12203 callbackParam : "callback",
12205 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12206 * name to the request.
12211 * Load data from the configured URL, read the data object into
12212 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12213 * process that block using the passed callback.
12214 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12215 * for the request to the remote server.
12216 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12217 * object into a block of Roo.data.Records.
12218 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12219 * The function must be passed <ul>
12220 * <li>The Record block object</li>
12221 * <li>The "arg" argument from the load function</li>
12222 * <li>A boolean success indicator</li>
12224 * @param {Object} scope The scope in which to call the callback
12225 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12227 load : function(params, reader, callback, scope, arg){
12228 if(this.fireEvent("beforeload", this, params) !== false){
12230 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12232 var url = this.url;
12233 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12235 url += "&_dc=" + (new Date().getTime());
12237 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12240 cb : "stcCallback"+transId,
12241 scriptId : "stcScript"+transId,
12245 callback : callback,
12251 window[trans.cb] = function(o){
12252 conn.handleResponse(o, trans);
12255 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12257 if(this.autoAbort !== false){
12261 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12263 var script = document.createElement("script");
12264 script.setAttribute("src", url);
12265 script.setAttribute("type", "text/javascript");
12266 script.setAttribute("id", trans.scriptId);
12267 this.head.appendChild(script);
12269 this.trans = trans;
12271 callback.call(scope||this, null, arg, false);
12276 isLoading : function(){
12277 return this.trans ? true : false;
12281 * Abort the current server request.
12283 abort : function(){
12284 if(this.isLoading()){
12285 this.destroyTrans(this.trans);
12290 destroyTrans : function(trans, isLoaded){
12291 this.head.removeChild(document.getElementById(trans.scriptId));
12292 clearTimeout(trans.timeoutId);
12294 window[trans.cb] = undefined;
12296 delete window[trans.cb];
12299 // if hasn't been loaded, wait for load to remove it to prevent script error
12300 window[trans.cb] = function(){
12301 window[trans.cb] = undefined;
12303 delete window[trans.cb];
12310 handleResponse : function(o, trans){
12311 this.trans = false;
12312 this.destroyTrans(trans, true);
12315 result = trans.reader.readRecords(o);
12317 this.fireEvent("loadexception", this, o, trans.arg, e);
12318 trans.callback.call(trans.scope||window, null, trans.arg, false);
12321 this.fireEvent("load", this, o, trans.arg);
12322 trans.callback.call(trans.scope||window, result, trans.arg, true);
12326 handleFailure : function(trans){
12327 this.trans = false;
12328 this.destroyTrans(trans, false);
12329 this.fireEvent("loadexception", this, null, trans.arg);
12330 trans.callback.call(trans.scope||window, null, trans.arg, false);
12334 * Ext JS Library 1.1.1
12335 * Copyright(c) 2006-2007, Ext JS, LLC.
12337 * Originally Released Under LGPL - original licence link has changed is not relivant.
12340 * <script type="text/javascript">
12344 * @class Roo.data.JsonReader
12345 * @extends Roo.data.DataReader
12346 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12347 * based on mappings in a provided Roo.data.Record constructor.
12349 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12350 * in the reply previously.
12355 var RecordDef = Roo.data.Record.create([
12356 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12357 {name: 'occupation'} // This field will use "occupation" as the mapping.
12359 var myReader = new Roo.data.JsonReader({
12360 totalProperty: "results", // The property which contains the total dataset size (optional)
12361 root: "rows", // The property which contains an Array of row objects
12362 id: "id" // The property within each row object that provides an ID for the record (optional)
12366 * This would consume a JSON file like this:
12368 { 'results': 2, 'rows': [
12369 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12370 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12373 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12374 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12375 * paged from the remote server.
12376 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12377 * @cfg {String} root name of the property which contains the Array of row objects.
12378 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12379 * @cfg {Array} fields Array of field definition objects
12381 * Create a new JsonReader
12382 * @param {Object} meta Metadata configuration options
12383 * @param {Object} recordType Either an Array of field definition objects,
12384 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12386 Roo.data.JsonReader = function(meta, recordType){
12389 // set some defaults:
12390 Roo.applyIf(meta, {
12391 totalProperty: 'total',
12392 successProperty : 'success',
12397 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12399 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12402 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12403 * Used by Store query builder to append _requestMeta to params.
12406 metaFromRemote : false,
12408 * This method is only used by a DataProxy which has retrieved data from a remote server.
12409 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12410 * @return {Object} data A data block which is used by an Roo.data.Store object as
12411 * a cache of Roo.data.Records.
12413 read : function(response){
12414 var json = response.responseText;
12416 var o = /* eval:var:o */ eval("("+json+")");
12418 throw {message: "JsonReader.read: Json object not found"};
12424 this.metaFromRemote = true;
12425 this.meta = o.metaData;
12426 this.recordType = Roo.data.Record.create(o.metaData.fields);
12427 this.onMetaChange(this.meta, this.recordType, o);
12429 return this.readRecords(o);
12432 // private function a store will implement
12433 onMetaChange : function(meta, recordType, o){
12440 simpleAccess: function(obj, subsc) {
12447 getJsonAccessor: function(){
12449 return function(expr) {
12451 return(re.test(expr))
12452 ? new Function("obj", "return obj." + expr)
12457 return Roo.emptyFn;
12462 * Create a data block containing Roo.data.Records from an XML document.
12463 * @param {Object} o An object which contains an Array of row objects in the property specified
12464 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12465 * which contains the total size of the dataset.
12466 * @return {Object} data A data block which is used by an Roo.data.Store object as
12467 * a cache of Roo.data.Records.
12469 readRecords : function(o){
12471 * After any data loads, the raw JSON data is available for further custom processing.
12475 var s = this.meta, Record = this.recordType,
12476 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12478 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12480 if(s.totalProperty) {
12481 this.getTotal = this.getJsonAccessor(s.totalProperty);
12483 if(s.successProperty) {
12484 this.getSuccess = this.getJsonAccessor(s.successProperty);
12486 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12488 var g = this.getJsonAccessor(s.id);
12489 this.getId = function(rec) {
12491 return (r === undefined || r === "") ? null : r;
12494 this.getId = function(){return null;};
12497 for(var jj = 0; jj < fl; jj++){
12499 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12500 this.ef[jj] = this.getJsonAccessor(map);
12504 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12505 if(s.totalProperty){
12506 var vt = parseInt(this.getTotal(o), 10);
12511 if(s.successProperty){
12512 var vs = this.getSuccess(o);
12513 if(vs === false || vs === 'false'){
12518 for(var i = 0; i < c; i++){
12521 var id = this.getId(n);
12522 for(var j = 0; j < fl; j++){
12524 var v = this.ef[j](n);
12526 Roo.log('missing convert for ' + f.name);
12530 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12532 var record = new Record(values, id);
12534 records[i] = record;
12540 totalRecords : totalRecords
12545 * Ext JS Library 1.1.1
12546 * Copyright(c) 2006-2007, Ext JS, LLC.
12548 * Originally Released Under LGPL - original licence link has changed is not relivant.
12551 * <script type="text/javascript">
12555 * @class Roo.data.ArrayReader
12556 * @extends Roo.data.DataReader
12557 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12558 * Each element of that Array represents a row of data fields. The
12559 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12560 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12564 var RecordDef = Roo.data.Record.create([
12565 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12566 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12568 var myReader = new Roo.data.ArrayReader({
12569 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12573 * This would consume an Array like this:
12575 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12577 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12579 * Create a new JsonReader
12580 * @param {Object} meta Metadata configuration options.
12581 * @param {Object} recordType Either an Array of field definition objects
12582 * as specified to {@link Roo.data.Record#create},
12583 * or an {@link Roo.data.Record} object
12584 * created using {@link Roo.data.Record#create}.
12586 Roo.data.ArrayReader = function(meta, recordType){
12587 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12590 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12592 * Create a data block containing Roo.data.Records from an XML document.
12593 * @param {Object} o An Array of row objects which represents the dataset.
12594 * @return {Object} data A data block which is used by an Roo.data.Store object as
12595 * a cache of Roo.data.Records.
12597 readRecords : function(o){
12598 var sid = this.meta ? this.meta.id : null;
12599 var recordType = this.recordType, fields = recordType.prototype.fields;
12602 for(var i = 0; i < root.length; i++){
12605 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12606 for(var j = 0, jlen = fields.length; j < jlen; j++){
12607 var f = fields.items[j];
12608 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12609 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12611 values[f.name] = v;
12613 var record = new recordType(values, id);
12615 records[records.length] = record;
12619 totalRecords : records.length
12628 * @class Roo.bootstrap.ComboBox
12629 * @extends Roo.bootstrap.TriggerField
12630 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12631 * @cfg {Boolean} append (true|false) default false
12632 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12633 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12634 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12635 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12636 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12637 * @cfg {Boolean} animate default true
12638 * @cfg {Boolean} emptyResultText only for touch device
12639 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12640 * @cfg {String} emptyTitle default ''
12642 * Create a new ComboBox.
12643 * @param {Object} config Configuration options
12645 Roo.bootstrap.ComboBox = function(config){
12646 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12650 * Fires when the dropdown list is expanded
12651 * @param {Roo.bootstrap.ComboBox} combo This combo box
12656 * Fires when the dropdown list is collapsed
12657 * @param {Roo.bootstrap.ComboBox} combo This combo box
12661 * @event beforeselect
12662 * Fires before a list item is selected. Return false to cancel the selection.
12663 * @param {Roo.bootstrap.ComboBox} combo This combo box
12664 * @param {Roo.data.Record} record The data record returned from the underlying store
12665 * @param {Number} index The index of the selected item in the dropdown list
12667 'beforeselect' : true,
12670 * Fires when a list item is selected
12671 * @param {Roo.bootstrap.ComboBox} combo This combo box
12672 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12673 * @param {Number} index The index of the selected item in the dropdown list
12677 * @event beforequery
12678 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12679 * The event object passed has these properties:
12680 * @param {Roo.bootstrap.ComboBox} combo This combo box
12681 * @param {String} query The query
12682 * @param {Boolean} forceAll true to force "all" query
12683 * @param {Boolean} cancel true to cancel the query
12684 * @param {Object} e The query event object
12686 'beforequery': true,
12689 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12690 * @param {Roo.bootstrap.ComboBox} combo This combo box
12695 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12696 * @param {Roo.bootstrap.ComboBox} combo This combo box
12697 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12702 * Fires when the remove value from the combobox array
12703 * @param {Roo.bootstrap.ComboBox} combo This combo box
12707 * @event afterremove
12708 * Fires when the remove value from the combobox array
12709 * @param {Roo.bootstrap.ComboBox} combo This combo box
12711 'afterremove' : true,
12713 * @event specialfilter
12714 * Fires when specialfilter
12715 * @param {Roo.bootstrap.ComboBox} combo This combo box
12717 'specialfilter' : true,
12720 * Fires when tick the element
12721 * @param {Roo.bootstrap.ComboBox} combo This combo box
12725 * @event touchviewdisplay
12726 * Fires when touch view require special display (default is using displayField)
12727 * @param {Roo.bootstrap.ComboBox} combo This combo box
12728 * @param {Object} cfg set html .
12730 'touchviewdisplay' : true
12735 this.tickItems = [];
12737 this.selectedIndex = -1;
12738 if(this.mode == 'local'){
12739 if(config.queryDelay === undefined){
12740 this.queryDelay = 10;
12742 if(config.minChars === undefined){
12748 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12751 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12752 * rendering into an Roo.Editor, defaults to false)
12755 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12756 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12759 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12762 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12763 * the dropdown list (defaults to undefined, with no header element)
12767 * @cfg {String/Roo.Template} tpl The template to use to render the output
12771 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12773 listWidth: undefined,
12775 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12776 * mode = 'remote' or 'text' if mode = 'local')
12778 displayField: undefined,
12781 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12782 * mode = 'remote' or 'value' if mode = 'local').
12783 * Note: use of a valueField requires the user make a selection
12784 * in order for a value to be mapped.
12786 valueField: undefined,
12788 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12793 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12794 * field's data value (defaults to the underlying DOM element's name)
12796 hiddenName: undefined,
12798 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12802 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12804 selectedClass: 'active',
12807 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12811 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12812 * anchor positions (defaults to 'tl-bl')
12814 listAlign: 'tl-bl?',
12816 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12820 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12821 * query specified by the allQuery config option (defaults to 'query')
12823 triggerAction: 'query',
12825 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12826 * (defaults to 4, does not apply if editable = false)
12830 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12831 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12835 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12836 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12840 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12841 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12845 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12846 * when editable = true (defaults to false)
12848 selectOnFocus:false,
12850 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12852 queryParam: 'query',
12854 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12855 * when mode = 'remote' (defaults to 'Loading...')
12857 loadingText: 'Loading...',
12859 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12863 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12867 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12868 * traditional select (defaults to true)
12872 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12876 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12880 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12881 * listWidth has a higher value)
12885 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12886 * allow the user to set arbitrary text into the field (defaults to false)
12888 forceSelection:false,
12890 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12891 * if typeAhead = true (defaults to 250)
12893 typeAheadDelay : 250,
12895 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12896 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12898 valueNotFoundText : undefined,
12900 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12902 blockFocus : false,
12905 * @cfg {Boolean} disableClear Disable showing of clear button.
12907 disableClear : false,
12909 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12911 alwaysQuery : false,
12914 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12919 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12921 invalidClass : "has-warning",
12924 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12926 validClass : "has-success",
12929 * @cfg {Boolean} specialFilter (true|false) special filter default false
12931 specialFilter : false,
12934 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12936 mobileTouchView : true,
12939 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12941 useNativeIOS : false,
12943 ios_options : false,
12955 btnPosition : 'right',
12956 triggerList : true,
12957 showToggleBtn : true,
12959 emptyResultText: 'Empty',
12960 triggerText : 'Select',
12963 // element that contains real text value.. (when hidden is used..)
12965 getAutoCreate : function()
12970 * Render classic select for iso
12973 if(Roo.isIOS && this.useNativeIOS){
12974 cfg = this.getAutoCreateNativeIOS();
12982 if(Roo.isTouch && this.mobileTouchView){
12983 cfg = this.getAutoCreateTouchView();
12990 if(!this.tickable){
12991 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12996 * ComboBox with tickable selections
12999 var align = this.labelAlign || this.parentLabelAlign();
13002 cls : 'form-group roo-combobox-tickable' //input-group
13005 var btn_text_select = '';
13006 var btn_text_done = '';
13007 var btn_text_cancel = '';
13009 if (this.btn_text_show) {
13010 btn_text_select = 'Select';
13011 btn_text_done = 'Done';
13012 btn_text_cancel = 'Cancel';
13017 cls : 'tickable-buttons',
13022 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13023 //html : this.triggerText
13024 html: btn_text_select
13030 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13032 html: btn_text_done
13038 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13040 html: btn_text_cancel
13046 buttons.cn.unshift({
13048 cls: 'roo-select2-search-field-input'
13054 Roo.each(buttons.cn, function(c){
13056 c.cls += ' btn-' + _this.size;
13059 if (_this.disabled) {
13070 cls: 'form-hidden-field'
13074 cls: 'roo-select2-choices',
13078 cls: 'roo-select2-search-field',
13089 cls: 'roo-select2-container input-group roo-select2-container-multi',
13094 // cls: 'typeahead typeahead-long dropdown-menu',
13095 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13100 if(this.hasFeedback && !this.allowBlank){
13104 cls: 'glyphicon form-control-feedback'
13107 combobox.cn.push(feedback);
13111 if (align ==='left' && this.fieldLabel.length) {
13113 cfg.cls += ' roo-form-group-label-left';
13118 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13119 tooltip : 'This field is required'
13124 cls : 'control-label',
13125 html : this.fieldLabel
13137 var labelCfg = cfg.cn[1];
13138 var contentCfg = cfg.cn[2];
13141 if(this.indicatorpos == 'right'){
13147 cls : 'control-label',
13151 html : this.fieldLabel
13155 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13156 tooltip : 'This field is required'
13171 labelCfg = cfg.cn[0];
13172 contentCfg = cfg.cn[1];
13176 if(this.labelWidth > 12){
13177 labelCfg.style = "width: " + this.labelWidth + 'px';
13180 if(this.labelWidth < 13 && this.labelmd == 0){
13181 this.labelmd = this.labelWidth;
13184 if(this.labellg > 0){
13185 labelCfg.cls += ' col-lg-' + this.labellg;
13186 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13189 if(this.labelmd > 0){
13190 labelCfg.cls += ' col-md-' + this.labelmd;
13191 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13194 if(this.labelsm > 0){
13195 labelCfg.cls += ' col-sm-' + this.labelsm;
13196 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13199 if(this.labelxs > 0){
13200 labelCfg.cls += ' col-xs-' + this.labelxs;
13201 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13205 } else if ( this.fieldLabel.length) {
13206 // Roo.log(" label");
13210 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13211 tooltip : 'This field is required'
13215 //cls : 'input-group-addon',
13216 html : this.fieldLabel
13221 if(this.indicatorpos == 'right'){
13225 //cls : 'input-group-addon',
13226 html : this.fieldLabel
13230 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13231 tooltip : 'This field is required'
13240 // Roo.log(" no label && no align");
13247 ['xs','sm','md','lg'].map(function(size){
13248 if (settings[size]) {
13249 cfg.cls += ' col-' + size + '-' + settings[size];
13257 _initEventsCalled : false,
13260 initEvents: function()
13262 if (this._initEventsCalled) { // as we call render... prevent looping...
13265 this._initEventsCalled = true;
13268 throw "can not find store for combo";
13271 this.indicator = this.indicatorEl();
13273 this.store = Roo.factory(this.store, Roo.data);
13274 this.store.parent = this;
13276 // if we are building from html. then this element is so complex, that we can not really
13277 // use the rendered HTML.
13278 // so we have to trash and replace the previous code.
13279 if (Roo.XComponent.build_from_html) {
13280 // remove this element....
13281 var e = this.el.dom, k=0;
13282 while (e ) { e = e.previousSibling; ++k;}
13287 this.rendered = false;
13289 this.render(this.parent().getChildContainer(true), k);
13292 if(Roo.isIOS && this.useNativeIOS){
13293 this.initIOSView();
13301 if(Roo.isTouch && this.mobileTouchView){
13302 this.initTouchView();
13307 this.initTickableEvents();
13311 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13313 if(this.hiddenName){
13315 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13317 this.hiddenField.dom.value =
13318 this.hiddenValue !== undefined ? this.hiddenValue :
13319 this.value !== undefined ? this.value : '';
13321 // prevent input submission
13322 this.el.dom.removeAttribute('name');
13323 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13328 // this.el.dom.setAttribute('autocomplete', 'off');
13331 var cls = 'x-combo-list';
13333 //this.list = new Roo.Layer({
13334 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13340 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13341 _this.list.setWidth(lw);
13344 this.list.on('mouseover', this.onViewOver, this);
13345 this.list.on('mousemove', this.onViewMove, this);
13346 this.list.on('scroll', this.onViewScroll, this);
13349 this.list.swallowEvent('mousewheel');
13350 this.assetHeight = 0;
13353 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13354 this.assetHeight += this.header.getHeight();
13357 this.innerList = this.list.createChild({cls:cls+'-inner'});
13358 this.innerList.on('mouseover', this.onViewOver, this);
13359 this.innerList.on('mousemove', this.onViewMove, this);
13360 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13362 if(this.allowBlank && !this.pageSize && !this.disableClear){
13363 this.footer = this.list.createChild({cls:cls+'-ft'});
13364 this.pageTb = new Roo.Toolbar(this.footer);
13368 this.footer = this.list.createChild({cls:cls+'-ft'});
13369 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13370 {pageSize: this.pageSize});
13374 if (this.pageTb && this.allowBlank && !this.disableClear) {
13376 this.pageTb.add(new Roo.Toolbar.Fill(), {
13377 cls: 'x-btn-icon x-btn-clear',
13379 handler: function()
13382 _this.clearValue();
13383 _this.onSelect(false, -1);
13388 this.assetHeight += this.footer.getHeight();
13393 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13396 this.view = new Roo.View(this.list, this.tpl, {
13397 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13399 //this.view.wrapEl.setDisplayed(false);
13400 this.view.on('click', this.onViewClick, this);
13403 this.store.on('beforeload', this.onBeforeLoad, this);
13404 this.store.on('load', this.onLoad, this);
13405 this.store.on('loadexception', this.onLoadException, this);
13407 if(this.resizable){
13408 this.resizer = new Roo.Resizable(this.list, {
13409 pinned:true, handles:'se'
13411 this.resizer.on('resize', function(r, w, h){
13412 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13413 this.listWidth = w;
13414 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13415 this.restrictHeight();
13417 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13420 if(!this.editable){
13421 this.editable = true;
13422 this.setEditable(false);
13427 if (typeof(this.events.add.listeners) != 'undefined') {
13429 this.addicon = this.wrap.createChild(
13430 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13432 this.addicon.on('click', function(e) {
13433 this.fireEvent('add', this);
13436 if (typeof(this.events.edit.listeners) != 'undefined') {
13438 this.editicon = this.wrap.createChild(
13439 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13440 if (this.addicon) {
13441 this.editicon.setStyle('margin-left', '40px');
13443 this.editicon.on('click', function(e) {
13445 // we fire even if inothing is selected..
13446 this.fireEvent('edit', this, this.lastData );
13452 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13453 "up" : function(e){
13454 this.inKeyMode = true;
13458 "down" : function(e){
13459 if(!this.isExpanded()){
13460 this.onTriggerClick();
13462 this.inKeyMode = true;
13467 "enter" : function(e){
13468 // this.onViewClick();
13472 if(this.fireEvent("specialkey", this, e)){
13473 this.onViewClick(false);
13479 "esc" : function(e){
13483 "tab" : function(e){
13486 if(this.fireEvent("specialkey", this, e)){
13487 this.onViewClick(false);
13495 doRelay : function(foo, bar, hname){
13496 if(hname == 'down' || this.scope.isExpanded()){
13497 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13506 this.queryDelay = Math.max(this.queryDelay || 10,
13507 this.mode == 'local' ? 10 : 250);
13510 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13512 if(this.typeAhead){
13513 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13515 if(this.editable !== false){
13516 this.inputEl().on("keyup", this.onKeyUp, this);
13518 if(this.forceSelection){
13519 this.inputEl().on('blur', this.doForce, this);
13523 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13524 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13528 initTickableEvents: function()
13532 if(this.hiddenName){
13534 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13536 this.hiddenField.dom.value =
13537 this.hiddenValue !== undefined ? this.hiddenValue :
13538 this.value !== undefined ? this.value : '';
13540 // prevent input submission
13541 this.el.dom.removeAttribute('name');
13542 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13547 // this.list = this.el.select('ul.dropdown-menu',true).first();
13549 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13550 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13551 if(this.triggerList){
13552 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13555 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13556 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13558 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13559 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13561 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13562 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13564 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13565 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13566 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13569 this.cancelBtn.hide();
13574 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13575 _this.list.setWidth(lw);
13578 this.list.on('mouseover', this.onViewOver, this);
13579 this.list.on('mousemove', this.onViewMove, this);
13581 this.list.on('scroll', this.onViewScroll, this);
13584 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13587 this.view = new Roo.View(this.list, this.tpl, {
13588 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13591 //this.view.wrapEl.setDisplayed(false);
13592 this.view.on('click', this.onViewClick, this);
13596 this.store.on('beforeload', this.onBeforeLoad, this);
13597 this.store.on('load', this.onLoad, this);
13598 this.store.on('loadexception', this.onLoadException, this);
13601 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13602 "up" : function(e){
13603 this.inKeyMode = true;
13607 "down" : function(e){
13608 this.inKeyMode = true;
13612 "enter" : function(e){
13613 if(this.fireEvent("specialkey", this, e)){
13614 this.onViewClick(false);
13620 "esc" : function(e){
13621 this.onTickableFooterButtonClick(e, false, false);
13624 "tab" : function(e){
13625 this.fireEvent("specialkey", this, e);
13627 this.onTickableFooterButtonClick(e, false, false);
13634 doRelay : function(e, fn, key){
13635 if(this.scope.isExpanded()){
13636 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13645 this.queryDelay = Math.max(this.queryDelay || 10,
13646 this.mode == 'local' ? 10 : 250);
13649 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13651 if(this.typeAhead){
13652 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13655 if(this.editable !== false){
13656 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13659 this.indicator = this.indicatorEl();
13661 if(this.indicator){
13662 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13663 this.indicator.hide();
13668 onDestroy : function(){
13670 this.view.setStore(null);
13671 this.view.el.removeAllListeners();
13672 this.view.el.remove();
13673 this.view.purgeListeners();
13676 this.list.dom.innerHTML = '';
13680 this.store.un('beforeload', this.onBeforeLoad, this);
13681 this.store.un('load', this.onLoad, this);
13682 this.store.un('loadexception', this.onLoadException, this);
13684 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13688 fireKey : function(e){
13689 if(e.isNavKeyPress() && !this.list.isVisible()){
13690 this.fireEvent("specialkey", this, e);
13695 onResize: function(w, h){
13696 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13698 // if(typeof w != 'number'){
13699 // // we do not handle it!?!?
13702 // var tw = this.trigger.getWidth();
13703 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13704 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13706 // this.inputEl().setWidth( this.adjustWidth('input', x));
13708 // //this.trigger.setStyle('left', x+'px');
13710 // if(this.list && this.listWidth === undefined){
13711 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13712 // this.list.setWidth(lw);
13713 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13721 * Allow or prevent the user from directly editing the field text. If false is passed,
13722 * the user will only be able to select from the items defined in the dropdown list. This method
13723 * is the runtime equivalent of setting the 'editable' config option at config time.
13724 * @param {Boolean} value True to allow the user to directly edit the field text
13726 setEditable : function(value){
13727 if(value == this.editable){
13730 this.editable = value;
13732 this.inputEl().dom.setAttribute('readOnly', true);
13733 this.inputEl().on('mousedown', this.onTriggerClick, this);
13734 this.inputEl().addClass('x-combo-noedit');
13736 this.inputEl().dom.setAttribute('readOnly', false);
13737 this.inputEl().un('mousedown', this.onTriggerClick, this);
13738 this.inputEl().removeClass('x-combo-noedit');
13744 onBeforeLoad : function(combo,opts){
13745 if(!this.hasFocus){
13749 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13751 this.restrictHeight();
13752 this.selectedIndex = -1;
13756 onLoad : function(){
13758 this.hasQuery = false;
13760 if(!this.hasFocus){
13764 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13765 this.loading.hide();
13768 if(this.store.getCount() > 0){
13771 this.restrictHeight();
13772 if(this.lastQuery == this.allQuery){
13773 if(this.editable && !this.tickable){
13774 this.inputEl().dom.select();
13778 !this.selectByValue(this.value, true) &&
13781 !this.store.lastOptions ||
13782 typeof(this.store.lastOptions.add) == 'undefined' ||
13783 this.store.lastOptions.add != true
13786 this.select(0, true);
13789 if(this.autoFocus){
13792 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13793 this.taTask.delay(this.typeAheadDelay);
13797 this.onEmptyResults();
13803 onLoadException : function()
13805 this.hasQuery = false;
13807 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13808 this.loading.hide();
13811 if(this.tickable && this.editable){
13816 // only causes errors at present
13817 //Roo.log(this.store.reader.jsonData);
13818 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13820 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13826 onTypeAhead : function(){
13827 if(this.store.getCount() > 0){
13828 var r = this.store.getAt(0);
13829 var newValue = r.data[this.displayField];
13830 var len = newValue.length;
13831 var selStart = this.getRawValue().length;
13833 if(selStart != len){
13834 this.setRawValue(newValue);
13835 this.selectText(selStart, newValue.length);
13841 onSelect : function(record, index){
13843 if(this.fireEvent('beforeselect', this, record, index) !== false){
13845 this.setFromData(index > -1 ? record.data : false);
13848 this.fireEvent('select', this, record, index);
13853 * Returns the currently selected field value or empty string if no value is set.
13854 * @return {String} value The selected value
13856 getValue : function()
13858 if(Roo.isIOS && this.useNativeIOS){
13859 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13863 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13866 if(this.valueField){
13867 return typeof this.value != 'undefined' ? this.value : '';
13869 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13873 getRawValue : function()
13875 if(Roo.isIOS && this.useNativeIOS){
13876 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13879 var v = this.inputEl().getValue();
13885 * Clears any text/value currently set in the field
13887 clearValue : function(){
13889 if(this.hiddenField){
13890 this.hiddenField.dom.value = '';
13893 this.setRawValue('');
13894 this.lastSelectionText = '';
13895 this.lastData = false;
13897 var close = this.closeTriggerEl();
13908 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13909 * will be displayed in the field. If the value does not match the data value of an existing item,
13910 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13911 * Otherwise the field will be blank (although the value will still be set).
13912 * @param {String} value The value to match
13914 setValue : function(v)
13916 if(Roo.isIOS && this.useNativeIOS){
13917 this.setIOSValue(v);
13927 if(this.valueField){
13928 var r = this.findRecord(this.valueField, v);
13930 text = r.data[this.displayField];
13931 }else if(this.valueNotFoundText !== undefined){
13932 text = this.valueNotFoundText;
13935 this.lastSelectionText = text;
13936 if(this.hiddenField){
13937 this.hiddenField.dom.value = v;
13939 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13942 var close = this.closeTriggerEl();
13945 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13951 * @property {Object} the last set data for the element
13956 * Sets the value of the field based on a object which is related to the record format for the store.
13957 * @param {Object} value the value to set as. or false on reset?
13959 setFromData : function(o){
13966 var dv = ''; // display value
13967 var vv = ''; // value value..
13969 if (this.displayField) {
13970 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13972 // this is an error condition!!!
13973 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13976 if(this.valueField){
13977 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13980 var close = this.closeTriggerEl();
13983 if(dv.length || vv * 1 > 0){
13985 this.blockFocus=true;
13991 if(this.hiddenField){
13992 this.hiddenField.dom.value = vv;
13994 this.lastSelectionText = dv;
13995 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13999 // no hidden field.. - we store the value in 'value', but still display
14000 // display field!!!!
14001 this.lastSelectionText = dv;
14002 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14009 reset : function(){
14010 // overridden so that last data is reset..
14017 this.setValue(this.originalValue);
14018 //this.clearInvalid();
14019 this.lastData = false;
14021 this.view.clearSelections();
14027 findRecord : function(prop, value){
14029 if(this.store.getCount() > 0){
14030 this.store.each(function(r){
14031 if(r.data[prop] == value){
14041 getName: function()
14043 // returns hidden if it's set..
14044 if (!this.rendered) {return ''};
14045 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14049 onViewMove : function(e, t){
14050 this.inKeyMode = false;
14054 onViewOver : function(e, t){
14055 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14058 var item = this.view.findItemFromChild(t);
14061 var index = this.view.indexOf(item);
14062 this.select(index, false);
14067 onViewClick : function(view, doFocus, el, e)
14069 var index = this.view.getSelectedIndexes()[0];
14071 var r = this.store.getAt(index);
14075 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14082 Roo.each(this.tickItems, function(v,k){
14084 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14086 _this.tickItems.splice(k, 1);
14088 if(typeof(e) == 'undefined' && view == false){
14089 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14101 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14102 this.tickItems.push(r.data);
14105 if(typeof(e) == 'undefined' && view == false){
14106 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14113 this.onSelect(r, index);
14115 if(doFocus !== false && !this.blockFocus){
14116 this.inputEl().focus();
14121 restrictHeight : function(){
14122 //this.innerList.dom.style.height = '';
14123 //var inner = this.innerList.dom;
14124 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14125 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14126 //this.list.beginUpdate();
14127 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14128 this.list.alignTo(this.inputEl(), this.listAlign);
14129 this.list.alignTo(this.inputEl(), this.listAlign);
14130 //this.list.endUpdate();
14134 onEmptyResults : function(){
14136 if(this.tickable && this.editable){
14137 this.hasFocus = false;
14138 this.restrictHeight();
14146 * Returns true if the dropdown list is expanded, else false.
14148 isExpanded : function(){
14149 return this.list.isVisible();
14153 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14154 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14155 * @param {String} value The data value of the item to select
14156 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14157 * selected item if it is not currently in view (defaults to true)
14158 * @return {Boolean} True if the value matched an item in the list, else false
14160 selectByValue : function(v, scrollIntoView){
14161 if(v !== undefined && v !== null){
14162 var r = this.findRecord(this.valueField || this.displayField, v);
14164 this.select(this.store.indexOf(r), scrollIntoView);
14172 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14173 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14174 * @param {Number} index The zero-based index of the list item to select
14175 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14176 * selected item if it is not currently in view (defaults to true)
14178 select : function(index, scrollIntoView){
14179 this.selectedIndex = index;
14180 this.view.select(index);
14181 if(scrollIntoView !== false){
14182 var el = this.view.getNode(index);
14184 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14187 this.list.scrollChildIntoView(el, false);
14193 selectNext : function(){
14194 var ct = this.store.getCount();
14196 if(this.selectedIndex == -1){
14198 }else if(this.selectedIndex < ct-1){
14199 this.select(this.selectedIndex+1);
14205 selectPrev : function(){
14206 var ct = this.store.getCount();
14208 if(this.selectedIndex == -1){
14210 }else if(this.selectedIndex != 0){
14211 this.select(this.selectedIndex-1);
14217 onKeyUp : function(e){
14218 if(this.editable !== false && !e.isSpecialKey()){
14219 this.lastKey = e.getKey();
14220 this.dqTask.delay(this.queryDelay);
14225 validateBlur : function(){
14226 return !this.list || !this.list.isVisible();
14230 initQuery : function(){
14232 var v = this.getRawValue();
14234 if(this.tickable && this.editable){
14235 v = this.tickableInputEl().getValue();
14242 doForce : function(){
14243 if(this.inputEl().dom.value.length > 0){
14244 this.inputEl().dom.value =
14245 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14251 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14252 * query allowing the query action to be canceled if needed.
14253 * @param {String} query The SQL query to execute
14254 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14255 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14256 * saved in the current store (defaults to false)
14258 doQuery : function(q, forceAll){
14260 if(q === undefined || q === null){
14265 forceAll: forceAll,
14269 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14274 forceAll = qe.forceAll;
14275 if(forceAll === true || (q.length >= this.minChars)){
14277 this.hasQuery = true;
14279 if(this.lastQuery != q || this.alwaysQuery){
14280 this.lastQuery = q;
14281 if(this.mode == 'local'){
14282 this.selectedIndex = -1;
14284 this.store.clearFilter();
14287 if(this.specialFilter){
14288 this.fireEvent('specialfilter', this);
14293 this.store.filter(this.displayField, q);
14296 this.store.fireEvent("datachanged", this.store);
14303 this.store.baseParams[this.queryParam] = q;
14305 var options = {params : this.getParams(q)};
14308 options.add = true;
14309 options.params.start = this.page * this.pageSize;
14312 this.store.load(options);
14315 * this code will make the page width larger, at the beginning, the list not align correctly,
14316 * we should expand the list on onLoad
14317 * so command out it
14322 this.selectedIndex = -1;
14327 this.loadNext = false;
14331 getParams : function(q){
14333 //p[this.queryParam] = q;
14337 p.limit = this.pageSize;
14343 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14345 collapse : function(){
14346 if(!this.isExpanded()){
14352 this.hasFocus = false;
14356 this.cancelBtn.hide();
14357 this.trigger.show();
14360 this.tickableInputEl().dom.value = '';
14361 this.tickableInputEl().blur();
14366 Roo.get(document).un('mousedown', this.collapseIf, this);
14367 Roo.get(document).un('mousewheel', this.collapseIf, this);
14368 if (!this.editable) {
14369 Roo.get(document).un('keydown', this.listKeyPress, this);
14371 this.fireEvent('collapse', this);
14377 collapseIf : function(e){
14378 var in_combo = e.within(this.el);
14379 var in_list = e.within(this.list);
14380 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14382 if (in_combo || in_list || is_list) {
14383 //e.stopPropagation();
14388 this.onTickableFooterButtonClick(e, false, false);
14396 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14398 expand : function(){
14400 if(this.isExpanded() || !this.hasFocus){
14404 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14405 this.list.setWidth(lw);
14411 this.restrictHeight();
14415 this.tickItems = Roo.apply([], this.item);
14418 this.cancelBtn.show();
14419 this.trigger.hide();
14422 this.tickableInputEl().focus();
14427 Roo.get(document).on('mousedown', this.collapseIf, this);
14428 Roo.get(document).on('mousewheel', this.collapseIf, this);
14429 if (!this.editable) {
14430 Roo.get(document).on('keydown', this.listKeyPress, this);
14433 this.fireEvent('expand', this);
14437 // Implements the default empty TriggerField.onTriggerClick function
14438 onTriggerClick : function(e)
14440 Roo.log('trigger click');
14442 if(this.disabled || !this.triggerList){
14447 this.loadNext = false;
14449 if(this.isExpanded()){
14451 if (!this.blockFocus) {
14452 this.inputEl().focus();
14456 this.hasFocus = true;
14457 if(this.triggerAction == 'all') {
14458 this.doQuery(this.allQuery, true);
14460 this.doQuery(this.getRawValue());
14462 if (!this.blockFocus) {
14463 this.inputEl().focus();
14468 onTickableTriggerClick : function(e)
14475 this.loadNext = false;
14476 this.hasFocus = true;
14478 if(this.triggerAction == 'all') {
14479 this.doQuery(this.allQuery, true);
14481 this.doQuery(this.getRawValue());
14485 onSearchFieldClick : function(e)
14487 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14488 this.onTickableFooterButtonClick(e, false, false);
14492 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14497 this.loadNext = false;
14498 this.hasFocus = true;
14500 if(this.triggerAction == 'all') {
14501 this.doQuery(this.allQuery, true);
14503 this.doQuery(this.getRawValue());
14507 listKeyPress : function(e)
14509 //Roo.log('listkeypress');
14510 // scroll to first matching element based on key pres..
14511 if (e.isSpecialKey()) {
14514 var k = String.fromCharCode(e.getKey()).toUpperCase();
14517 var csel = this.view.getSelectedNodes();
14518 var cselitem = false;
14520 var ix = this.view.indexOf(csel[0]);
14521 cselitem = this.store.getAt(ix);
14522 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14528 this.store.each(function(v) {
14530 // start at existing selection.
14531 if (cselitem.id == v.id) {
14537 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14538 match = this.store.indexOf(v);
14544 if (match === false) {
14545 return true; // no more action?
14548 this.view.select(match);
14549 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14550 sn.scrollIntoView(sn.dom.parentNode, false);
14553 onViewScroll : function(e, t){
14555 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){
14559 this.hasQuery = true;
14561 this.loading = this.list.select('.loading', true).first();
14563 if(this.loading === null){
14564 this.list.createChild({
14566 cls: 'loading roo-select2-more-results roo-select2-active',
14567 html: 'Loading more results...'
14570 this.loading = this.list.select('.loading', true).first();
14572 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14574 this.loading.hide();
14577 this.loading.show();
14582 this.loadNext = true;
14584 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14589 addItem : function(o)
14591 var dv = ''; // display value
14593 if (this.displayField) {
14594 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14596 // this is an error condition!!!
14597 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14604 var choice = this.choices.createChild({
14606 cls: 'roo-select2-search-choice',
14615 cls: 'roo-select2-search-choice-close fa fa-times',
14620 }, this.searchField);
14622 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14624 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14632 this.inputEl().dom.value = '';
14637 onRemoveItem : function(e, _self, o)
14639 e.preventDefault();
14641 this.lastItem = Roo.apply([], this.item);
14643 var index = this.item.indexOf(o.data) * 1;
14646 Roo.log('not this item?!');
14650 this.item.splice(index, 1);
14655 this.fireEvent('remove', this, e);
14661 syncValue : function()
14663 if(!this.item.length){
14670 Roo.each(this.item, function(i){
14671 if(_this.valueField){
14672 value.push(i[_this.valueField]);
14679 this.value = value.join(',');
14681 if(this.hiddenField){
14682 this.hiddenField.dom.value = this.value;
14685 this.store.fireEvent("datachanged", this.store);
14690 clearItem : function()
14692 if(!this.multiple){
14698 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14706 if(this.tickable && !Roo.isTouch){
14707 this.view.refresh();
14711 inputEl: function ()
14713 if(Roo.isIOS && this.useNativeIOS){
14714 return this.el.select('select.roo-ios-select', true).first();
14717 if(Roo.isTouch && this.mobileTouchView){
14718 return this.el.select('input.form-control',true).first();
14722 return this.searchField;
14725 return this.el.select('input.form-control',true).first();
14728 onTickableFooterButtonClick : function(e, btn, el)
14730 e.preventDefault();
14732 this.lastItem = Roo.apply([], this.item);
14734 if(btn && btn.name == 'cancel'){
14735 this.tickItems = Roo.apply([], this.item);
14744 Roo.each(this.tickItems, function(o){
14752 validate : function()
14754 if(this.getVisibilityEl().hasClass('hidden')){
14758 var v = this.getRawValue();
14761 v = this.getValue();
14764 if(this.disabled || this.allowBlank || v.length){
14769 this.markInvalid();
14773 tickableInputEl : function()
14775 if(!this.tickable || !this.editable){
14776 return this.inputEl();
14779 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14783 getAutoCreateTouchView : function()
14788 cls: 'form-group' //input-group
14794 type : this.inputType,
14795 cls : 'form-control x-combo-noedit',
14796 autocomplete: 'new-password',
14797 placeholder : this.placeholder || '',
14802 input.name = this.name;
14806 input.cls += ' input-' + this.size;
14809 if (this.disabled) {
14810 input.disabled = true;
14821 inputblock.cls += ' input-group';
14823 inputblock.cn.unshift({
14825 cls : 'input-group-addon',
14830 if(this.removable && !this.multiple){
14831 inputblock.cls += ' roo-removable';
14833 inputblock.cn.push({
14836 cls : 'roo-combo-removable-btn close'
14840 if(this.hasFeedback && !this.allowBlank){
14842 inputblock.cls += ' has-feedback';
14844 inputblock.cn.push({
14846 cls: 'glyphicon form-control-feedback'
14853 inputblock.cls += (this.before) ? '' : ' input-group';
14855 inputblock.cn.push({
14857 cls : 'input-group-addon',
14868 cls: 'form-hidden-field'
14882 cls: 'form-hidden-field'
14886 cls: 'roo-select2-choices',
14890 cls: 'roo-select2-search-field',
14903 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14909 if(!this.multiple && this.showToggleBtn){
14916 if (this.caret != false) {
14919 cls: 'fa fa-' + this.caret
14926 cls : 'input-group-addon btn dropdown-toggle',
14931 cls: 'combobox-clear',
14945 combobox.cls += ' roo-select2-container-multi';
14948 var align = this.labelAlign || this.parentLabelAlign();
14950 if (align ==='left' && this.fieldLabel.length) {
14955 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14956 tooltip : 'This field is required'
14960 cls : 'control-label',
14961 html : this.fieldLabel
14972 var labelCfg = cfg.cn[1];
14973 var contentCfg = cfg.cn[2];
14976 if(this.indicatorpos == 'right'){
14981 cls : 'control-label',
14985 html : this.fieldLabel
14989 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14990 tooltip : 'This field is required'
15003 labelCfg = cfg.cn[0];
15004 contentCfg = cfg.cn[1];
15009 if(this.labelWidth > 12){
15010 labelCfg.style = "width: " + this.labelWidth + 'px';
15013 if(this.labelWidth < 13 && this.labelmd == 0){
15014 this.labelmd = this.labelWidth;
15017 if(this.labellg > 0){
15018 labelCfg.cls += ' col-lg-' + this.labellg;
15019 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15022 if(this.labelmd > 0){
15023 labelCfg.cls += ' col-md-' + this.labelmd;
15024 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15027 if(this.labelsm > 0){
15028 labelCfg.cls += ' col-sm-' + this.labelsm;
15029 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15032 if(this.labelxs > 0){
15033 labelCfg.cls += ' col-xs-' + this.labelxs;
15034 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15038 } else if ( this.fieldLabel.length) {
15042 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15043 tooltip : 'This field is required'
15047 cls : 'control-label',
15048 html : this.fieldLabel
15059 if(this.indicatorpos == 'right'){
15063 cls : 'control-label',
15064 html : this.fieldLabel,
15068 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15069 tooltip : 'This field is required'
15086 var settings = this;
15088 ['xs','sm','md','lg'].map(function(size){
15089 if (settings[size]) {
15090 cfg.cls += ' col-' + size + '-' + settings[size];
15097 initTouchView : function()
15099 this.renderTouchView();
15101 this.touchViewEl.on('scroll', function(){
15102 this.el.dom.scrollTop = 0;
15105 this.originalValue = this.getValue();
15107 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15109 this.inputEl().on("click", this.showTouchView, this);
15110 if (this.triggerEl) {
15111 this.triggerEl.on("click", this.showTouchView, this);
15115 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15116 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15118 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15120 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15121 this.store.on('load', this.onTouchViewLoad, this);
15122 this.store.on('loadexception', this.onTouchViewLoadException, this);
15124 if(this.hiddenName){
15126 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15128 this.hiddenField.dom.value =
15129 this.hiddenValue !== undefined ? this.hiddenValue :
15130 this.value !== undefined ? this.value : '';
15132 this.el.dom.removeAttribute('name');
15133 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15137 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15138 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15141 if(this.removable && !this.multiple){
15142 var close = this.closeTriggerEl();
15144 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15145 close.on('click', this.removeBtnClick, this, close);
15149 * fix the bug in Safari iOS8
15151 this.inputEl().on("focus", function(e){
15152 document.activeElement.blur();
15160 renderTouchView : function()
15162 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15163 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15165 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15166 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15168 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15169 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15170 this.touchViewBodyEl.setStyle('overflow', 'auto');
15172 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15173 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15175 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15176 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15180 showTouchView : function()
15186 this.touchViewHeaderEl.hide();
15188 if(this.modalTitle.length){
15189 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15190 this.touchViewHeaderEl.show();
15193 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15194 this.touchViewEl.show();
15196 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15198 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15199 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15201 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15203 if(this.modalTitle.length){
15204 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15207 this.touchViewBodyEl.setHeight(bodyHeight);
15211 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15213 this.touchViewEl.addClass('in');
15216 this.doTouchViewQuery();
15220 hideTouchView : function()
15222 this.touchViewEl.removeClass('in');
15226 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15228 this.touchViewEl.setStyle('display', 'none');
15233 setTouchViewValue : function()
15240 Roo.each(this.tickItems, function(o){
15245 this.hideTouchView();
15248 doTouchViewQuery : function()
15257 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15261 if(!this.alwaysQuery || this.mode == 'local'){
15262 this.onTouchViewLoad();
15269 onTouchViewBeforeLoad : function(combo,opts)
15275 onTouchViewLoad : function()
15277 if(this.store.getCount() < 1){
15278 this.onTouchViewEmptyResults();
15282 this.clearTouchView();
15284 var rawValue = this.getRawValue();
15286 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15288 this.tickItems = [];
15290 this.store.data.each(function(d, rowIndex){
15291 var row = this.touchViewListGroup.createChild(template);
15293 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15294 row.addClass(d.data.cls);
15297 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15300 html : d.data[this.displayField]
15303 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15304 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15307 row.removeClass('selected');
15308 if(!this.multiple && this.valueField &&
15309 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15312 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15313 row.addClass('selected');
15316 if(this.multiple && this.valueField &&
15317 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15321 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15322 this.tickItems.push(d.data);
15325 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15329 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15331 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15333 if(this.modalTitle.length){
15334 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15337 var listHeight = this.touchViewListGroup.getHeight();
15341 if(firstChecked && listHeight > bodyHeight){
15342 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15347 onTouchViewLoadException : function()
15349 this.hideTouchView();
15352 onTouchViewEmptyResults : function()
15354 this.clearTouchView();
15356 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15358 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15362 clearTouchView : function()
15364 this.touchViewListGroup.dom.innerHTML = '';
15367 onTouchViewClick : function(e, el, o)
15369 e.preventDefault();
15372 var rowIndex = o.rowIndex;
15374 var r = this.store.getAt(rowIndex);
15376 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15378 if(!this.multiple){
15379 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15380 c.dom.removeAttribute('checked');
15383 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15385 this.setFromData(r.data);
15387 var close = this.closeTriggerEl();
15393 this.hideTouchView();
15395 this.fireEvent('select', this, r, rowIndex);
15400 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15401 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15402 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15406 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15407 this.addItem(r.data);
15408 this.tickItems.push(r.data);
15412 getAutoCreateNativeIOS : function()
15415 cls: 'form-group' //input-group,
15420 cls : 'roo-ios-select'
15424 combobox.name = this.name;
15427 if (this.disabled) {
15428 combobox.disabled = true;
15431 var settings = this;
15433 ['xs','sm','md','lg'].map(function(size){
15434 if (settings[size]) {
15435 cfg.cls += ' col-' + size + '-' + settings[size];
15445 initIOSView : function()
15447 this.store.on('load', this.onIOSViewLoad, this);
15452 onIOSViewLoad : function()
15454 if(this.store.getCount() < 1){
15458 this.clearIOSView();
15460 if(this.allowBlank) {
15462 var default_text = '-- SELECT --';
15464 if(this.placeholder.length){
15465 default_text = this.placeholder;
15468 if(this.emptyTitle.length){
15469 default_text += ' - ' + this.emptyTitle + ' -';
15472 var opt = this.inputEl().createChild({
15475 html : default_text
15479 o[this.valueField] = 0;
15480 o[this.displayField] = default_text;
15482 this.ios_options.push({
15489 this.store.data.each(function(d, rowIndex){
15493 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15494 html = d.data[this.displayField];
15499 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15500 value = d.data[this.valueField];
15509 if(this.value == d.data[this.valueField]){
15510 option['selected'] = true;
15513 var opt = this.inputEl().createChild(option);
15515 this.ios_options.push({
15522 this.inputEl().on('change', function(){
15523 this.fireEvent('select', this);
15528 clearIOSView: function()
15530 this.inputEl().dom.innerHTML = '';
15532 this.ios_options = [];
15535 setIOSValue: function(v)
15539 if(!this.ios_options){
15543 Roo.each(this.ios_options, function(opts){
15545 opts.el.dom.removeAttribute('selected');
15547 if(opts.data[this.valueField] != v){
15551 opts.el.dom.setAttribute('selected', true);
15557 * @cfg {Boolean} grow
15561 * @cfg {Number} growMin
15565 * @cfg {Number} growMax
15574 Roo.apply(Roo.bootstrap.ComboBox, {
15578 cls: 'modal-header',
15600 cls: 'list-group-item',
15604 cls: 'roo-combobox-list-group-item-value'
15608 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15622 listItemCheckbox : {
15624 cls: 'list-group-item',
15628 cls: 'roo-combobox-list-group-item-value'
15632 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15648 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15653 cls: 'modal-footer',
15661 cls: 'col-xs-6 text-left',
15664 cls: 'btn btn-danger roo-touch-view-cancel',
15670 cls: 'col-xs-6 text-right',
15673 cls: 'btn btn-success roo-touch-view-ok',
15684 Roo.apply(Roo.bootstrap.ComboBox, {
15686 touchViewTemplate : {
15688 cls: 'modal fade roo-combobox-touch-view',
15692 cls: 'modal-dialog',
15693 style : 'position:fixed', // we have to fix position....
15697 cls: 'modal-content',
15699 Roo.bootstrap.ComboBox.header,
15700 Roo.bootstrap.ComboBox.body,
15701 Roo.bootstrap.ComboBox.footer
15710 * Ext JS Library 1.1.1
15711 * Copyright(c) 2006-2007, Ext JS, LLC.
15713 * Originally Released Under LGPL - original licence link has changed is not relivant.
15716 * <script type="text/javascript">
15721 * @extends Roo.util.Observable
15722 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15723 * This class also supports single and multi selection modes. <br>
15724 * Create a data model bound view:
15726 var store = new Roo.data.Store(...);
15728 var view = new Roo.View({
15730 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15732 singleSelect: true,
15733 selectedClass: "ydataview-selected",
15737 // listen for node click?
15738 view.on("click", function(vw, index, node, e){
15739 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15743 dataModel.load("foobar.xml");
15745 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15747 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15748 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15750 * Note: old style constructor is still suported (container, template, config)
15753 * Create a new View
15754 * @param {Object} config The config object
15757 Roo.View = function(config, depreciated_tpl, depreciated_config){
15759 this.parent = false;
15761 if (typeof(depreciated_tpl) == 'undefined') {
15762 // new way.. - universal constructor.
15763 Roo.apply(this, config);
15764 this.el = Roo.get(this.el);
15767 this.el = Roo.get(config);
15768 this.tpl = depreciated_tpl;
15769 Roo.apply(this, depreciated_config);
15771 this.wrapEl = this.el.wrap().wrap();
15772 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15775 if(typeof(this.tpl) == "string"){
15776 this.tpl = new Roo.Template(this.tpl);
15778 // support xtype ctors..
15779 this.tpl = new Roo.factory(this.tpl, Roo);
15783 this.tpl.compile();
15788 * @event beforeclick
15789 * Fires before a click is processed. Returns false to cancel the default action.
15790 * @param {Roo.View} this
15791 * @param {Number} index The index of the target node
15792 * @param {HTMLElement} node The target node
15793 * @param {Roo.EventObject} e The raw event object
15795 "beforeclick" : true,
15798 * Fires when a template node is clicked.
15799 * @param {Roo.View} this
15800 * @param {Number} index The index of the target node
15801 * @param {HTMLElement} node The target node
15802 * @param {Roo.EventObject} e The raw event object
15807 * Fires when a template node is double clicked.
15808 * @param {Roo.View} this
15809 * @param {Number} index The index of the target node
15810 * @param {HTMLElement} node The target node
15811 * @param {Roo.EventObject} e The raw event object
15815 * @event contextmenu
15816 * Fires when a template node is right clicked.
15817 * @param {Roo.View} this
15818 * @param {Number} index The index of the target node
15819 * @param {HTMLElement} node The target node
15820 * @param {Roo.EventObject} e The raw event object
15822 "contextmenu" : true,
15824 * @event selectionchange
15825 * Fires when the selected nodes change.
15826 * @param {Roo.View} this
15827 * @param {Array} selections Array of the selected nodes
15829 "selectionchange" : true,
15832 * @event beforeselect
15833 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15834 * @param {Roo.View} this
15835 * @param {HTMLElement} node The node to be selected
15836 * @param {Array} selections Array of currently selected nodes
15838 "beforeselect" : true,
15840 * @event preparedata
15841 * Fires on every row to render, to allow you to change the data.
15842 * @param {Roo.View} this
15843 * @param {Object} data to be rendered (change this)
15845 "preparedata" : true
15853 "click": this.onClick,
15854 "dblclick": this.onDblClick,
15855 "contextmenu": this.onContextMenu,
15859 this.selections = [];
15861 this.cmp = new Roo.CompositeElementLite([]);
15863 this.store = Roo.factory(this.store, Roo.data);
15864 this.setStore(this.store, true);
15867 if ( this.footer && this.footer.xtype) {
15869 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15871 this.footer.dataSource = this.store;
15872 this.footer.container = fctr;
15873 this.footer = Roo.factory(this.footer, Roo);
15874 fctr.insertFirst(this.el);
15876 // this is a bit insane - as the paging toolbar seems to detach the el..
15877 // dom.parentNode.parentNode.parentNode
15878 // they get detached?
15882 Roo.View.superclass.constructor.call(this);
15887 Roo.extend(Roo.View, Roo.util.Observable, {
15890 * @cfg {Roo.data.Store} store Data store to load data from.
15895 * @cfg {String|Roo.Element} el The container element.
15900 * @cfg {String|Roo.Template} tpl The template used by this View
15904 * @cfg {String} dataName the named area of the template to use as the data area
15905 * Works with domtemplates roo-name="name"
15909 * @cfg {String} selectedClass The css class to add to selected nodes
15911 selectedClass : "x-view-selected",
15913 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15918 * @cfg {String} text to display on mask (default Loading)
15922 * @cfg {Boolean} multiSelect Allow multiple selection
15924 multiSelect : false,
15926 * @cfg {Boolean} singleSelect Allow single selection
15928 singleSelect: false,
15931 * @cfg {Boolean} toggleSelect - selecting
15933 toggleSelect : false,
15936 * @cfg {Boolean} tickable - selecting
15941 * Returns the element this view is bound to.
15942 * @return {Roo.Element}
15944 getEl : function(){
15945 return this.wrapEl;
15951 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15953 refresh : function(){
15954 //Roo.log('refresh');
15957 // if we are using something like 'domtemplate', then
15958 // the what gets used is:
15959 // t.applySubtemplate(NAME, data, wrapping data..)
15960 // the outer template then get' applied with
15961 // the store 'extra data'
15962 // and the body get's added to the
15963 // roo-name="data" node?
15964 // <span class='roo-tpl-{name}'></span> ?????
15968 this.clearSelections();
15969 this.el.update("");
15971 var records = this.store.getRange();
15972 if(records.length < 1) {
15974 // is this valid?? = should it render a template??
15976 this.el.update(this.emptyText);
15980 if (this.dataName) {
15981 this.el.update(t.apply(this.store.meta)); //????
15982 el = this.el.child('.roo-tpl-' + this.dataName);
15985 for(var i = 0, len = records.length; i < len; i++){
15986 var data = this.prepareData(records[i].data, i, records[i]);
15987 this.fireEvent("preparedata", this, data, i, records[i]);
15989 var d = Roo.apply({}, data);
15992 Roo.apply(d, {'roo-id' : Roo.id()});
15996 Roo.each(this.parent.item, function(item){
15997 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16000 Roo.apply(d, {'roo-data-checked' : 'checked'});
16004 html[html.length] = Roo.util.Format.trim(
16006 t.applySubtemplate(this.dataName, d, this.store.meta) :
16013 el.update(html.join(""));
16014 this.nodes = el.dom.childNodes;
16015 this.updateIndexes(0);
16020 * Function to override to reformat the data that is sent to
16021 * the template for each node.
16022 * DEPRICATED - use the preparedata event handler.
16023 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16024 * a JSON object for an UpdateManager bound view).
16026 prepareData : function(data, index, record)
16028 this.fireEvent("preparedata", this, data, index, record);
16032 onUpdate : function(ds, record){
16033 // Roo.log('on update');
16034 this.clearSelections();
16035 var index = this.store.indexOf(record);
16036 var n = this.nodes[index];
16037 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16038 n.parentNode.removeChild(n);
16039 this.updateIndexes(index, index);
16045 onAdd : function(ds, records, index)
16047 //Roo.log(['on Add', ds, records, index] );
16048 this.clearSelections();
16049 if(this.nodes.length == 0){
16053 var n = this.nodes[index];
16054 for(var i = 0, len = records.length; i < len; i++){
16055 var d = this.prepareData(records[i].data, i, records[i]);
16057 this.tpl.insertBefore(n, d);
16060 this.tpl.append(this.el, d);
16063 this.updateIndexes(index);
16066 onRemove : function(ds, record, index){
16067 // Roo.log('onRemove');
16068 this.clearSelections();
16069 var el = this.dataName ?
16070 this.el.child('.roo-tpl-' + this.dataName) :
16073 el.dom.removeChild(this.nodes[index]);
16074 this.updateIndexes(index);
16078 * Refresh an individual node.
16079 * @param {Number} index
16081 refreshNode : function(index){
16082 this.onUpdate(this.store, this.store.getAt(index));
16085 updateIndexes : function(startIndex, endIndex){
16086 var ns = this.nodes;
16087 startIndex = startIndex || 0;
16088 endIndex = endIndex || ns.length - 1;
16089 for(var i = startIndex; i <= endIndex; i++){
16090 ns[i].nodeIndex = i;
16095 * Changes the data store this view uses and refresh the view.
16096 * @param {Store} store
16098 setStore : function(store, initial){
16099 if(!initial && this.store){
16100 this.store.un("datachanged", this.refresh);
16101 this.store.un("add", this.onAdd);
16102 this.store.un("remove", this.onRemove);
16103 this.store.un("update", this.onUpdate);
16104 this.store.un("clear", this.refresh);
16105 this.store.un("beforeload", this.onBeforeLoad);
16106 this.store.un("load", this.onLoad);
16107 this.store.un("loadexception", this.onLoad);
16111 store.on("datachanged", this.refresh, this);
16112 store.on("add", this.onAdd, this);
16113 store.on("remove", this.onRemove, this);
16114 store.on("update", this.onUpdate, this);
16115 store.on("clear", this.refresh, this);
16116 store.on("beforeload", this.onBeforeLoad, this);
16117 store.on("load", this.onLoad, this);
16118 store.on("loadexception", this.onLoad, this);
16126 * onbeforeLoad - masks the loading area.
16129 onBeforeLoad : function(store,opts)
16131 //Roo.log('onBeforeLoad');
16133 this.el.update("");
16135 this.el.mask(this.mask ? this.mask : "Loading" );
16137 onLoad : function ()
16144 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16145 * @param {HTMLElement} node
16146 * @return {HTMLElement} The template node
16148 findItemFromChild : function(node){
16149 var el = this.dataName ?
16150 this.el.child('.roo-tpl-' + this.dataName,true) :
16153 if(!node || node.parentNode == el){
16156 var p = node.parentNode;
16157 while(p && p != el){
16158 if(p.parentNode == el){
16167 onClick : function(e){
16168 var item = this.findItemFromChild(e.getTarget());
16170 var index = this.indexOf(item);
16171 if(this.onItemClick(item, index, e) !== false){
16172 this.fireEvent("click", this, index, item, e);
16175 this.clearSelections();
16180 onContextMenu : function(e){
16181 var item = this.findItemFromChild(e.getTarget());
16183 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16188 onDblClick : function(e){
16189 var item = this.findItemFromChild(e.getTarget());
16191 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16195 onItemClick : function(item, index, e)
16197 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16200 if (this.toggleSelect) {
16201 var m = this.isSelected(item) ? 'unselect' : 'select';
16204 _t[m](item, true, false);
16207 if(this.multiSelect || this.singleSelect){
16208 if(this.multiSelect && e.shiftKey && this.lastSelection){
16209 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16211 this.select(item, this.multiSelect && e.ctrlKey);
16212 this.lastSelection = item;
16215 if(!this.tickable){
16216 e.preventDefault();
16224 * Get the number of selected nodes.
16227 getSelectionCount : function(){
16228 return this.selections.length;
16232 * Get the currently selected nodes.
16233 * @return {Array} An array of HTMLElements
16235 getSelectedNodes : function(){
16236 return this.selections;
16240 * Get the indexes of the selected nodes.
16243 getSelectedIndexes : function(){
16244 var indexes = [], s = this.selections;
16245 for(var i = 0, len = s.length; i < len; i++){
16246 indexes.push(s[i].nodeIndex);
16252 * Clear all selections
16253 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16255 clearSelections : function(suppressEvent){
16256 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16257 this.cmp.elements = this.selections;
16258 this.cmp.removeClass(this.selectedClass);
16259 this.selections = [];
16260 if(!suppressEvent){
16261 this.fireEvent("selectionchange", this, this.selections);
16267 * Returns true if the passed node is selected
16268 * @param {HTMLElement/Number} node The node or node index
16269 * @return {Boolean}
16271 isSelected : function(node){
16272 var s = this.selections;
16276 node = this.getNode(node);
16277 return s.indexOf(node) !== -1;
16282 * @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
16283 * @param {Boolean} keepExisting (optional) true to keep existing selections
16284 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16286 select : function(nodeInfo, keepExisting, suppressEvent){
16287 if(nodeInfo instanceof Array){
16289 this.clearSelections(true);
16291 for(var i = 0, len = nodeInfo.length; i < len; i++){
16292 this.select(nodeInfo[i], true, true);
16296 var node = this.getNode(nodeInfo);
16297 if(!node || this.isSelected(node)){
16298 return; // already selected.
16301 this.clearSelections(true);
16304 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16305 Roo.fly(node).addClass(this.selectedClass);
16306 this.selections.push(node);
16307 if(!suppressEvent){
16308 this.fireEvent("selectionchange", this, this.selections);
16316 * @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
16317 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16318 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16320 unselect : function(nodeInfo, keepExisting, suppressEvent)
16322 if(nodeInfo instanceof Array){
16323 Roo.each(this.selections, function(s) {
16324 this.unselect(s, nodeInfo);
16328 var node = this.getNode(nodeInfo);
16329 if(!node || !this.isSelected(node)){
16330 //Roo.log("not selected");
16331 return; // not selected.
16335 Roo.each(this.selections, function(s) {
16337 Roo.fly(node).removeClass(this.selectedClass);
16344 this.selections= ns;
16345 this.fireEvent("selectionchange", this, this.selections);
16349 * Gets a template node.
16350 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16351 * @return {HTMLElement} The node or null if it wasn't found
16353 getNode : function(nodeInfo){
16354 if(typeof nodeInfo == "string"){
16355 return document.getElementById(nodeInfo);
16356 }else if(typeof nodeInfo == "number"){
16357 return this.nodes[nodeInfo];
16363 * Gets a range template nodes.
16364 * @param {Number} startIndex
16365 * @param {Number} endIndex
16366 * @return {Array} An array of nodes
16368 getNodes : function(start, end){
16369 var ns = this.nodes;
16370 start = start || 0;
16371 end = typeof end == "undefined" ? ns.length - 1 : end;
16374 for(var i = start; i <= end; i++){
16378 for(var i = start; i >= end; i--){
16386 * Finds the index of the passed node
16387 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16388 * @return {Number} The index of the node or -1
16390 indexOf : function(node){
16391 node = this.getNode(node);
16392 if(typeof node.nodeIndex == "number"){
16393 return node.nodeIndex;
16395 var ns = this.nodes;
16396 for(var i = 0, len = ns.length; i < len; i++){
16407 * based on jquery fullcalendar
16411 Roo.bootstrap = Roo.bootstrap || {};
16413 * @class Roo.bootstrap.Calendar
16414 * @extends Roo.bootstrap.Component
16415 * Bootstrap Calendar class
16416 * @cfg {Boolean} loadMask (true|false) default false
16417 * @cfg {Object} header generate the user specific header of the calendar, default false
16420 * Create a new Container
16421 * @param {Object} config The config object
16426 Roo.bootstrap.Calendar = function(config){
16427 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16431 * Fires when a date is selected
16432 * @param {DatePicker} this
16433 * @param {Date} date The selected date
16437 * @event monthchange
16438 * Fires when the displayed month changes
16439 * @param {DatePicker} this
16440 * @param {Date} date The selected month
16442 'monthchange': true,
16444 * @event evententer
16445 * Fires when mouse over an event
16446 * @param {Calendar} this
16447 * @param {event} Event
16449 'evententer': true,
16451 * @event eventleave
16452 * Fires when the mouse leaves an
16453 * @param {Calendar} this
16456 'eventleave': true,
16458 * @event eventclick
16459 * Fires when the mouse click an
16460 * @param {Calendar} this
16469 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16472 * @cfg {Number} startDay
16473 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16481 getAutoCreate : function(){
16484 var fc_button = function(name, corner, style, content ) {
16485 return Roo.apply({},{
16487 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16489 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16492 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16503 style : 'width:100%',
16510 cls : 'fc-header-left',
16512 fc_button('prev', 'left', 'arrow', '‹' ),
16513 fc_button('next', 'right', 'arrow', '›' ),
16514 { tag: 'span', cls: 'fc-header-space' },
16515 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16523 cls : 'fc-header-center',
16527 cls: 'fc-header-title',
16530 html : 'month / year'
16538 cls : 'fc-header-right',
16540 /* fc_button('month', 'left', '', 'month' ),
16541 fc_button('week', '', '', 'week' ),
16542 fc_button('day', 'right', '', 'day' )
16554 header = this.header;
16557 var cal_heads = function() {
16559 // fixme - handle this.
16561 for (var i =0; i < Date.dayNames.length; i++) {
16562 var d = Date.dayNames[i];
16565 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16566 html : d.substring(0,3)
16570 ret[0].cls += ' fc-first';
16571 ret[6].cls += ' fc-last';
16574 var cal_cell = function(n) {
16577 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16582 cls: 'fc-day-number',
16586 cls: 'fc-day-content',
16590 style: 'position: relative;' // height: 17px;
16602 var cal_rows = function() {
16605 for (var r = 0; r < 6; r++) {
16612 for (var i =0; i < Date.dayNames.length; i++) {
16613 var d = Date.dayNames[i];
16614 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16617 row.cn[0].cls+=' fc-first';
16618 row.cn[0].cn[0].style = 'min-height:90px';
16619 row.cn[6].cls+=' fc-last';
16623 ret[0].cls += ' fc-first';
16624 ret[4].cls += ' fc-prev-last';
16625 ret[5].cls += ' fc-last';
16632 cls: 'fc-border-separate',
16633 style : 'width:100%',
16641 cls : 'fc-first fc-last',
16659 cls : 'fc-content',
16660 style : "position: relative;",
16663 cls : 'fc-view fc-view-month fc-grid',
16664 style : 'position: relative',
16665 unselectable : 'on',
16668 cls : 'fc-event-container',
16669 style : 'position:absolute;z-index:8;top:0;left:0;'
16687 initEvents : function()
16690 throw "can not find store for calendar";
16696 style: "text-align:center",
16700 style: "background-color:white;width:50%;margin:250 auto",
16704 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16715 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16717 var size = this.el.select('.fc-content', true).first().getSize();
16718 this.maskEl.setSize(size.width, size.height);
16719 this.maskEl.enableDisplayMode("block");
16720 if(!this.loadMask){
16721 this.maskEl.hide();
16724 this.store = Roo.factory(this.store, Roo.data);
16725 this.store.on('load', this.onLoad, this);
16726 this.store.on('beforeload', this.onBeforeLoad, this);
16730 this.cells = this.el.select('.fc-day',true);
16731 //Roo.log(this.cells);
16732 this.textNodes = this.el.query('.fc-day-number');
16733 this.cells.addClassOnOver('fc-state-hover');
16735 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16736 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16737 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16738 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16740 this.on('monthchange', this.onMonthChange, this);
16742 this.update(new Date().clearTime());
16745 resize : function() {
16746 var sz = this.el.getSize();
16748 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16749 this.el.select('.fc-day-content div',true).setHeight(34);
16754 showPrevMonth : function(e){
16755 this.update(this.activeDate.add("mo", -1));
16757 showToday : function(e){
16758 this.update(new Date().clearTime());
16761 showNextMonth : function(e){
16762 this.update(this.activeDate.add("mo", 1));
16766 showPrevYear : function(){
16767 this.update(this.activeDate.add("y", -1));
16771 showNextYear : function(){
16772 this.update(this.activeDate.add("y", 1));
16777 update : function(date)
16779 var vd = this.activeDate;
16780 this.activeDate = date;
16781 // if(vd && this.el){
16782 // var t = date.getTime();
16783 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16784 // Roo.log('using add remove');
16786 // this.fireEvent('monthchange', this, date);
16788 // this.cells.removeClass("fc-state-highlight");
16789 // this.cells.each(function(c){
16790 // if(c.dateValue == t){
16791 // c.addClass("fc-state-highlight");
16792 // setTimeout(function(){
16793 // try{c.dom.firstChild.focus();}catch(e){}
16803 var days = date.getDaysInMonth();
16805 var firstOfMonth = date.getFirstDateOfMonth();
16806 var startingPos = firstOfMonth.getDay()-this.startDay;
16808 if(startingPos < this.startDay){
16812 var pm = date.add(Date.MONTH, -1);
16813 var prevStart = pm.getDaysInMonth()-startingPos;
16815 this.cells = this.el.select('.fc-day',true);
16816 this.textNodes = this.el.query('.fc-day-number');
16817 this.cells.addClassOnOver('fc-state-hover');
16819 var cells = this.cells.elements;
16820 var textEls = this.textNodes;
16822 Roo.each(cells, function(cell){
16823 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16826 days += startingPos;
16828 // convert everything to numbers so it's fast
16829 var day = 86400000;
16830 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16833 //Roo.log(prevStart);
16835 var today = new Date().clearTime().getTime();
16836 var sel = date.clearTime().getTime();
16837 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16838 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16839 var ddMatch = this.disabledDatesRE;
16840 var ddText = this.disabledDatesText;
16841 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16842 var ddaysText = this.disabledDaysText;
16843 var format = this.format;
16845 var setCellClass = function(cal, cell){
16849 //Roo.log('set Cell Class');
16851 var t = d.getTime();
16855 cell.dateValue = t;
16857 cell.className += " fc-today";
16858 cell.className += " fc-state-highlight";
16859 cell.title = cal.todayText;
16862 // disable highlight in other month..
16863 //cell.className += " fc-state-highlight";
16868 cell.className = " fc-state-disabled";
16869 cell.title = cal.minText;
16873 cell.className = " fc-state-disabled";
16874 cell.title = cal.maxText;
16878 if(ddays.indexOf(d.getDay()) != -1){
16879 cell.title = ddaysText;
16880 cell.className = " fc-state-disabled";
16883 if(ddMatch && format){
16884 var fvalue = d.dateFormat(format);
16885 if(ddMatch.test(fvalue)){
16886 cell.title = ddText.replace("%0", fvalue);
16887 cell.className = " fc-state-disabled";
16891 if (!cell.initialClassName) {
16892 cell.initialClassName = cell.dom.className;
16895 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16900 for(; i < startingPos; i++) {
16901 textEls[i].innerHTML = (++prevStart);
16902 d.setDate(d.getDate()+1);
16904 cells[i].className = "fc-past fc-other-month";
16905 setCellClass(this, cells[i]);
16910 for(; i < days; i++){
16911 intDay = i - startingPos + 1;
16912 textEls[i].innerHTML = (intDay);
16913 d.setDate(d.getDate()+1);
16915 cells[i].className = ''; // "x-date-active";
16916 setCellClass(this, cells[i]);
16920 for(; i < 42; i++) {
16921 textEls[i].innerHTML = (++extraDays);
16922 d.setDate(d.getDate()+1);
16924 cells[i].className = "fc-future fc-other-month";
16925 setCellClass(this, cells[i]);
16928 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16930 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16932 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16933 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16935 if(totalRows != 6){
16936 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16937 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16940 this.fireEvent('monthchange', this, date);
16944 if(!this.internalRender){
16945 var main = this.el.dom.firstChild;
16946 var w = main.offsetWidth;
16947 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16948 Roo.fly(main).setWidth(w);
16949 this.internalRender = true;
16950 // opera does not respect the auto grow header center column
16951 // then, after it gets a width opera refuses to recalculate
16952 // without a second pass
16953 if(Roo.isOpera && !this.secondPass){
16954 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16955 this.secondPass = true;
16956 this.update.defer(10, this, [date]);
16963 findCell : function(dt) {
16964 dt = dt.clearTime().getTime();
16966 this.cells.each(function(c){
16967 //Roo.log("check " +c.dateValue + '?=' + dt);
16968 if(c.dateValue == dt){
16978 findCells : function(ev) {
16979 var s = ev.start.clone().clearTime().getTime();
16981 var e= ev.end.clone().clearTime().getTime();
16984 this.cells.each(function(c){
16985 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16987 if(c.dateValue > e){
16990 if(c.dateValue < s){
16999 // findBestRow: function(cells)
17003 // for (var i =0 ; i < cells.length;i++) {
17004 // ret = Math.max(cells[i].rows || 0,ret);
17011 addItem : function(ev)
17013 // look for vertical location slot in
17014 var cells = this.findCells(ev);
17016 // ev.row = this.findBestRow(cells);
17018 // work out the location.
17022 for(var i =0; i < cells.length; i++) {
17024 cells[i].row = cells[0].row;
17027 cells[i].row = cells[i].row + 1;
17037 if (crow.start.getY() == cells[i].getY()) {
17039 crow.end = cells[i];
17056 cells[0].events.push(ev);
17058 this.calevents.push(ev);
17061 clearEvents: function() {
17063 if(!this.calevents){
17067 Roo.each(this.cells.elements, function(c){
17073 Roo.each(this.calevents, function(e) {
17074 Roo.each(e.els, function(el) {
17075 el.un('mouseenter' ,this.onEventEnter, this);
17076 el.un('mouseleave' ,this.onEventLeave, this);
17081 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17087 renderEvents: function()
17091 this.cells.each(function(c) {
17100 if(c.row != c.events.length){
17101 r = 4 - (4 - (c.row - c.events.length));
17104 c.events = ev.slice(0, r);
17105 c.more = ev.slice(r);
17107 if(c.more.length && c.more.length == 1){
17108 c.events.push(c.more.pop());
17111 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17115 this.cells.each(function(c) {
17117 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17120 for (var e = 0; e < c.events.length; e++){
17121 var ev = c.events[e];
17122 var rows = ev.rows;
17124 for(var i = 0; i < rows.length; i++) {
17126 // how many rows should it span..
17129 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17130 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17132 unselectable : "on",
17135 cls: 'fc-event-inner',
17139 // cls: 'fc-event-time',
17140 // html : cells.length > 1 ? '' : ev.time
17144 cls: 'fc-event-title',
17145 html : String.format('{0}', ev.title)
17152 cls: 'ui-resizable-handle ui-resizable-e',
17153 html : '  '
17160 cfg.cls += ' fc-event-start';
17162 if ((i+1) == rows.length) {
17163 cfg.cls += ' fc-event-end';
17166 var ctr = _this.el.select('.fc-event-container',true).first();
17167 var cg = ctr.createChild(cfg);
17169 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17170 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17172 var r = (c.more.length) ? 1 : 0;
17173 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17174 cg.setWidth(ebox.right - sbox.x -2);
17176 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17177 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17178 cg.on('click', _this.onEventClick, _this, ev);
17189 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17190 style : 'position: absolute',
17191 unselectable : "on",
17194 cls: 'fc-event-inner',
17198 cls: 'fc-event-title',
17206 cls: 'ui-resizable-handle ui-resizable-e',
17207 html : '  '
17213 var ctr = _this.el.select('.fc-event-container',true).first();
17214 var cg = ctr.createChild(cfg);
17216 var sbox = c.select('.fc-day-content',true).first().getBox();
17217 var ebox = c.select('.fc-day-content',true).first().getBox();
17219 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17220 cg.setWidth(ebox.right - sbox.x -2);
17222 cg.on('click', _this.onMoreEventClick, _this, c.more);
17232 onEventEnter: function (e, el,event,d) {
17233 this.fireEvent('evententer', this, el, event);
17236 onEventLeave: function (e, el,event,d) {
17237 this.fireEvent('eventleave', this, el, event);
17240 onEventClick: function (e, el,event,d) {
17241 this.fireEvent('eventclick', this, el, event);
17244 onMonthChange: function () {
17248 onMoreEventClick: function(e, el, more)
17252 this.calpopover.placement = 'right';
17253 this.calpopover.setTitle('More');
17255 this.calpopover.setContent('');
17257 var ctr = this.calpopover.el.select('.popover-content', true).first();
17259 Roo.each(more, function(m){
17261 cls : 'fc-event-hori fc-event-draggable',
17264 var cg = ctr.createChild(cfg);
17266 cg.on('click', _this.onEventClick, _this, m);
17269 this.calpopover.show(el);
17274 onLoad: function ()
17276 this.calevents = [];
17279 if(this.store.getCount() > 0){
17280 this.store.data.each(function(d){
17283 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17284 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17285 time : d.data.start_time,
17286 title : d.data.title,
17287 description : d.data.description,
17288 venue : d.data.venue
17293 this.renderEvents();
17295 if(this.calevents.length && this.loadMask){
17296 this.maskEl.hide();
17300 onBeforeLoad: function()
17302 this.clearEvents();
17304 this.maskEl.show();
17318 * @class Roo.bootstrap.Popover
17319 * @extends Roo.bootstrap.Component
17320 * Bootstrap Popover class
17321 * @cfg {String} html contents of the popover (or false to use children..)
17322 * @cfg {String} title of popover (or false to hide)
17323 * @cfg {String} placement how it is placed
17324 * @cfg {String} trigger click || hover (or false to trigger manually)
17325 * @cfg {String} over what (parent or false to trigger manually.)
17326 * @cfg {Number} delay - delay before showing
17329 * Create a new Popover
17330 * @param {Object} config The config object
17333 Roo.bootstrap.Popover = function(config){
17334 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17340 * After the popover show
17342 * @param {Roo.bootstrap.Popover} this
17347 * After the popover hide
17349 * @param {Roo.bootstrap.Popover} this
17355 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17357 title: 'Fill in a title',
17360 placement : 'right',
17361 trigger : 'hover', // hover
17367 can_build_overlaid : false,
17369 getChildContainer : function()
17371 return this.el.select('.popover-content',true).first();
17374 getAutoCreate : function(){
17377 cls : 'popover roo-dynamic',
17378 style: 'display:block',
17384 cls : 'popover-inner',
17388 cls: 'popover-title',
17392 cls : 'popover-content',
17403 setTitle: function(str)
17406 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17408 setContent: function(str)
17411 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17413 // as it get's added to the bottom of the page.
17414 onRender : function(ct, position)
17416 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17418 var cfg = Roo.apply({}, this.getAutoCreate());
17422 cfg.cls += ' ' + this.cls;
17425 cfg.style = this.style;
17427 //Roo.log("adding to ");
17428 this.el = Roo.get(document.body).createChild(cfg, position);
17429 // Roo.log(this.el);
17434 initEvents : function()
17436 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17437 this.el.enableDisplayMode('block');
17439 if (this.over === false) {
17442 if (this.triggers === false) {
17445 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17446 var triggers = this.trigger ? this.trigger.split(' ') : [];
17447 Roo.each(triggers, function(trigger) {
17449 if (trigger == 'click') {
17450 on_el.on('click', this.toggle, this);
17451 } else if (trigger != 'manual') {
17452 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17453 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17455 on_el.on(eventIn ,this.enter, this);
17456 on_el.on(eventOut, this.leave, this);
17467 toggle : function () {
17468 this.hoverState == 'in' ? this.leave() : this.enter();
17471 enter : function () {
17473 clearTimeout(this.timeout);
17475 this.hoverState = 'in';
17477 if (!this.delay || !this.delay.show) {
17482 this.timeout = setTimeout(function () {
17483 if (_t.hoverState == 'in') {
17486 }, this.delay.show)
17489 leave : function() {
17490 clearTimeout(this.timeout);
17492 this.hoverState = 'out';
17494 if (!this.delay || !this.delay.hide) {
17499 this.timeout = setTimeout(function () {
17500 if (_t.hoverState == 'out') {
17503 }, this.delay.hide)
17506 show : function (on_el)
17509 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17513 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17514 if (this.html !== false) {
17515 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17517 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17518 if (!this.title.length) {
17519 this.el.select('.popover-title',true).hide();
17522 var placement = typeof this.placement == 'function' ?
17523 this.placement.call(this, this.el, on_el) :
17526 var autoToken = /\s?auto?\s?/i;
17527 var autoPlace = autoToken.test(placement);
17529 placement = placement.replace(autoToken, '') || 'top';
17533 //this.el.setXY([0,0]);
17535 this.el.dom.style.display='block';
17536 this.el.addClass(placement);
17538 //this.el.appendTo(on_el);
17540 var p = this.getPosition();
17541 var box = this.el.getBox();
17546 var align = Roo.bootstrap.Popover.alignment[placement];
17549 this.el.alignTo(on_el, align[0],align[1]);
17550 //var arrow = this.el.select('.arrow',true).first();
17551 //arrow.set(align[2],
17553 this.el.addClass('in');
17556 if (this.el.hasClass('fade')) {
17560 this.hoverState = 'in';
17562 this.fireEvent('show', this);
17567 this.el.setXY([0,0]);
17568 this.el.removeClass('in');
17570 this.hoverState = null;
17572 this.fireEvent('hide', this);
17577 Roo.bootstrap.Popover.alignment = {
17578 'left' : ['r-l', [-10,0], 'right'],
17579 'right' : ['l-r', [10,0], 'left'],
17580 'bottom' : ['t-b', [0,10], 'top'],
17581 'top' : [ 'b-t', [0,-10], 'bottom']
17592 * @class Roo.bootstrap.Progress
17593 * @extends Roo.bootstrap.Component
17594 * Bootstrap Progress class
17595 * @cfg {Boolean} striped striped of the progress bar
17596 * @cfg {Boolean} active animated of the progress bar
17600 * Create a new Progress
17601 * @param {Object} config The config object
17604 Roo.bootstrap.Progress = function(config){
17605 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17608 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17613 getAutoCreate : function(){
17621 cfg.cls += ' progress-striped';
17625 cfg.cls += ' active';
17644 * @class Roo.bootstrap.ProgressBar
17645 * @extends Roo.bootstrap.Component
17646 * Bootstrap ProgressBar class
17647 * @cfg {Number} aria_valuenow aria-value now
17648 * @cfg {Number} aria_valuemin aria-value min
17649 * @cfg {Number} aria_valuemax aria-value max
17650 * @cfg {String} label label for the progress bar
17651 * @cfg {String} panel (success | info | warning | danger )
17652 * @cfg {String} role role of the progress bar
17653 * @cfg {String} sr_only text
17657 * Create a new ProgressBar
17658 * @param {Object} config The config object
17661 Roo.bootstrap.ProgressBar = function(config){
17662 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17665 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17669 aria_valuemax : 100,
17675 getAutoCreate : function()
17680 cls: 'progress-bar',
17681 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17693 cfg.role = this.role;
17696 if(this.aria_valuenow){
17697 cfg['aria-valuenow'] = this.aria_valuenow;
17700 if(this.aria_valuemin){
17701 cfg['aria-valuemin'] = this.aria_valuemin;
17704 if(this.aria_valuemax){
17705 cfg['aria-valuemax'] = this.aria_valuemax;
17708 if(this.label && !this.sr_only){
17709 cfg.html = this.label;
17713 cfg.cls += ' progress-bar-' + this.panel;
17719 update : function(aria_valuenow)
17721 this.aria_valuenow = aria_valuenow;
17723 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17738 * @class Roo.bootstrap.TabGroup
17739 * @extends Roo.bootstrap.Column
17740 * Bootstrap Column class
17741 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17742 * @cfg {Boolean} carousel true to make the group behave like a carousel
17743 * @cfg {Boolean} bullets show bullets for the panels
17744 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17745 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17746 * @cfg {Boolean} showarrow (true|false) show arrow default true
17749 * Create a new TabGroup
17750 * @param {Object} config The config object
17753 Roo.bootstrap.TabGroup = function(config){
17754 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17756 this.navId = Roo.id();
17759 Roo.bootstrap.TabGroup.register(this);
17763 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17766 transition : false,
17771 slideOnTouch : false,
17774 getAutoCreate : function()
17776 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17778 cfg.cls += ' tab-content';
17780 if (this.carousel) {
17781 cfg.cls += ' carousel slide';
17784 cls : 'carousel-inner',
17788 if(this.bullets && !Roo.isTouch){
17791 cls : 'carousel-bullets',
17795 if(this.bullets_cls){
17796 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17803 cfg.cn[0].cn.push(bullets);
17806 if(this.showarrow){
17807 cfg.cn[0].cn.push({
17809 class : 'carousel-arrow',
17813 class : 'carousel-prev',
17817 class : 'fa fa-chevron-left'
17823 class : 'carousel-next',
17827 class : 'fa fa-chevron-right'
17840 initEvents: function()
17842 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17843 // this.el.on("touchstart", this.onTouchStart, this);
17846 if(this.autoslide){
17849 this.slideFn = window.setInterval(function() {
17850 _this.showPanelNext();
17854 if(this.showarrow){
17855 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17856 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17862 // onTouchStart : function(e, el, o)
17864 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17868 // this.showPanelNext();
17872 getChildContainer : function()
17874 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17878 * register a Navigation item
17879 * @param {Roo.bootstrap.NavItem} the navitem to add
17881 register : function(item)
17883 this.tabs.push( item);
17884 item.navId = this.navId; // not really needed..
17889 getActivePanel : function()
17892 Roo.each(this.tabs, function(t) {
17902 getPanelByName : function(n)
17905 Roo.each(this.tabs, function(t) {
17906 if (t.tabId == n) {
17914 indexOfPanel : function(p)
17917 Roo.each(this.tabs, function(t,i) {
17918 if (t.tabId == p.tabId) {
17927 * show a specific panel
17928 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17929 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17931 showPanel : function (pan)
17933 if(this.transition || typeof(pan) == 'undefined'){
17934 Roo.log("waiting for the transitionend");
17938 if (typeof(pan) == 'number') {
17939 pan = this.tabs[pan];
17942 if (typeof(pan) == 'string') {
17943 pan = this.getPanelByName(pan);
17946 var cur = this.getActivePanel();
17949 Roo.log('pan or acitve pan is undefined');
17953 if (pan.tabId == this.getActivePanel().tabId) {
17957 if (false === cur.fireEvent('beforedeactivate')) {
17961 if(this.bullets > 0 && !Roo.isTouch){
17962 this.setActiveBullet(this.indexOfPanel(pan));
17965 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17967 this.transition = true;
17968 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17969 var lr = dir == 'next' ? 'left' : 'right';
17970 pan.el.addClass(dir); // or prev
17971 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17972 cur.el.addClass(lr); // or right
17973 pan.el.addClass(lr);
17976 cur.el.on('transitionend', function() {
17977 Roo.log("trans end?");
17979 pan.el.removeClass([lr,dir]);
17980 pan.setActive(true);
17982 cur.el.removeClass([lr]);
17983 cur.setActive(false);
17985 _this.transition = false;
17987 }, this, { single: true } );
17992 cur.setActive(false);
17993 pan.setActive(true);
17998 showPanelNext : function()
18000 var i = this.indexOfPanel(this.getActivePanel());
18002 if (i >= this.tabs.length - 1 && !this.autoslide) {
18006 if (i >= this.tabs.length - 1 && this.autoslide) {
18010 this.showPanel(this.tabs[i+1]);
18013 showPanelPrev : function()
18015 var i = this.indexOfPanel(this.getActivePanel());
18017 if (i < 1 && !this.autoslide) {
18021 if (i < 1 && this.autoslide) {
18022 i = this.tabs.length;
18025 this.showPanel(this.tabs[i-1]);
18029 addBullet: function()
18031 if(!this.bullets || Roo.isTouch){
18034 var ctr = this.el.select('.carousel-bullets',true).first();
18035 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18036 var bullet = ctr.createChild({
18037 cls : 'bullet bullet-' + i
18038 },ctr.dom.lastChild);
18043 bullet.on('click', (function(e, el, o, ii, t){
18045 e.preventDefault();
18047 this.showPanel(ii);
18049 if(this.autoslide && this.slideFn){
18050 clearInterval(this.slideFn);
18051 this.slideFn = window.setInterval(function() {
18052 _this.showPanelNext();
18056 }).createDelegate(this, [i, bullet], true));
18061 setActiveBullet : function(i)
18067 Roo.each(this.el.select('.bullet', true).elements, function(el){
18068 el.removeClass('selected');
18071 var bullet = this.el.select('.bullet-' + i, true).first();
18077 bullet.addClass('selected');
18088 Roo.apply(Roo.bootstrap.TabGroup, {
18092 * register a Navigation Group
18093 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18095 register : function(navgrp)
18097 this.groups[navgrp.navId] = navgrp;
18101 * fetch a Navigation Group based on the navigation ID
18102 * if one does not exist , it will get created.
18103 * @param {string} the navgroup to add
18104 * @returns {Roo.bootstrap.NavGroup} the navgroup
18106 get: function(navId) {
18107 if (typeof(this.groups[navId]) == 'undefined') {
18108 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18110 return this.groups[navId] ;
18125 * @class Roo.bootstrap.TabPanel
18126 * @extends Roo.bootstrap.Component
18127 * Bootstrap TabPanel class
18128 * @cfg {Boolean} active panel active
18129 * @cfg {String} html panel content
18130 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18131 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18132 * @cfg {String} href click to link..
18136 * Create a new TabPanel
18137 * @param {Object} config The config object
18140 Roo.bootstrap.TabPanel = function(config){
18141 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18145 * Fires when the active status changes
18146 * @param {Roo.bootstrap.TabPanel} this
18147 * @param {Boolean} state the new state
18152 * @event beforedeactivate
18153 * Fires before a tab is de-activated - can be used to do validation on a form.
18154 * @param {Roo.bootstrap.TabPanel} this
18155 * @return {Boolean} false if there is an error
18158 'beforedeactivate': true
18161 this.tabId = this.tabId || Roo.id();
18165 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18173 getAutoCreate : function(){
18176 // item is needed for carousel - not sure if it has any effect otherwise
18177 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18178 html: this.html || ''
18182 cfg.cls += ' active';
18186 cfg.tabId = this.tabId;
18193 initEvents: function()
18195 var p = this.parent();
18197 this.navId = this.navId || p.navId;
18199 if (typeof(this.navId) != 'undefined') {
18200 // not really needed.. but just in case.. parent should be a NavGroup.
18201 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18205 var i = tg.tabs.length - 1;
18207 if(this.active && tg.bullets > 0 && i < tg.bullets){
18208 tg.setActiveBullet(i);
18212 this.el.on('click', this.onClick, this);
18215 this.el.on("touchstart", this.onTouchStart, this);
18216 this.el.on("touchmove", this.onTouchMove, this);
18217 this.el.on("touchend", this.onTouchEnd, this);
18222 onRender : function(ct, position)
18224 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18227 setActive : function(state)
18229 Roo.log("panel - set active " + this.tabId + "=" + state);
18231 this.active = state;
18233 this.el.removeClass('active');
18235 } else if (!this.el.hasClass('active')) {
18236 this.el.addClass('active');
18239 this.fireEvent('changed', this, state);
18242 onClick : function(e)
18244 e.preventDefault();
18246 if(!this.href.length){
18250 window.location.href = this.href;
18259 onTouchStart : function(e)
18261 this.swiping = false;
18263 this.startX = e.browserEvent.touches[0].clientX;
18264 this.startY = e.browserEvent.touches[0].clientY;
18267 onTouchMove : function(e)
18269 this.swiping = true;
18271 this.endX = e.browserEvent.touches[0].clientX;
18272 this.endY = e.browserEvent.touches[0].clientY;
18275 onTouchEnd : function(e)
18282 var tabGroup = this.parent();
18284 if(this.endX > this.startX){ // swiping right
18285 tabGroup.showPanelPrev();
18289 if(this.startX > this.endX){ // swiping left
18290 tabGroup.showPanelNext();
18309 * @class Roo.bootstrap.DateField
18310 * @extends Roo.bootstrap.Input
18311 * Bootstrap DateField class
18312 * @cfg {Number} weekStart default 0
18313 * @cfg {String} viewMode default empty, (months|years)
18314 * @cfg {String} minViewMode default empty, (months|years)
18315 * @cfg {Number} startDate default -Infinity
18316 * @cfg {Number} endDate default Infinity
18317 * @cfg {Boolean} todayHighlight default false
18318 * @cfg {Boolean} todayBtn default false
18319 * @cfg {Boolean} calendarWeeks default false
18320 * @cfg {Object} daysOfWeekDisabled default empty
18321 * @cfg {Boolean} singleMode default false (true | false)
18323 * @cfg {Boolean} keyboardNavigation default true
18324 * @cfg {String} language default en
18327 * Create a new DateField
18328 * @param {Object} config The config object
18331 Roo.bootstrap.DateField = function(config){
18332 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18336 * Fires when this field show.
18337 * @param {Roo.bootstrap.DateField} this
18338 * @param {Mixed} date The date value
18343 * Fires when this field hide.
18344 * @param {Roo.bootstrap.DateField} this
18345 * @param {Mixed} date The date value
18350 * Fires when select a date.
18351 * @param {Roo.bootstrap.DateField} this
18352 * @param {Mixed} date The date value
18356 * @event beforeselect
18357 * Fires when before select a date.
18358 * @param {Roo.bootstrap.DateField} this
18359 * @param {Mixed} date The date value
18361 beforeselect : true
18365 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18368 * @cfg {String} format
18369 * The default date format string which can be overriden for localization support. The format must be
18370 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18374 * @cfg {String} altFormats
18375 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18376 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18378 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18386 todayHighlight : false,
18392 keyboardNavigation: true,
18394 calendarWeeks: false,
18396 startDate: -Infinity,
18400 daysOfWeekDisabled: [],
18404 singleMode : false,
18406 UTCDate: function()
18408 return new Date(Date.UTC.apply(Date, arguments));
18411 UTCToday: function()
18413 var today = new Date();
18414 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18417 getDate: function() {
18418 var d = this.getUTCDate();
18419 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18422 getUTCDate: function() {
18426 setDate: function(d) {
18427 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18430 setUTCDate: function(d) {
18432 this.setValue(this.formatDate(this.date));
18435 onRender: function(ct, position)
18438 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18440 this.language = this.language || 'en';
18441 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18442 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18444 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18445 this.format = this.format || 'm/d/y';
18446 this.isInline = false;
18447 this.isInput = true;
18448 this.component = this.el.select('.add-on', true).first() || false;
18449 this.component = (this.component && this.component.length === 0) ? false : this.component;
18450 this.hasInput = this.component && this.inputEl().length;
18452 if (typeof(this.minViewMode === 'string')) {
18453 switch (this.minViewMode) {
18455 this.minViewMode = 1;
18458 this.minViewMode = 2;
18461 this.minViewMode = 0;
18466 if (typeof(this.viewMode === 'string')) {
18467 switch (this.viewMode) {
18480 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18482 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18484 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18486 this.picker().on('mousedown', this.onMousedown, this);
18487 this.picker().on('click', this.onClick, this);
18489 this.picker().addClass('datepicker-dropdown');
18491 this.startViewMode = this.viewMode;
18493 if(this.singleMode){
18494 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18495 v.setVisibilityMode(Roo.Element.DISPLAY);
18499 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18500 v.setStyle('width', '189px');
18504 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18505 if(!this.calendarWeeks){
18510 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18511 v.attr('colspan', function(i, val){
18512 return parseInt(val) + 1;
18517 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18519 this.setStartDate(this.startDate);
18520 this.setEndDate(this.endDate);
18522 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18529 if(this.isInline) {
18534 picker : function()
18536 return this.pickerEl;
18537 // return this.el.select('.datepicker', true).first();
18540 fillDow: function()
18542 var dowCnt = this.weekStart;
18551 if(this.calendarWeeks){
18559 while (dowCnt < this.weekStart + 7) {
18563 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18567 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18570 fillMonths: function()
18573 var months = this.picker().select('>.datepicker-months td', true).first();
18575 months.dom.innerHTML = '';
18581 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18584 months.createChild(month);
18591 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;
18593 if (this.date < this.startDate) {
18594 this.viewDate = new Date(this.startDate);
18595 } else if (this.date > this.endDate) {
18596 this.viewDate = new Date(this.endDate);
18598 this.viewDate = new Date(this.date);
18606 var d = new Date(this.viewDate),
18607 year = d.getUTCFullYear(),
18608 month = d.getUTCMonth(),
18609 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18610 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18611 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18612 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18613 currentDate = this.date && this.date.valueOf(),
18614 today = this.UTCToday();
18616 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18618 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18620 // this.picker.select('>tfoot th.today').
18621 // .text(dates[this.language].today)
18622 // .toggle(this.todayBtn !== false);
18624 this.updateNavArrows();
18627 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18629 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18631 prevMonth.setUTCDate(day);
18633 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18635 var nextMonth = new Date(prevMonth);
18637 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18639 nextMonth = nextMonth.valueOf();
18641 var fillMonths = false;
18643 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18645 while(prevMonth.valueOf() <= nextMonth) {
18648 if (prevMonth.getUTCDay() === this.weekStart) {
18650 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18658 if(this.calendarWeeks){
18659 // ISO 8601: First week contains first thursday.
18660 // ISO also states week starts on Monday, but we can be more abstract here.
18662 // Start of current week: based on weekstart/current date
18663 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18664 // Thursday of this week
18665 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18666 // First Thursday of year, year from thursday
18667 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18668 // Calendar week: ms between thursdays, div ms per day, div 7 days
18669 calWeek = (th - yth) / 864e5 / 7 + 1;
18671 fillMonths.cn.push({
18679 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18681 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18684 if (this.todayHighlight &&
18685 prevMonth.getUTCFullYear() == today.getFullYear() &&
18686 prevMonth.getUTCMonth() == today.getMonth() &&
18687 prevMonth.getUTCDate() == today.getDate()) {
18688 clsName += ' today';
18691 if (currentDate && prevMonth.valueOf() === currentDate) {
18692 clsName += ' active';
18695 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18696 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18697 clsName += ' disabled';
18700 fillMonths.cn.push({
18702 cls: 'day ' + clsName,
18703 html: prevMonth.getDate()
18706 prevMonth.setDate(prevMonth.getDate()+1);
18709 var currentYear = this.date && this.date.getUTCFullYear();
18710 var currentMonth = this.date && this.date.getUTCMonth();
18712 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18714 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18715 v.removeClass('active');
18717 if(currentYear === year && k === currentMonth){
18718 v.addClass('active');
18721 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18722 v.addClass('disabled');
18728 year = parseInt(year/10, 10) * 10;
18730 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18732 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18735 for (var i = -1; i < 11; i++) {
18736 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18738 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18746 showMode: function(dir)
18749 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18752 Roo.each(this.picker().select('>div',true).elements, function(v){
18753 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18756 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18761 if(this.isInline) {
18765 this.picker().removeClass(['bottom', 'top']);
18767 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18769 * place to the top of element!
18773 this.picker().addClass('top');
18774 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18779 this.picker().addClass('bottom');
18781 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18784 parseDate : function(value)
18786 if(!value || value instanceof Date){
18789 var v = Date.parseDate(value, this.format);
18790 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18791 v = Date.parseDate(value, 'Y-m-d');
18793 if(!v && this.altFormats){
18794 if(!this.altFormatsArray){
18795 this.altFormatsArray = this.altFormats.split("|");
18797 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18798 v = Date.parseDate(value, this.altFormatsArray[i]);
18804 formatDate : function(date, fmt)
18806 return (!date || !(date instanceof Date)) ?
18807 date : date.dateFormat(fmt || this.format);
18810 onFocus : function()
18812 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18816 onBlur : function()
18818 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18820 var d = this.inputEl().getValue();
18829 this.picker().show();
18833 this.fireEvent('show', this, this.date);
18838 if(this.isInline) {
18841 this.picker().hide();
18842 this.viewMode = this.startViewMode;
18845 this.fireEvent('hide', this, this.date);
18849 onMousedown: function(e)
18851 e.stopPropagation();
18852 e.preventDefault();
18857 Roo.bootstrap.DateField.superclass.keyup.call(this);
18861 setValue: function(v)
18863 if(this.fireEvent('beforeselect', this, v) !== false){
18864 var d = new Date(this.parseDate(v) ).clearTime();
18866 if(isNaN(d.getTime())){
18867 this.date = this.viewDate = '';
18868 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18872 v = this.formatDate(d);
18874 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18876 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18880 this.fireEvent('select', this, this.date);
18884 getValue: function()
18886 return this.formatDate(this.date);
18889 fireKey: function(e)
18891 if (!this.picker().isVisible()){
18892 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18898 var dateChanged = false,
18900 newDate, newViewDate;
18905 e.preventDefault();
18909 if (!this.keyboardNavigation) {
18912 dir = e.keyCode == 37 ? -1 : 1;
18915 newDate = this.moveYear(this.date, dir);
18916 newViewDate = this.moveYear(this.viewDate, dir);
18917 } else if (e.shiftKey){
18918 newDate = this.moveMonth(this.date, dir);
18919 newViewDate = this.moveMonth(this.viewDate, dir);
18921 newDate = new Date(this.date);
18922 newDate.setUTCDate(this.date.getUTCDate() + dir);
18923 newViewDate = new Date(this.viewDate);
18924 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18926 if (this.dateWithinRange(newDate)){
18927 this.date = newDate;
18928 this.viewDate = newViewDate;
18929 this.setValue(this.formatDate(this.date));
18931 e.preventDefault();
18932 dateChanged = true;
18937 if (!this.keyboardNavigation) {
18940 dir = e.keyCode == 38 ? -1 : 1;
18942 newDate = this.moveYear(this.date, dir);
18943 newViewDate = this.moveYear(this.viewDate, dir);
18944 } else if (e.shiftKey){
18945 newDate = this.moveMonth(this.date, dir);
18946 newViewDate = this.moveMonth(this.viewDate, dir);
18948 newDate = new Date(this.date);
18949 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18950 newViewDate = new Date(this.viewDate);
18951 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18953 if (this.dateWithinRange(newDate)){
18954 this.date = newDate;
18955 this.viewDate = newViewDate;
18956 this.setValue(this.formatDate(this.date));
18958 e.preventDefault();
18959 dateChanged = true;
18963 this.setValue(this.formatDate(this.date));
18965 e.preventDefault();
18968 this.setValue(this.formatDate(this.date));
18982 onClick: function(e)
18984 e.stopPropagation();
18985 e.preventDefault();
18987 var target = e.getTarget();
18989 if(target.nodeName.toLowerCase() === 'i'){
18990 target = Roo.get(target).dom.parentNode;
18993 var nodeName = target.nodeName;
18994 var className = target.className;
18995 var html = target.innerHTML;
18996 //Roo.log(nodeName);
18998 switch(nodeName.toLowerCase()) {
19000 switch(className) {
19006 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19007 switch(this.viewMode){
19009 this.viewDate = this.moveMonth(this.viewDate, dir);
19013 this.viewDate = this.moveYear(this.viewDate, dir);
19019 var date = new Date();
19020 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19022 this.setValue(this.formatDate(this.date));
19029 if (className.indexOf('disabled') < 0) {
19030 this.viewDate.setUTCDate(1);
19031 if (className.indexOf('month') > -1) {
19032 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19034 var year = parseInt(html, 10) || 0;
19035 this.viewDate.setUTCFullYear(year);
19039 if(this.singleMode){
19040 this.setValue(this.formatDate(this.viewDate));
19051 //Roo.log(className);
19052 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19053 var day = parseInt(html, 10) || 1;
19054 var year = this.viewDate.getUTCFullYear(),
19055 month = this.viewDate.getUTCMonth();
19057 if (className.indexOf('old') > -1) {
19064 } else if (className.indexOf('new') > -1) {
19072 //Roo.log([year,month,day]);
19073 this.date = this.UTCDate(year, month, day,0,0,0,0);
19074 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19076 //Roo.log(this.formatDate(this.date));
19077 this.setValue(this.formatDate(this.date));
19084 setStartDate: function(startDate)
19086 this.startDate = startDate || -Infinity;
19087 if (this.startDate !== -Infinity) {
19088 this.startDate = this.parseDate(this.startDate);
19091 this.updateNavArrows();
19094 setEndDate: function(endDate)
19096 this.endDate = endDate || Infinity;
19097 if (this.endDate !== Infinity) {
19098 this.endDate = this.parseDate(this.endDate);
19101 this.updateNavArrows();
19104 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19106 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19107 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19108 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19110 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19111 return parseInt(d, 10);
19114 this.updateNavArrows();
19117 updateNavArrows: function()
19119 if(this.singleMode){
19123 var d = new Date(this.viewDate),
19124 year = d.getUTCFullYear(),
19125 month = d.getUTCMonth();
19127 Roo.each(this.picker().select('.prev', true).elements, function(v){
19129 switch (this.viewMode) {
19132 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19138 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19145 Roo.each(this.picker().select('.next', true).elements, function(v){
19147 switch (this.viewMode) {
19150 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19156 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19164 moveMonth: function(date, dir)
19169 var new_date = new Date(date.valueOf()),
19170 day = new_date.getUTCDate(),
19171 month = new_date.getUTCMonth(),
19172 mag = Math.abs(dir),
19174 dir = dir > 0 ? 1 : -1;
19177 // If going back one month, make sure month is not current month
19178 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19180 return new_date.getUTCMonth() == month;
19182 // If going forward one month, make sure month is as expected
19183 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19185 return new_date.getUTCMonth() != new_month;
19187 new_month = month + dir;
19188 new_date.setUTCMonth(new_month);
19189 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19190 if (new_month < 0 || new_month > 11) {
19191 new_month = (new_month + 12) % 12;
19194 // For magnitudes >1, move one month at a time...
19195 for (var i=0; i<mag; i++) {
19196 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19197 new_date = this.moveMonth(new_date, dir);
19199 // ...then reset the day, keeping it in the new month
19200 new_month = new_date.getUTCMonth();
19201 new_date.setUTCDate(day);
19203 return new_month != new_date.getUTCMonth();
19206 // Common date-resetting loop -- if date is beyond end of month, make it
19209 new_date.setUTCDate(--day);
19210 new_date.setUTCMonth(new_month);
19215 moveYear: function(date, dir)
19217 return this.moveMonth(date, dir*12);
19220 dateWithinRange: function(date)
19222 return date >= this.startDate && date <= this.endDate;
19228 this.picker().remove();
19231 validateValue : function(value)
19233 if(this.getVisibilityEl().hasClass('hidden')){
19237 if(value.length < 1) {
19238 if(this.allowBlank){
19244 if(value.length < this.minLength){
19247 if(value.length > this.maxLength){
19251 var vt = Roo.form.VTypes;
19252 if(!vt[this.vtype](value, this)){
19256 if(typeof this.validator == "function"){
19257 var msg = this.validator(value);
19263 if(this.regex && !this.regex.test(value)){
19267 if(typeof(this.parseDate(value)) == 'undefined'){
19271 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19275 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19283 setVisible : function(visible)
19289 this.getEl().removeClass('hidden');
19295 this.getEl().addClass('hidden');
19300 Roo.apply(Roo.bootstrap.DateField, {
19311 html: '<i class="fa fa-arrow-left"/>'
19321 html: '<i class="fa fa-arrow-right"/>'
19363 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19364 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19365 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19366 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19367 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19380 navFnc: 'FullYear',
19385 navFnc: 'FullYear',
19390 Roo.apply(Roo.bootstrap.DateField, {
19394 cls: 'datepicker dropdown-menu roo-dynamic',
19398 cls: 'datepicker-days',
19402 cls: 'table-condensed',
19404 Roo.bootstrap.DateField.head,
19408 Roo.bootstrap.DateField.footer
19415 cls: 'datepicker-months',
19419 cls: 'table-condensed',
19421 Roo.bootstrap.DateField.head,
19422 Roo.bootstrap.DateField.content,
19423 Roo.bootstrap.DateField.footer
19430 cls: 'datepicker-years',
19434 cls: 'table-condensed',
19436 Roo.bootstrap.DateField.head,
19437 Roo.bootstrap.DateField.content,
19438 Roo.bootstrap.DateField.footer
19457 * @class Roo.bootstrap.TimeField
19458 * @extends Roo.bootstrap.Input
19459 * Bootstrap DateField class
19463 * Create a new TimeField
19464 * @param {Object} config The config object
19467 Roo.bootstrap.TimeField = function(config){
19468 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19472 * Fires when this field show.
19473 * @param {Roo.bootstrap.DateField} thisthis
19474 * @param {Mixed} date The date value
19479 * Fires when this field hide.
19480 * @param {Roo.bootstrap.DateField} this
19481 * @param {Mixed} date The date value
19486 * Fires when select a date.
19487 * @param {Roo.bootstrap.DateField} this
19488 * @param {Mixed} date The date value
19494 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19497 * @cfg {String} format
19498 * The default time format string which can be overriden for localization support. The format must be
19499 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19503 onRender: function(ct, position)
19506 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19508 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19510 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19512 this.pop = this.picker().select('>.datepicker-time',true).first();
19513 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19515 this.picker().on('mousedown', this.onMousedown, this);
19516 this.picker().on('click', this.onClick, this);
19518 this.picker().addClass('datepicker-dropdown');
19523 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19524 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19525 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19526 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19527 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19528 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19532 fireKey: function(e){
19533 if (!this.picker().isVisible()){
19534 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19540 e.preventDefault();
19548 this.onTogglePeriod();
19551 this.onIncrementMinutes();
19554 this.onDecrementMinutes();
19563 onClick: function(e) {
19564 e.stopPropagation();
19565 e.preventDefault();
19568 picker : function()
19570 return this.el.select('.datepicker', true).first();
19573 fillTime: function()
19575 var time = this.pop.select('tbody', true).first();
19577 time.dom.innerHTML = '';
19592 cls: 'hours-up glyphicon glyphicon-chevron-up'
19612 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19633 cls: 'timepicker-hour',
19648 cls: 'timepicker-minute',
19663 cls: 'btn btn-primary period',
19685 cls: 'hours-down glyphicon glyphicon-chevron-down'
19705 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19723 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19730 var hours = this.time.getHours();
19731 var minutes = this.time.getMinutes();
19744 hours = hours - 12;
19748 hours = '0' + hours;
19752 minutes = '0' + minutes;
19755 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19756 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19757 this.pop.select('button', true).first().dom.innerHTML = period;
19763 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19765 var cls = ['bottom'];
19767 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19774 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19779 this.picker().addClass(cls.join('-'));
19783 Roo.each(cls, function(c){
19785 _this.picker().setTop(_this.inputEl().getHeight());
19789 _this.picker().setTop(0 - _this.picker().getHeight());
19794 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19798 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19805 onFocus : function()
19807 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19811 onBlur : function()
19813 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19819 this.picker().show();
19824 this.fireEvent('show', this, this.date);
19829 this.picker().hide();
19832 this.fireEvent('hide', this, this.date);
19835 setTime : function()
19838 this.setValue(this.time.format(this.format));
19840 this.fireEvent('select', this, this.date);
19845 onMousedown: function(e){
19846 e.stopPropagation();
19847 e.preventDefault();
19850 onIncrementHours: function()
19852 Roo.log('onIncrementHours');
19853 this.time = this.time.add(Date.HOUR, 1);
19858 onDecrementHours: function()
19860 Roo.log('onDecrementHours');
19861 this.time = this.time.add(Date.HOUR, -1);
19865 onIncrementMinutes: function()
19867 Roo.log('onIncrementMinutes');
19868 this.time = this.time.add(Date.MINUTE, 1);
19872 onDecrementMinutes: function()
19874 Roo.log('onDecrementMinutes');
19875 this.time = this.time.add(Date.MINUTE, -1);
19879 onTogglePeriod: function()
19881 Roo.log('onTogglePeriod');
19882 this.time = this.time.add(Date.HOUR, 12);
19889 Roo.apply(Roo.bootstrap.TimeField, {
19919 cls: 'btn btn-info ok',
19931 Roo.apply(Roo.bootstrap.TimeField, {
19935 cls: 'datepicker dropdown-menu',
19939 cls: 'datepicker-time',
19943 cls: 'table-condensed',
19945 Roo.bootstrap.TimeField.content,
19946 Roo.bootstrap.TimeField.footer
19965 * @class Roo.bootstrap.MonthField
19966 * @extends Roo.bootstrap.Input
19967 * Bootstrap MonthField class
19969 * @cfg {String} language default en
19972 * Create a new MonthField
19973 * @param {Object} config The config object
19976 Roo.bootstrap.MonthField = function(config){
19977 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19982 * Fires when this field show.
19983 * @param {Roo.bootstrap.MonthField} this
19984 * @param {Mixed} date The date value
19989 * Fires when this field hide.
19990 * @param {Roo.bootstrap.MonthField} this
19991 * @param {Mixed} date The date value
19996 * Fires when select a date.
19997 * @param {Roo.bootstrap.MonthField} this
19998 * @param {String} oldvalue The old value
19999 * @param {String} newvalue The new value
20005 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20007 onRender: function(ct, position)
20010 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20012 this.language = this.language || 'en';
20013 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20014 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20016 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20017 this.isInline = false;
20018 this.isInput = true;
20019 this.component = this.el.select('.add-on', true).first() || false;
20020 this.component = (this.component && this.component.length === 0) ? false : this.component;
20021 this.hasInput = this.component && this.inputEL().length;
20023 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20025 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20027 this.picker().on('mousedown', this.onMousedown, this);
20028 this.picker().on('click', this.onClick, this);
20030 this.picker().addClass('datepicker-dropdown');
20032 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20033 v.setStyle('width', '189px');
20040 if(this.isInline) {
20046 setValue: function(v, suppressEvent)
20048 var o = this.getValue();
20050 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20054 if(suppressEvent !== true){
20055 this.fireEvent('select', this, o, v);
20060 getValue: function()
20065 onClick: function(e)
20067 e.stopPropagation();
20068 e.preventDefault();
20070 var target = e.getTarget();
20072 if(target.nodeName.toLowerCase() === 'i'){
20073 target = Roo.get(target).dom.parentNode;
20076 var nodeName = target.nodeName;
20077 var className = target.className;
20078 var html = target.innerHTML;
20080 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20084 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20086 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20092 picker : function()
20094 return this.pickerEl;
20097 fillMonths: function()
20100 var months = this.picker().select('>.datepicker-months td', true).first();
20102 months.dom.innerHTML = '';
20108 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20111 months.createChild(month);
20120 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20121 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20124 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20125 e.removeClass('active');
20127 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20128 e.addClass('active');
20135 if(this.isInline) {
20139 this.picker().removeClass(['bottom', 'top']);
20141 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20143 * place to the top of element!
20147 this.picker().addClass('top');
20148 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20153 this.picker().addClass('bottom');
20155 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20158 onFocus : function()
20160 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20164 onBlur : function()
20166 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20168 var d = this.inputEl().getValue();
20177 this.picker().show();
20178 this.picker().select('>.datepicker-months', true).first().show();
20182 this.fireEvent('show', this, this.date);
20187 if(this.isInline) {
20190 this.picker().hide();
20191 this.fireEvent('hide', this, this.date);
20195 onMousedown: function(e)
20197 e.stopPropagation();
20198 e.preventDefault();
20203 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20207 fireKey: function(e)
20209 if (!this.picker().isVisible()){
20210 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20221 e.preventDefault();
20225 dir = e.keyCode == 37 ? -1 : 1;
20227 this.vIndex = this.vIndex + dir;
20229 if(this.vIndex < 0){
20233 if(this.vIndex > 11){
20237 if(isNaN(this.vIndex)){
20241 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20247 dir = e.keyCode == 38 ? -1 : 1;
20249 this.vIndex = this.vIndex + dir * 4;
20251 if(this.vIndex < 0){
20255 if(this.vIndex > 11){
20259 if(isNaN(this.vIndex)){
20263 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20268 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20269 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20273 e.preventDefault();
20276 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20277 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20293 this.picker().remove();
20298 Roo.apply(Roo.bootstrap.MonthField, {
20317 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20318 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20323 Roo.apply(Roo.bootstrap.MonthField, {
20327 cls: 'datepicker dropdown-menu roo-dynamic',
20331 cls: 'datepicker-months',
20335 cls: 'table-condensed',
20337 Roo.bootstrap.DateField.content
20357 * @class Roo.bootstrap.CheckBox
20358 * @extends Roo.bootstrap.Input
20359 * Bootstrap CheckBox class
20361 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20362 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20363 * @cfg {String} boxLabel The text that appears beside the checkbox
20364 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20365 * @cfg {Boolean} checked initnal the element
20366 * @cfg {Boolean} inline inline the element (default false)
20367 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20368 * @cfg {String} tooltip label tooltip
20371 * Create a new CheckBox
20372 * @param {Object} config The config object
20375 Roo.bootstrap.CheckBox = function(config){
20376 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20381 * Fires when the element is checked or unchecked.
20382 * @param {Roo.bootstrap.CheckBox} this This input
20383 * @param {Boolean} checked The new checked value
20388 * Fires when the element is click.
20389 * @param {Roo.bootstrap.CheckBox} this This input
20396 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20398 inputType: 'checkbox',
20407 getAutoCreate : function()
20409 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20415 cfg.cls = 'form-group ' + this.inputType; //input-group
20418 cfg.cls += ' ' + this.inputType + '-inline';
20424 type : this.inputType,
20425 value : this.inputValue,
20426 cls : 'roo-' + this.inputType, //'form-box',
20427 placeholder : this.placeholder || ''
20431 if(this.inputType != 'radio'){
20435 cls : 'roo-hidden-value',
20436 value : this.checked ? this.inputValue : this.valueOff
20441 if (this.weight) { // Validity check?
20442 cfg.cls += " " + this.inputType + "-" + this.weight;
20445 if (this.disabled) {
20446 input.disabled=true;
20450 input.checked = this.checked;
20455 input.name = this.name;
20457 if(this.inputType != 'radio'){
20458 hidden.name = this.name;
20459 input.name = '_hidden_' + this.name;
20464 input.cls += ' input-' + this.size;
20469 ['xs','sm','md','lg'].map(function(size){
20470 if (settings[size]) {
20471 cfg.cls += ' col-' + size + '-' + settings[size];
20475 var inputblock = input;
20477 if (this.before || this.after) {
20480 cls : 'input-group',
20485 inputblock.cn.push({
20487 cls : 'input-group-addon',
20492 inputblock.cn.push(input);
20494 if(this.inputType != 'radio'){
20495 inputblock.cn.push(hidden);
20499 inputblock.cn.push({
20501 cls : 'input-group-addon',
20508 if (align ==='left' && this.fieldLabel.length) {
20509 // Roo.log("left and has label");
20514 cls : 'control-label',
20515 html : this.fieldLabel
20525 if(this.labelWidth > 12){
20526 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20529 if(this.labelWidth < 13 && this.labelmd == 0){
20530 this.labelmd = this.labelWidth;
20533 if(this.labellg > 0){
20534 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20535 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20538 if(this.labelmd > 0){
20539 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20540 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20543 if(this.labelsm > 0){
20544 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20545 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20548 if(this.labelxs > 0){
20549 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20550 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20553 } else if ( this.fieldLabel.length) {
20554 // Roo.log(" label");
20558 tag: this.boxLabel ? 'span' : 'label',
20560 cls: 'control-label box-input-label',
20561 //cls : 'input-group-addon',
20562 html : this.fieldLabel
20571 // Roo.log(" no label && no align");
20572 cfg.cn = [ inputblock ] ;
20578 var boxLabelCfg = {
20580 //'for': id, // box label is handled by onclick - so no for...
20582 html: this.boxLabel
20586 boxLabelCfg.tooltip = this.tooltip;
20589 cfg.cn.push(boxLabelCfg);
20592 if(this.inputType != 'radio'){
20593 cfg.cn.push(hidden);
20601 * return the real input element.
20603 inputEl: function ()
20605 return this.el.select('input.roo-' + this.inputType,true).first();
20607 hiddenEl: function ()
20609 return this.el.select('input.roo-hidden-value',true).first();
20612 labelEl: function()
20614 return this.el.select('label.control-label',true).first();
20616 /* depricated... */
20620 return this.labelEl();
20623 boxLabelEl: function()
20625 return this.el.select('label.box-label',true).first();
20628 initEvents : function()
20630 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20632 this.inputEl().on('click', this.onClick, this);
20634 if (this.boxLabel) {
20635 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20638 this.startValue = this.getValue();
20641 Roo.bootstrap.CheckBox.register(this);
20645 onClick : function(e)
20647 if(this.fireEvent('click', this, e) !== false){
20648 this.setChecked(!this.checked);
20653 setChecked : function(state,suppressEvent)
20655 this.startValue = this.getValue();
20657 if(this.inputType == 'radio'){
20659 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20660 e.dom.checked = false;
20663 this.inputEl().dom.checked = true;
20665 this.inputEl().dom.value = this.inputValue;
20667 if(suppressEvent !== true){
20668 this.fireEvent('check', this, true);
20676 this.checked = state;
20678 this.inputEl().dom.checked = state;
20681 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20683 if(suppressEvent !== true){
20684 this.fireEvent('check', this, state);
20690 getValue : function()
20692 if(this.inputType == 'radio'){
20693 return this.getGroupValue();
20696 return this.hiddenEl().dom.value;
20700 getGroupValue : function()
20702 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20706 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20709 setValue : function(v,suppressEvent)
20711 if(this.inputType == 'radio'){
20712 this.setGroupValue(v, suppressEvent);
20716 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20721 setGroupValue : function(v, suppressEvent)
20723 this.startValue = this.getValue();
20725 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20726 e.dom.checked = false;
20728 if(e.dom.value == v){
20729 e.dom.checked = true;
20733 if(suppressEvent !== true){
20734 this.fireEvent('check', this, true);
20742 validate : function()
20744 if(this.getVisibilityEl().hasClass('hidden')){
20750 (this.inputType == 'radio' && this.validateRadio()) ||
20751 (this.inputType == 'checkbox' && this.validateCheckbox())
20757 this.markInvalid();
20761 validateRadio : function()
20763 if(this.getVisibilityEl().hasClass('hidden')){
20767 if(this.allowBlank){
20773 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20774 if(!e.dom.checked){
20786 validateCheckbox : function()
20789 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20790 //return (this.getValue() == this.inputValue) ? true : false;
20793 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20801 for(var i in group){
20802 if(group[i].el.isVisible(true)){
20810 for(var i in group){
20815 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20822 * Mark this field as valid
20824 markValid : function()
20828 this.fireEvent('valid', this);
20830 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20833 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20840 if(this.inputType == 'radio'){
20841 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20842 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20843 e.findParent('.form-group', false, true).addClass(_this.validClass);
20850 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20851 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20855 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20861 for(var i in group){
20862 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20863 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20868 * Mark this field as invalid
20869 * @param {String} msg The validation message
20871 markInvalid : function(msg)
20873 if(this.allowBlank){
20879 this.fireEvent('invalid', this, msg);
20881 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20884 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20888 label.markInvalid();
20891 if(this.inputType == 'radio'){
20892 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20893 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20894 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20901 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20902 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20906 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20912 for(var i in group){
20913 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20914 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20919 clearInvalid : function()
20921 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20923 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20925 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20927 if (label && label.iconEl) {
20928 label.iconEl.removeClass(label.validClass);
20929 label.iconEl.removeClass(label.invalidClass);
20933 disable : function()
20935 if(this.inputType != 'radio'){
20936 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20943 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20944 _this.getActionEl().addClass(this.disabledClass);
20945 e.dom.disabled = true;
20949 this.disabled = true;
20950 this.fireEvent("disable", this);
20954 enable : function()
20956 if(this.inputType != 'radio'){
20957 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20964 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20965 _this.getActionEl().removeClass(this.disabledClass);
20966 e.dom.disabled = false;
20970 this.disabled = false;
20971 this.fireEvent("enable", this);
20975 setBoxLabel : function(v)
20980 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20986 Roo.apply(Roo.bootstrap.CheckBox, {
20991 * register a CheckBox Group
20992 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20994 register : function(checkbox)
20996 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20997 this.groups[checkbox.groupId] = {};
21000 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21004 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21008 * fetch a CheckBox Group based on the group ID
21009 * @param {string} the group ID
21010 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21012 get: function(groupId) {
21013 if (typeof(this.groups[groupId]) == 'undefined') {
21017 return this.groups[groupId] ;
21030 * @class Roo.bootstrap.Radio
21031 * @extends Roo.bootstrap.Component
21032 * Bootstrap Radio class
21033 * @cfg {String} boxLabel - the label associated
21034 * @cfg {String} value - the value of radio
21037 * Create a new Radio
21038 * @param {Object} config The config object
21040 Roo.bootstrap.Radio = function(config){
21041 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21045 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21051 getAutoCreate : function()
21055 cls : 'form-group radio',
21060 html : this.boxLabel
21068 initEvents : function()
21070 this.parent().register(this);
21072 this.el.on('click', this.onClick, this);
21076 onClick : function(e)
21078 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21079 this.setChecked(true);
21083 setChecked : function(state, suppressEvent)
21085 this.parent().setValue(this.value, suppressEvent);
21089 setBoxLabel : function(v)
21094 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21109 * @class Roo.bootstrap.SecurePass
21110 * @extends Roo.bootstrap.Input
21111 * Bootstrap SecurePass class
21115 * Create a new SecurePass
21116 * @param {Object} config The config object
21119 Roo.bootstrap.SecurePass = function (config) {
21120 // these go here, so the translation tool can replace them..
21122 PwdEmpty: "Please type a password, and then retype it to confirm.",
21123 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21124 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21125 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21126 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21127 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21128 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21129 TooWeak: "Your password is Too Weak."
21131 this.meterLabel = "Password strength:";
21132 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21133 this.meterClass = [
21134 "roo-password-meter-tooweak",
21135 "roo-password-meter-weak",
21136 "roo-password-meter-medium",
21137 "roo-password-meter-strong",
21138 "roo-password-meter-grey"
21143 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21146 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21148 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21150 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21151 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21152 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21153 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21154 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21155 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21156 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21166 * @cfg {String/Object} Label for the strength meter (defaults to
21167 * 'Password strength:')
21172 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21173 * ['Weak', 'Medium', 'Strong'])
21176 pwdStrengths: false,
21189 initEvents: function ()
21191 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21193 if (this.el.is('input[type=password]') && Roo.isSafari) {
21194 this.el.on('keydown', this.SafariOnKeyDown, this);
21197 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21200 onRender: function (ct, position)
21202 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21203 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21204 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21206 this.trigger.createChild({
21211 cls: 'roo-password-meter-grey col-xs-12',
21214 //width: this.meterWidth + 'px'
21218 cls: 'roo-password-meter-text'
21224 if (this.hideTrigger) {
21225 this.trigger.setDisplayed(false);
21227 this.setSize(this.width || '', this.height || '');
21230 onDestroy: function ()
21232 if (this.trigger) {
21233 this.trigger.removeAllListeners();
21234 this.trigger.remove();
21237 this.wrap.remove();
21239 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21242 checkStrength: function ()
21244 var pwd = this.inputEl().getValue();
21245 if (pwd == this._lastPwd) {
21250 if (this.ClientSideStrongPassword(pwd)) {
21252 } else if (this.ClientSideMediumPassword(pwd)) {
21254 } else if (this.ClientSideWeakPassword(pwd)) {
21260 Roo.log('strength1: ' + strength);
21262 //var pm = this.trigger.child('div/div/div').dom;
21263 var pm = this.trigger.child('div/div');
21264 pm.removeClass(this.meterClass);
21265 pm.addClass(this.meterClass[strength]);
21268 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21270 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21272 this._lastPwd = pwd;
21276 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21278 this._lastPwd = '';
21280 var pm = this.trigger.child('div/div');
21281 pm.removeClass(this.meterClass);
21282 pm.addClass('roo-password-meter-grey');
21285 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21288 this.inputEl().dom.type='password';
21291 validateValue: function (value)
21294 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21297 if (value.length == 0) {
21298 if (this.allowBlank) {
21299 this.clearInvalid();
21303 this.markInvalid(this.errors.PwdEmpty);
21304 this.errorMsg = this.errors.PwdEmpty;
21312 if ('[\x21-\x7e]*'.match(value)) {
21313 this.markInvalid(this.errors.PwdBadChar);
21314 this.errorMsg = this.errors.PwdBadChar;
21317 if (value.length < 6) {
21318 this.markInvalid(this.errors.PwdShort);
21319 this.errorMsg = this.errors.PwdShort;
21322 if (value.length > 16) {
21323 this.markInvalid(this.errors.PwdLong);
21324 this.errorMsg = this.errors.PwdLong;
21328 if (this.ClientSideStrongPassword(value)) {
21330 } else if (this.ClientSideMediumPassword(value)) {
21332 } else if (this.ClientSideWeakPassword(value)) {
21339 if (strength < 2) {
21340 //this.markInvalid(this.errors.TooWeak);
21341 this.errorMsg = this.errors.TooWeak;
21346 console.log('strength2: ' + strength);
21348 //var pm = this.trigger.child('div/div/div').dom;
21350 var pm = this.trigger.child('div/div');
21351 pm.removeClass(this.meterClass);
21352 pm.addClass(this.meterClass[strength]);
21354 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21356 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21358 this.errorMsg = '';
21362 CharacterSetChecks: function (type)
21365 this.fResult = false;
21368 isctype: function (character, type)
21371 case this.kCapitalLetter:
21372 if (character >= 'A' && character <= 'Z') {
21377 case this.kSmallLetter:
21378 if (character >= 'a' && character <= 'z') {
21384 if (character >= '0' && character <= '9') {
21389 case this.kPunctuation:
21390 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21401 IsLongEnough: function (pwd, size)
21403 return !(pwd == null || isNaN(size) || pwd.length < size);
21406 SpansEnoughCharacterSets: function (word, nb)
21408 if (!this.IsLongEnough(word, nb))
21413 var characterSetChecks = new Array(
21414 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21415 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21418 for (var index = 0; index < word.length; ++index) {
21419 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21420 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21421 characterSetChecks[nCharSet].fResult = true;
21428 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21429 if (characterSetChecks[nCharSet].fResult) {
21434 if (nCharSets < nb) {
21440 ClientSideStrongPassword: function (pwd)
21442 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21445 ClientSideMediumPassword: function (pwd)
21447 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21450 ClientSideWeakPassword: function (pwd)
21452 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21455 })//<script type="text/javascript">
21458 * Based Ext JS Library 1.1.1
21459 * Copyright(c) 2006-2007, Ext JS, LLC.
21465 * @class Roo.HtmlEditorCore
21466 * @extends Roo.Component
21467 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21469 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21472 Roo.HtmlEditorCore = function(config){
21475 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21480 * @event initialize
21481 * Fires when the editor is fully initialized (including the iframe)
21482 * @param {Roo.HtmlEditorCore} this
21487 * Fires when the editor is first receives the focus. Any insertion must wait
21488 * until after this event.
21489 * @param {Roo.HtmlEditorCore} this
21493 * @event beforesync
21494 * Fires before the textarea is updated with content from the editor iframe. Return false
21495 * to cancel the sync.
21496 * @param {Roo.HtmlEditorCore} this
21497 * @param {String} html
21501 * @event beforepush
21502 * Fires before the iframe editor is updated with content from the textarea. Return false
21503 * to cancel the push.
21504 * @param {Roo.HtmlEditorCore} this
21505 * @param {String} html
21510 * Fires when the textarea is updated with content from the editor iframe.
21511 * @param {Roo.HtmlEditorCore} this
21512 * @param {String} html
21517 * Fires when the iframe editor is updated with content from the textarea.
21518 * @param {Roo.HtmlEditorCore} this
21519 * @param {String} html
21524 * @event editorevent
21525 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21526 * @param {Roo.HtmlEditorCore} this
21532 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21534 // defaults : white / black...
21535 this.applyBlacklists();
21542 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21546 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21552 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21557 * @cfg {Number} height (in pixels)
21561 * @cfg {Number} width (in pixels)
21566 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21569 stylesheets: false,
21574 // private properties
21575 validationEvent : false,
21577 initialized : false,
21579 sourceEditMode : false,
21580 onFocus : Roo.emptyFn,
21582 hideMode:'offsets',
21586 // blacklist + whitelisted elements..
21593 * Protected method that will not generally be called directly. It
21594 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21595 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21597 getDocMarkup : function(){
21601 // inherit styels from page...??
21602 if (this.stylesheets === false) {
21604 Roo.get(document.head).select('style').each(function(node) {
21605 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21608 Roo.get(document.head).select('link').each(function(node) {
21609 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21612 } else if (!this.stylesheets.length) {
21614 st = '<style type="text/css">' +
21615 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21618 st = '<style type="text/css">' +
21623 st += '<style type="text/css">' +
21624 'IMG { cursor: pointer } ' +
21627 var cls = 'roo-htmleditor-body';
21629 if(this.bodyCls.length){
21630 cls += ' ' + this.bodyCls;
21633 return '<html><head>' + st +
21634 //<style type="text/css">' +
21635 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21637 ' </head><body class="' + cls + '"></body></html>';
21641 onRender : function(ct, position)
21644 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21645 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21648 this.el.dom.style.border = '0 none';
21649 this.el.dom.setAttribute('tabIndex', -1);
21650 this.el.addClass('x-hidden hide');
21654 if(Roo.isIE){ // fix IE 1px bogus margin
21655 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21659 this.frameId = Roo.id();
21663 var iframe = this.owner.wrap.createChild({
21665 cls: 'form-control', // bootstrap..
21667 name: this.frameId,
21668 frameBorder : 'no',
21669 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21674 this.iframe = iframe.dom;
21676 this.assignDocWin();
21678 this.doc.designMode = 'on';
21681 this.doc.write(this.getDocMarkup());
21685 var task = { // must defer to wait for browser to be ready
21687 //console.log("run task?" + this.doc.readyState);
21688 this.assignDocWin();
21689 if(this.doc.body || this.doc.readyState == 'complete'){
21691 this.doc.designMode="on";
21695 Roo.TaskMgr.stop(task);
21696 this.initEditor.defer(10, this);
21703 Roo.TaskMgr.start(task);
21708 onResize : function(w, h)
21710 Roo.log('resize: ' +w + ',' + h );
21711 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21715 if(typeof w == 'number'){
21717 this.iframe.style.width = w + 'px';
21719 if(typeof h == 'number'){
21721 this.iframe.style.height = h + 'px';
21723 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21730 * Toggles the editor between standard and source edit mode.
21731 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21733 toggleSourceEdit : function(sourceEditMode){
21735 this.sourceEditMode = sourceEditMode === true;
21737 if(this.sourceEditMode){
21739 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21742 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21743 //this.iframe.className = '';
21746 //this.setSize(this.owner.wrap.getSize());
21747 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21754 * Protected method that will not generally be called directly. If you need/want
21755 * custom HTML cleanup, this is the method you should override.
21756 * @param {String} html The HTML to be cleaned
21757 * return {String} The cleaned HTML
21759 cleanHtml : function(html){
21760 html = String(html);
21761 if(html.length > 5){
21762 if(Roo.isSafari){ // strip safari nonsense
21763 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21766 if(html == ' '){
21773 * HTML Editor -> Textarea
21774 * Protected method that will not generally be called directly. Syncs the contents
21775 * of the editor iframe with the textarea.
21777 syncValue : function(){
21778 if(this.initialized){
21779 var bd = (this.doc.body || this.doc.documentElement);
21780 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21781 var html = bd.innerHTML;
21783 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21784 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21786 html = '<div style="'+m[0]+'">' + html + '</div>';
21789 html = this.cleanHtml(html);
21790 // fix up the special chars.. normaly like back quotes in word...
21791 // however we do not want to do this with chinese..
21792 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21793 var cc = b.charCodeAt();
21795 (cc >= 0x4E00 && cc < 0xA000 ) ||
21796 (cc >= 0x3400 && cc < 0x4E00 ) ||
21797 (cc >= 0xf900 && cc < 0xfb00 )
21803 if(this.owner.fireEvent('beforesync', this, html) !== false){
21804 this.el.dom.value = html;
21805 this.owner.fireEvent('sync', this, html);
21811 * Protected method that will not generally be called directly. Pushes the value of the textarea
21812 * into the iframe editor.
21814 pushValue : function(){
21815 if(this.initialized){
21816 var v = this.el.dom.value.trim();
21818 // if(v.length < 1){
21822 if(this.owner.fireEvent('beforepush', this, v) !== false){
21823 var d = (this.doc.body || this.doc.documentElement);
21825 this.cleanUpPaste();
21826 this.el.dom.value = d.innerHTML;
21827 this.owner.fireEvent('push', this, v);
21833 deferFocus : function(){
21834 this.focus.defer(10, this);
21838 focus : function(){
21839 if(this.win && !this.sourceEditMode){
21846 assignDocWin: function()
21848 var iframe = this.iframe;
21851 this.doc = iframe.contentWindow.document;
21852 this.win = iframe.contentWindow;
21854 // if (!Roo.get(this.frameId)) {
21857 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21858 // this.win = Roo.get(this.frameId).dom.contentWindow;
21860 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21864 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21865 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21870 initEditor : function(){
21871 //console.log("INIT EDITOR");
21872 this.assignDocWin();
21876 this.doc.designMode="on";
21878 this.doc.write(this.getDocMarkup());
21881 var dbody = (this.doc.body || this.doc.documentElement);
21882 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21883 // this copies styles from the containing element into thsi one..
21884 // not sure why we need all of this..
21885 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21887 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21888 //ss['background-attachment'] = 'fixed'; // w3c
21889 dbody.bgProperties = 'fixed'; // ie
21890 //Roo.DomHelper.applyStyles(dbody, ss);
21891 Roo.EventManager.on(this.doc, {
21892 //'mousedown': this.onEditorEvent,
21893 'mouseup': this.onEditorEvent,
21894 'dblclick': this.onEditorEvent,
21895 'click': this.onEditorEvent,
21896 'keyup': this.onEditorEvent,
21901 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21903 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21904 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21906 this.initialized = true;
21908 this.owner.fireEvent('initialize', this);
21913 onDestroy : function(){
21919 //for (var i =0; i < this.toolbars.length;i++) {
21920 // // fixme - ask toolbars for heights?
21921 // this.toolbars[i].onDestroy();
21924 //this.wrap.dom.innerHTML = '';
21925 //this.wrap.remove();
21930 onFirstFocus : function(){
21932 this.assignDocWin();
21935 this.activated = true;
21938 if(Roo.isGecko){ // prevent silly gecko errors
21940 var s = this.win.getSelection();
21941 if(!s.focusNode || s.focusNode.nodeType != 3){
21942 var r = s.getRangeAt(0);
21943 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21948 this.execCmd('useCSS', true);
21949 this.execCmd('styleWithCSS', false);
21952 this.owner.fireEvent('activate', this);
21956 adjustFont: function(btn){
21957 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21958 //if(Roo.isSafari){ // safari
21961 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21962 if(Roo.isSafari){ // safari
21963 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21964 v = (v < 10) ? 10 : v;
21965 v = (v > 48) ? 48 : v;
21966 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21971 v = Math.max(1, v+adjust);
21973 this.execCmd('FontSize', v );
21976 onEditorEvent : function(e)
21978 this.owner.fireEvent('editorevent', this, e);
21979 // this.updateToolbar();
21980 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21983 insertTag : function(tg)
21985 // could be a bit smarter... -> wrap the current selected tRoo..
21986 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21988 range = this.createRange(this.getSelection());
21989 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21990 wrappingNode.appendChild(range.extractContents());
21991 range.insertNode(wrappingNode);
21998 this.execCmd("formatblock", tg);
22002 insertText : function(txt)
22006 var range = this.createRange();
22007 range.deleteContents();
22008 //alert(Sender.getAttribute('label'));
22010 range.insertNode(this.doc.createTextNode(txt));
22016 * Executes a Midas editor command on the editor document and performs necessary focus and
22017 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22018 * @param {String} cmd The Midas command
22019 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22021 relayCmd : function(cmd, value){
22023 this.execCmd(cmd, value);
22024 this.owner.fireEvent('editorevent', this);
22025 //this.updateToolbar();
22026 this.owner.deferFocus();
22030 * Executes a Midas editor command directly on the editor document.
22031 * For visual commands, you should use {@link #relayCmd} instead.
22032 * <b>This should only be called after the editor is initialized.</b>
22033 * @param {String} cmd The Midas command
22034 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22036 execCmd : function(cmd, value){
22037 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22044 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22046 * @param {String} text | dom node..
22048 insertAtCursor : function(text)
22051 if(!this.activated){
22057 var r = this.doc.selection.createRange();
22068 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22072 // from jquery ui (MIT licenced)
22074 var win = this.win;
22076 if (win.getSelection && win.getSelection().getRangeAt) {
22077 range = win.getSelection().getRangeAt(0);
22078 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22079 range.insertNode(node);
22080 } else if (win.document.selection && win.document.selection.createRange) {
22081 // no firefox support
22082 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22083 win.document.selection.createRange().pasteHTML(txt);
22085 // no firefox support
22086 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22087 this.execCmd('InsertHTML', txt);
22096 mozKeyPress : function(e){
22098 var c = e.getCharCode(), cmd;
22101 c = String.fromCharCode(c).toLowerCase();
22115 this.cleanUpPaste.defer(100, this);
22123 e.preventDefault();
22131 fixKeys : function(){ // load time branching for fastest keydown performance
22133 return function(e){
22134 var k = e.getKey(), r;
22137 r = this.doc.selection.createRange();
22140 r.pasteHTML('    ');
22147 r = this.doc.selection.createRange();
22149 var target = r.parentElement();
22150 if(!target || target.tagName.toLowerCase() != 'li'){
22152 r.pasteHTML('<br />');
22158 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22159 this.cleanUpPaste.defer(100, this);
22165 }else if(Roo.isOpera){
22166 return function(e){
22167 var k = e.getKey();
22171 this.execCmd('InsertHTML','    ');
22174 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22175 this.cleanUpPaste.defer(100, this);
22180 }else if(Roo.isSafari){
22181 return function(e){
22182 var k = e.getKey();
22186 this.execCmd('InsertText','\t');
22190 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22191 this.cleanUpPaste.defer(100, this);
22199 getAllAncestors: function()
22201 var p = this.getSelectedNode();
22204 a.push(p); // push blank onto stack..
22205 p = this.getParentElement();
22209 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22213 a.push(this.doc.body);
22217 lastSelNode : false,
22220 getSelection : function()
22222 this.assignDocWin();
22223 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22226 getSelectedNode: function()
22228 // this may only work on Gecko!!!
22230 // should we cache this!!!!
22235 var range = this.createRange(this.getSelection()).cloneRange();
22238 var parent = range.parentElement();
22240 var testRange = range.duplicate();
22241 testRange.moveToElementText(parent);
22242 if (testRange.inRange(range)) {
22245 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22248 parent = parent.parentElement;
22253 // is ancestor a text element.
22254 var ac = range.commonAncestorContainer;
22255 if (ac.nodeType == 3) {
22256 ac = ac.parentNode;
22259 var ar = ac.childNodes;
22262 var other_nodes = [];
22263 var has_other_nodes = false;
22264 for (var i=0;i<ar.length;i++) {
22265 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22268 // fullly contained node.
22270 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22275 // probably selected..
22276 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22277 other_nodes.push(ar[i]);
22281 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22286 has_other_nodes = true;
22288 if (!nodes.length && other_nodes.length) {
22289 nodes= other_nodes;
22291 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22297 createRange: function(sel)
22299 // this has strange effects when using with
22300 // top toolbar - not sure if it's a great idea.
22301 //this.editor.contentWindow.focus();
22302 if (typeof sel != "undefined") {
22304 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22306 return this.doc.createRange();
22309 return this.doc.createRange();
22312 getParentElement: function()
22315 this.assignDocWin();
22316 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22318 var range = this.createRange(sel);
22321 var p = range.commonAncestorContainer;
22322 while (p.nodeType == 3) { // text node
22333 * Range intersection.. the hard stuff...
22337 * [ -- selected range --- ]
22341 * if end is before start or hits it. fail.
22342 * if start is after end or hits it fail.
22344 * if either hits (but other is outside. - then it's not
22350 // @see http://www.thismuchiknow.co.uk/?p=64.
22351 rangeIntersectsNode : function(range, node)
22353 var nodeRange = node.ownerDocument.createRange();
22355 nodeRange.selectNode(node);
22357 nodeRange.selectNodeContents(node);
22360 var rangeStartRange = range.cloneRange();
22361 rangeStartRange.collapse(true);
22363 var rangeEndRange = range.cloneRange();
22364 rangeEndRange.collapse(false);
22366 var nodeStartRange = nodeRange.cloneRange();
22367 nodeStartRange.collapse(true);
22369 var nodeEndRange = nodeRange.cloneRange();
22370 nodeEndRange.collapse(false);
22372 return rangeStartRange.compareBoundaryPoints(
22373 Range.START_TO_START, nodeEndRange) == -1 &&
22374 rangeEndRange.compareBoundaryPoints(
22375 Range.START_TO_START, nodeStartRange) == 1;
22379 rangeCompareNode : function(range, node)
22381 var nodeRange = node.ownerDocument.createRange();
22383 nodeRange.selectNode(node);
22385 nodeRange.selectNodeContents(node);
22389 range.collapse(true);
22391 nodeRange.collapse(true);
22393 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22394 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22396 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22398 var nodeIsBefore = ss == 1;
22399 var nodeIsAfter = ee == -1;
22401 if (nodeIsBefore && nodeIsAfter) {
22404 if (!nodeIsBefore && nodeIsAfter) {
22405 return 1; //right trailed.
22408 if (nodeIsBefore && !nodeIsAfter) {
22409 return 2; // left trailed.
22415 // private? - in a new class?
22416 cleanUpPaste : function()
22418 // cleans up the whole document..
22419 Roo.log('cleanuppaste');
22421 this.cleanUpChildren(this.doc.body);
22422 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22423 if (clean != this.doc.body.innerHTML) {
22424 this.doc.body.innerHTML = clean;
22429 cleanWordChars : function(input) {// change the chars to hex code
22430 var he = Roo.HtmlEditorCore;
22432 var output = input;
22433 Roo.each(he.swapCodes, function(sw) {
22434 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22436 output = output.replace(swapper, sw[1]);
22443 cleanUpChildren : function (n)
22445 if (!n.childNodes.length) {
22448 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22449 this.cleanUpChild(n.childNodes[i]);
22456 cleanUpChild : function (node)
22459 //console.log(node);
22460 if (node.nodeName == "#text") {
22461 // clean up silly Windows -- stuff?
22464 if (node.nodeName == "#comment") {
22465 node.parentNode.removeChild(node);
22466 // clean up silly Windows -- stuff?
22469 var lcname = node.tagName.toLowerCase();
22470 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22471 // whitelist of tags..
22473 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22475 node.parentNode.removeChild(node);
22480 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22482 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22483 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22485 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22486 // remove_keep_children = true;
22489 if (remove_keep_children) {
22490 this.cleanUpChildren(node);
22491 // inserts everything just before this node...
22492 while (node.childNodes.length) {
22493 var cn = node.childNodes[0];
22494 node.removeChild(cn);
22495 node.parentNode.insertBefore(cn, node);
22497 node.parentNode.removeChild(node);
22501 if (!node.attributes || !node.attributes.length) {
22502 this.cleanUpChildren(node);
22506 function cleanAttr(n,v)
22509 if (v.match(/^\./) || v.match(/^\//)) {
22512 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22515 if (v.match(/^#/)) {
22518 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22519 node.removeAttribute(n);
22523 var cwhite = this.cwhite;
22524 var cblack = this.cblack;
22526 function cleanStyle(n,v)
22528 if (v.match(/expression/)) { //XSS?? should we even bother..
22529 node.removeAttribute(n);
22533 var parts = v.split(/;/);
22536 Roo.each(parts, function(p) {
22537 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22541 var l = p.split(':').shift().replace(/\s+/g,'');
22542 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22544 if ( cwhite.length && cblack.indexOf(l) > -1) {
22545 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22546 //node.removeAttribute(n);
22550 // only allow 'c whitelisted system attributes'
22551 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22552 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22553 //node.removeAttribute(n);
22563 if (clean.length) {
22564 node.setAttribute(n, clean.join(';'));
22566 node.removeAttribute(n);
22572 for (var i = node.attributes.length-1; i > -1 ; i--) {
22573 var a = node.attributes[i];
22576 if (a.name.toLowerCase().substr(0,2)=='on') {
22577 node.removeAttribute(a.name);
22580 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22581 node.removeAttribute(a.name);
22584 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22585 cleanAttr(a.name,a.value); // fixme..
22588 if (a.name == 'style') {
22589 cleanStyle(a.name,a.value);
22592 /// clean up MS crap..
22593 // tecnically this should be a list of valid class'es..
22596 if (a.name == 'class') {
22597 if (a.value.match(/^Mso/)) {
22598 node.className = '';
22601 if (a.value.match(/^body$/)) {
22602 node.className = '';
22613 this.cleanUpChildren(node);
22619 * Clean up MS wordisms...
22621 cleanWord : function(node)
22626 this.cleanWord(this.doc.body);
22629 if (node.nodeName == "#text") {
22630 // clean up silly Windows -- stuff?
22633 if (node.nodeName == "#comment") {
22634 node.parentNode.removeChild(node);
22635 // clean up silly Windows -- stuff?
22639 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22640 node.parentNode.removeChild(node);
22644 // remove - but keep children..
22645 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22646 while (node.childNodes.length) {
22647 var cn = node.childNodes[0];
22648 node.removeChild(cn);
22649 node.parentNode.insertBefore(cn, node);
22651 node.parentNode.removeChild(node);
22652 this.iterateChildren(node, this.cleanWord);
22656 if (node.className.length) {
22658 var cn = node.className.split(/\W+/);
22660 Roo.each(cn, function(cls) {
22661 if (cls.match(/Mso[a-zA-Z]+/)) {
22666 node.className = cna.length ? cna.join(' ') : '';
22668 node.removeAttribute("class");
22672 if (node.hasAttribute("lang")) {
22673 node.removeAttribute("lang");
22676 if (node.hasAttribute("style")) {
22678 var styles = node.getAttribute("style").split(";");
22680 Roo.each(styles, function(s) {
22681 if (!s.match(/:/)) {
22684 var kv = s.split(":");
22685 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22688 // what ever is left... we allow.
22691 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22692 if (!nstyle.length) {
22693 node.removeAttribute('style');
22696 this.iterateChildren(node, this.cleanWord);
22702 * iterateChildren of a Node, calling fn each time, using this as the scole..
22703 * @param {DomNode} node node to iterate children of.
22704 * @param {Function} fn method of this class to call on each item.
22706 iterateChildren : function(node, fn)
22708 if (!node.childNodes.length) {
22711 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22712 fn.call(this, node.childNodes[i])
22718 * cleanTableWidths.
22720 * Quite often pasting from word etc.. results in tables with column and widths.
22721 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22724 cleanTableWidths : function(node)
22729 this.cleanTableWidths(this.doc.body);
22734 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22737 Roo.log(node.tagName);
22738 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22739 this.iterateChildren(node, this.cleanTableWidths);
22742 if (node.hasAttribute('width')) {
22743 node.removeAttribute('width');
22747 if (node.hasAttribute("style")) {
22750 var styles = node.getAttribute("style").split(";");
22752 Roo.each(styles, function(s) {
22753 if (!s.match(/:/)) {
22756 var kv = s.split(":");
22757 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22760 // what ever is left... we allow.
22763 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22764 if (!nstyle.length) {
22765 node.removeAttribute('style');
22769 this.iterateChildren(node, this.cleanTableWidths);
22777 domToHTML : function(currentElement, depth, nopadtext) {
22779 depth = depth || 0;
22780 nopadtext = nopadtext || false;
22782 if (!currentElement) {
22783 return this.domToHTML(this.doc.body);
22786 //Roo.log(currentElement);
22788 var allText = false;
22789 var nodeName = currentElement.nodeName;
22790 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22792 if (nodeName == '#text') {
22794 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22799 if (nodeName != 'BODY') {
22802 // Prints the node tagName, such as <A>, <IMG>, etc
22805 for(i = 0; i < currentElement.attributes.length;i++) {
22807 var aname = currentElement.attributes.item(i).name;
22808 if (!currentElement.attributes.item(i).value.length) {
22811 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22814 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22823 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22826 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22831 // Traverse the tree
22833 var currentElementChild = currentElement.childNodes.item(i);
22834 var allText = true;
22835 var innerHTML = '';
22837 while (currentElementChild) {
22838 // Formatting code (indent the tree so it looks nice on the screen)
22839 var nopad = nopadtext;
22840 if (lastnode == 'SPAN') {
22844 if (currentElementChild.nodeName == '#text') {
22845 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22846 toadd = nopadtext ? toadd : toadd.trim();
22847 if (!nopad && toadd.length > 80) {
22848 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22850 innerHTML += toadd;
22853 currentElementChild = currentElement.childNodes.item(i);
22859 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22861 // Recursively traverse the tree structure of the child node
22862 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22863 lastnode = currentElementChild.nodeName;
22865 currentElementChild=currentElement.childNodes.item(i);
22871 // The remaining code is mostly for formatting the tree
22872 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22877 ret+= "</"+tagName+">";
22883 applyBlacklists : function()
22885 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22886 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22890 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22891 if (b.indexOf(tag) > -1) {
22894 this.white.push(tag);
22898 Roo.each(w, function(tag) {
22899 if (b.indexOf(tag) > -1) {
22902 if (this.white.indexOf(tag) > -1) {
22905 this.white.push(tag);
22910 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22911 if (w.indexOf(tag) > -1) {
22914 this.black.push(tag);
22918 Roo.each(b, function(tag) {
22919 if (w.indexOf(tag) > -1) {
22922 if (this.black.indexOf(tag) > -1) {
22925 this.black.push(tag);
22930 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22931 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22935 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22936 if (b.indexOf(tag) > -1) {
22939 this.cwhite.push(tag);
22943 Roo.each(w, function(tag) {
22944 if (b.indexOf(tag) > -1) {
22947 if (this.cwhite.indexOf(tag) > -1) {
22950 this.cwhite.push(tag);
22955 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22956 if (w.indexOf(tag) > -1) {
22959 this.cblack.push(tag);
22963 Roo.each(b, function(tag) {
22964 if (w.indexOf(tag) > -1) {
22967 if (this.cblack.indexOf(tag) > -1) {
22970 this.cblack.push(tag);
22975 setStylesheets : function(stylesheets)
22977 if(typeof(stylesheets) == 'string'){
22978 Roo.get(this.iframe.contentDocument.head).createChild({
22980 rel : 'stylesheet',
22989 Roo.each(stylesheets, function(s) {
22994 Roo.get(_this.iframe.contentDocument.head).createChild({
22996 rel : 'stylesheet',
23005 removeStylesheets : function()
23009 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23014 setStyle : function(style)
23016 Roo.get(this.iframe.contentDocument.head).createChild({
23025 // hide stuff that is not compatible
23039 * @event specialkey
23043 * @cfg {String} fieldClass @hide
23046 * @cfg {String} focusClass @hide
23049 * @cfg {String} autoCreate @hide
23052 * @cfg {String} inputType @hide
23055 * @cfg {String} invalidClass @hide
23058 * @cfg {String} invalidText @hide
23061 * @cfg {String} msgFx @hide
23064 * @cfg {String} validateOnBlur @hide
23068 Roo.HtmlEditorCore.white = [
23069 'area', 'br', 'img', 'input', 'hr', 'wbr',
23071 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23072 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23073 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23074 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23075 'table', 'ul', 'xmp',
23077 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23080 'dir', 'menu', 'ol', 'ul', 'dl',
23086 Roo.HtmlEditorCore.black = [
23087 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23089 'base', 'basefont', 'bgsound', 'blink', 'body',
23090 'frame', 'frameset', 'head', 'html', 'ilayer',
23091 'iframe', 'layer', 'link', 'meta', 'object',
23092 'script', 'style' ,'title', 'xml' // clean later..
23094 Roo.HtmlEditorCore.clean = [
23095 'script', 'style', 'title', 'xml'
23097 Roo.HtmlEditorCore.remove = [
23102 Roo.HtmlEditorCore.ablack = [
23106 Roo.HtmlEditorCore.aclean = [
23107 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23111 Roo.HtmlEditorCore.pwhite= [
23112 'http', 'https', 'mailto'
23115 // white listed style attributes.
23116 Roo.HtmlEditorCore.cwhite= [
23117 // 'text-align', /// default is to allow most things..
23123 // black listed style attributes.
23124 Roo.HtmlEditorCore.cblack= [
23125 // 'font-size' -- this can be set by the project
23129 Roo.HtmlEditorCore.swapCodes =[
23148 * @class Roo.bootstrap.HtmlEditor
23149 * @extends Roo.bootstrap.TextArea
23150 * Bootstrap HtmlEditor class
23153 * Create a new HtmlEditor
23154 * @param {Object} config The config object
23157 Roo.bootstrap.HtmlEditor = function(config){
23158 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23159 if (!this.toolbars) {
23160 this.toolbars = [];
23163 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23166 * @event initialize
23167 * Fires when the editor is fully initialized (including the iframe)
23168 * @param {HtmlEditor} this
23173 * Fires when the editor is first receives the focus. Any insertion must wait
23174 * until after this event.
23175 * @param {HtmlEditor} this
23179 * @event beforesync
23180 * Fires before the textarea is updated with content from the editor iframe. Return false
23181 * to cancel the sync.
23182 * @param {HtmlEditor} this
23183 * @param {String} html
23187 * @event beforepush
23188 * Fires before the iframe editor is updated with content from the textarea. Return false
23189 * to cancel the push.
23190 * @param {HtmlEditor} this
23191 * @param {String} html
23196 * Fires when the textarea is updated with content from the editor iframe.
23197 * @param {HtmlEditor} this
23198 * @param {String} html
23203 * Fires when the iframe editor is updated with content from the textarea.
23204 * @param {HtmlEditor} this
23205 * @param {String} html
23209 * @event editmodechange
23210 * Fires when the editor switches edit modes
23211 * @param {HtmlEditor} this
23212 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23214 editmodechange: true,
23216 * @event editorevent
23217 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23218 * @param {HtmlEditor} this
23222 * @event firstfocus
23223 * Fires when on first focus - needed by toolbars..
23224 * @param {HtmlEditor} this
23229 * Auto save the htmlEditor value as a file into Events
23230 * @param {HtmlEditor} this
23234 * @event savedpreview
23235 * preview the saved version of htmlEditor
23236 * @param {HtmlEditor} this
23243 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23247 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23252 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23257 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23262 * @cfg {Number} height (in pixels)
23266 * @cfg {Number} width (in pixels)
23271 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23274 stylesheets: false,
23279 // private properties
23280 validationEvent : false,
23282 initialized : false,
23285 onFocus : Roo.emptyFn,
23287 hideMode:'offsets',
23289 tbContainer : false,
23293 toolbarContainer :function() {
23294 return this.wrap.select('.x-html-editor-tb',true).first();
23298 * Protected method that will not generally be called directly. It
23299 * is called when the editor creates its toolbar. Override this method if you need to
23300 * add custom toolbar buttons.
23301 * @param {HtmlEditor} editor
23303 createToolbar : function(){
23304 Roo.log('renewing');
23305 Roo.log("create toolbars");
23307 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23308 this.toolbars[0].render(this.toolbarContainer());
23312 // if (!editor.toolbars || !editor.toolbars.length) {
23313 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23316 // for (var i =0 ; i < editor.toolbars.length;i++) {
23317 // editor.toolbars[i] = Roo.factory(
23318 // typeof(editor.toolbars[i]) == 'string' ?
23319 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23320 // Roo.bootstrap.HtmlEditor);
23321 // editor.toolbars[i].init(editor);
23327 onRender : function(ct, position)
23329 // Roo.log("Call onRender: " + this.xtype);
23331 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23333 this.wrap = this.inputEl().wrap({
23334 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23337 this.editorcore.onRender(ct, position);
23339 if (this.resizable) {
23340 this.resizeEl = new Roo.Resizable(this.wrap, {
23344 minHeight : this.height,
23345 height: this.height,
23346 handles : this.resizable,
23349 resize : function(r, w, h) {
23350 _t.onResize(w,h); // -something
23356 this.createToolbar(this);
23359 if(!this.width && this.resizable){
23360 this.setSize(this.wrap.getSize());
23362 if (this.resizeEl) {
23363 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23364 // should trigger onReize..
23370 onResize : function(w, h)
23372 Roo.log('resize: ' +w + ',' + h );
23373 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23377 if(this.inputEl() ){
23378 if(typeof w == 'number'){
23379 var aw = w - this.wrap.getFrameWidth('lr');
23380 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23383 if(typeof h == 'number'){
23384 var tbh = -11; // fixme it needs to tool bar size!
23385 for (var i =0; i < this.toolbars.length;i++) {
23386 // fixme - ask toolbars for heights?
23387 tbh += this.toolbars[i].el.getHeight();
23388 //if (this.toolbars[i].footer) {
23389 // tbh += this.toolbars[i].footer.el.getHeight();
23397 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23398 ah -= 5; // knock a few pixes off for look..
23399 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23403 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23404 this.editorcore.onResize(ew,eh);
23409 * Toggles the editor between standard and source edit mode.
23410 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23412 toggleSourceEdit : function(sourceEditMode)
23414 this.editorcore.toggleSourceEdit(sourceEditMode);
23416 if(this.editorcore.sourceEditMode){
23417 Roo.log('editor - showing textarea');
23420 // Roo.log(this.syncValue());
23422 this.inputEl().removeClass(['hide', 'x-hidden']);
23423 this.inputEl().dom.removeAttribute('tabIndex');
23424 this.inputEl().focus();
23426 Roo.log('editor - hiding textarea');
23428 // Roo.log(this.pushValue());
23431 this.inputEl().addClass(['hide', 'x-hidden']);
23432 this.inputEl().dom.setAttribute('tabIndex', -1);
23433 //this.deferFocus();
23436 if(this.resizable){
23437 this.setSize(this.wrap.getSize());
23440 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23443 // private (for BoxComponent)
23444 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23446 // private (for BoxComponent)
23447 getResizeEl : function(){
23451 // private (for BoxComponent)
23452 getPositionEl : function(){
23457 initEvents : function(){
23458 this.originalValue = this.getValue();
23462 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23465 // markInvalid : Roo.emptyFn,
23467 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23470 // clearInvalid : Roo.emptyFn,
23472 setValue : function(v){
23473 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23474 this.editorcore.pushValue();
23479 deferFocus : function(){
23480 this.focus.defer(10, this);
23484 focus : function(){
23485 this.editorcore.focus();
23491 onDestroy : function(){
23497 for (var i =0; i < this.toolbars.length;i++) {
23498 // fixme - ask toolbars for heights?
23499 this.toolbars[i].onDestroy();
23502 this.wrap.dom.innerHTML = '';
23503 this.wrap.remove();
23508 onFirstFocus : function(){
23509 //Roo.log("onFirstFocus");
23510 this.editorcore.onFirstFocus();
23511 for (var i =0; i < this.toolbars.length;i++) {
23512 this.toolbars[i].onFirstFocus();
23518 syncValue : function()
23520 this.editorcore.syncValue();
23523 pushValue : function()
23525 this.editorcore.pushValue();
23529 // hide stuff that is not compatible
23543 * @event specialkey
23547 * @cfg {String} fieldClass @hide
23550 * @cfg {String} focusClass @hide
23553 * @cfg {String} autoCreate @hide
23556 * @cfg {String} inputType @hide
23559 * @cfg {String} invalidClass @hide
23562 * @cfg {String} invalidText @hide
23565 * @cfg {String} msgFx @hide
23568 * @cfg {String} validateOnBlur @hide
23577 Roo.namespace('Roo.bootstrap.htmleditor');
23579 * @class Roo.bootstrap.HtmlEditorToolbar1
23584 new Roo.bootstrap.HtmlEditor({
23587 new Roo.bootstrap.HtmlEditorToolbar1({
23588 disable : { fonts: 1 , format: 1, ..., ... , ...],
23594 * @cfg {Object} disable List of elements to disable..
23595 * @cfg {Array} btns List of additional buttons.
23599 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23602 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23605 Roo.apply(this, config);
23607 // default disabled, based on 'good practice'..
23608 this.disable = this.disable || {};
23609 Roo.applyIf(this.disable, {
23612 specialElements : true
23614 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23616 this.editor = config.editor;
23617 this.editorcore = config.editor.editorcore;
23619 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23621 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23622 // dont call parent... till later.
23624 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23629 editorcore : false,
23634 "h1","h2","h3","h4","h5","h6",
23636 "abbr", "acronym", "address", "cite", "samp", "var",
23640 onRender : function(ct, position)
23642 // Roo.log("Call onRender: " + this.xtype);
23644 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23646 this.el.dom.style.marginBottom = '0';
23648 var editorcore = this.editorcore;
23649 var editor= this.editor;
23652 var btn = function(id,cmd , toggle, handler, html){
23654 var event = toggle ? 'toggle' : 'click';
23659 xns: Roo.bootstrap,
23662 enableToggle:toggle !== false,
23664 pressed : toggle ? false : null,
23667 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23668 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23674 // var cb_box = function...
23679 xns: Roo.bootstrap,
23680 glyphicon : 'font',
23684 xns: Roo.bootstrap,
23688 Roo.each(this.formats, function(f) {
23689 style.menu.items.push({
23691 xns: Roo.bootstrap,
23692 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23697 editorcore.insertTag(this.tagname);
23704 children.push(style);
23706 btn('bold',false,true);
23707 btn('italic',false,true);
23708 btn('align-left', 'justifyleft',true);
23709 btn('align-center', 'justifycenter',true);
23710 btn('align-right' , 'justifyright',true);
23711 btn('link', false, false, function(btn) {
23712 //Roo.log("create link?");
23713 var url = prompt(this.createLinkText, this.defaultLinkValue);
23714 if(url && url != 'http:/'+'/'){
23715 this.editorcore.relayCmd('createlink', url);
23718 btn('list','insertunorderedlist',true);
23719 btn('pencil', false,true, function(btn){
23721 this.toggleSourceEdit(btn.pressed);
23724 if (this.editor.btns.length > 0) {
23725 for (var i = 0; i<this.editor.btns.length; i++) {
23726 children.push(this.editor.btns[i]);
23734 xns: Roo.bootstrap,
23739 xns: Roo.bootstrap,
23744 cog.menu.items.push({
23746 xns: Roo.bootstrap,
23747 html : Clean styles,
23752 editorcore.insertTag(this.tagname);
23761 this.xtype = 'NavSimplebar';
23763 for(var i=0;i< children.length;i++) {
23765 this.buttons.add(this.addxtypeChild(children[i]));
23769 editor.on('editorevent', this.updateToolbar, this);
23771 onBtnClick : function(id)
23773 this.editorcore.relayCmd(id);
23774 this.editorcore.focus();
23778 * Protected method that will not generally be called directly. It triggers
23779 * a toolbar update by reading the markup state of the current selection in the editor.
23781 updateToolbar: function(){
23783 if(!this.editorcore.activated){
23784 this.editor.onFirstFocus(); // is this neeed?
23788 var btns = this.buttons;
23789 var doc = this.editorcore.doc;
23790 btns.get('bold').setActive(doc.queryCommandState('bold'));
23791 btns.get('italic').setActive(doc.queryCommandState('italic'));
23792 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23794 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23795 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23796 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23798 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23799 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23802 var ans = this.editorcore.getAllAncestors();
23803 if (this.formatCombo) {
23806 var store = this.formatCombo.store;
23807 this.formatCombo.setValue("");
23808 for (var i =0; i < ans.length;i++) {
23809 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23811 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23819 // hides menus... - so this cant be on a menu...
23820 Roo.bootstrap.MenuMgr.hideAll();
23822 Roo.bootstrap.MenuMgr.hideAll();
23823 //this.editorsyncValue();
23825 onFirstFocus: function() {
23826 this.buttons.each(function(item){
23830 toggleSourceEdit : function(sourceEditMode){
23833 if(sourceEditMode){
23834 Roo.log("disabling buttons");
23835 this.buttons.each( function(item){
23836 if(item.cmd != 'pencil'){
23842 Roo.log("enabling buttons");
23843 if(this.editorcore.initialized){
23844 this.buttons.each( function(item){
23850 Roo.log("calling toggole on editor");
23851 // tell the editor that it's been pressed..
23852 this.editor.toggleSourceEdit(sourceEditMode);
23862 * @class Roo.bootstrap.Table.AbstractSelectionModel
23863 * @extends Roo.util.Observable
23864 * Abstract base class for grid SelectionModels. It provides the interface that should be
23865 * implemented by descendant classes. This class should not be directly instantiated.
23868 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23869 this.locked = false;
23870 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23874 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23875 /** @ignore Called by the grid automatically. Do not call directly. */
23876 init : function(grid){
23882 * Locks the selections.
23885 this.locked = true;
23889 * Unlocks the selections.
23891 unlock : function(){
23892 this.locked = false;
23896 * Returns true if the selections are locked.
23897 * @return {Boolean}
23899 isLocked : function(){
23900 return this.locked;
23904 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23905 * @class Roo.bootstrap.Table.RowSelectionModel
23906 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23907 * It supports multiple selections and keyboard selection/navigation.
23909 * @param {Object} config
23912 Roo.bootstrap.Table.RowSelectionModel = function(config){
23913 Roo.apply(this, config);
23914 this.selections = new Roo.util.MixedCollection(false, function(o){
23919 this.lastActive = false;
23923 * @event selectionchange
23924 * Fires when the selection changes
23925 * @param {SelectionModel} this
23927 "selectionchange" : true,
23929 * @event afterselectionchange
23930 * Fires after the selection changes (eg. by key press or clicking)
23931 * @param {SelectionModel} this
23933 "afterselectionchange" : true,
23935 * @event beforerowselect
23936 * Fires when a row is selected being selected, return false to cancel.
23937 * @param {SelectionModel} this
23938 * @param {Number} rowIndex The selected index
23939 * @param {Boolean} keepExisting False if other selections will be cleared
23941 "beforerowselect" : true,
23944 * Fires when a row is selected.
23945 * @param {SelectionModel} this
23946 * @param {Number} rowIndex The selected index
23947 * @param {Roo.data.Record} r The record
23949 "rowselect" : true,
23951 * @event rowdeselect
23952 * Fires when a row is deselected.
23953 * @param {SelectionModel} this
23954 * @param {Number} rowIndex The selected index
23956 "rowdeselect" : true
23958 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23959 this.locked = false;
23962 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23964 * @cfg {Boolean} singleSelect
23965 * True to allow selection of only one row at a time (defaults to false)
23967 singleSelect : false,
23970 initEvents : function()
23973 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23974 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23975 //}else{ // allow click to work like normal
23976 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23978 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23979 this.grid.on("rowclick", this.handleMouseDown, this);
23981 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23982 "up" : function(e){
23984 this.selectPrevious(e.shiftKey);
23985 }else if(this.last !== false && this.lastActive !== false){
23986 var last = this.last;
23987 this.selectRange(this.last, this.lastActive-1);
23988 this.grid.getView().focusRow(this.lastActive);
23989 if(last !== false){
23993 this.selectFirstRow();
23995 this.fireEvent("afterselectionchange", this);
23997 "down" : function(e){
23999 this.selectNext(e.shiftKey);
24000 }else if(this.last !== false && this.lastActive !== false){
24001 var last = this.last;
24002 this.selectRange(this.last, this.lastActive+1);
24003 this.grid.getView().focusRow(this.lastActive);
24004 if(last !== false){
24008 this.selectFirstRow();
24010 this.fireEvent("afterselectionchange", this);
24014 this.grid.store.on('load', function(){
24015 this.selections.clear();
24018 var view = this.grid.view;
24019 view.on("refresh", this.onRefresh, this);
24020 view.on("rowupdated", this.onRowUpdated, this);
24021 view.on("rowremoved", this.onRemove, this);
24026 onRefresh : function()
24028 var ds = this.grid.store, i, v = this.grid.view;
24029 var s = this.selections;
24030 s.each(function(r){
24031 if((i = ds.indexOfId(r.id)) != -1){
24040 onRemove : function(v, index, r){
24041 this.selections.remove(r);
24045 onRowUpdated : function(v, index, r){
24046 if(this.isSelected(r)){
24047 v.onRowSelect(index);
24053 * @param {Array} records The records to select
24054 * @param {Boolean} keepExisting (optional) True to keep existing selections
24056 selectRecords : function(records, keepExisting)
24059 this.clearSelections();
24061 var ds = this.grid.store;
24062 for(var i = 0, len = records.length; i < len; i++){
24063 this.selectRow(ds.indexOf(records[i]), true);
24068 * Gets the number of selected rows.
24071 getCount : function(){
24072 return this.selections.length;
24076 * Selects the first row in the grid.
24078 selectFirstRow : function(){
24083 * Select the last row.
24084 * @param {Boolean} keepExisting (optional) True to keep existing selections
24086 selectLastRow : function(keepExisting){
24087 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24088 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24092 * Selects the row immediately following the last selected row.
24093 * @param {Boolean} keepExisting (optional) True to keep existing selections
24095 selectNext : function(keepExisting)
24097 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24098 this.selectRow(this.last+1, keepExisting);
24099 this.grid.getView().focusRow(this.last);
24104 * Selects the row that precedes the last selected row.
24105 * @param {Boolean} keepExisting (optional) True to keep existing selections
24107 selectPrevious : function(keepExisting){
24109 this.selectRow(this.last-1, keepExisting);
24110 this.grid.getView().focusRow(this.last);
24115 * Returns the selected records
24116 * @return {Array} Array of selected records
24118 getSelections : function(){
24119 return [].concat(this.selections.items);
24123 * Returns the first selected record.
24126 getSelected : function(){
24127 return this.selections.itemAt(0);
24132 * Clears all selections.
24134 clearSelections : function(fast)
24140 var ds = this.grid.store;
24141 var s = this.selections;
24142 s.each(function(r){
24143 this.deselectRow(ds.indexOfId(r.id));
24147 this.selections.clear();
24154 * Selects all rows.
24156 selectAll : function(){
24160 this.selections.clear();
24161 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24162 this.selectRow(i, true);
24167 * Returns True if there is a selection.
24168 * @return {Boolean}
24170 hasSelection : function(){
24171 return this.selections.length > 0;
24175 * Returns True if the specified row is selected.
24176 * @param {Number/Record} record The record or index of the record to check
24177 * @return {Boolean}
24179 isSelected : function(index){
24180 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24181 return (r && this.selections.key(r.id) ? true : false);
24185 * Returns True if the specified record id is selected.
24186 * @param {String} id The id of record to check
24187 * @return {Boolean}
24189 isIdSelected : function(id){
24190 return (this.selections.key(id) ? true : false);
24195 handleMouseDBClick : function(e, t){
24199 handleMouseDown : function(e, t)
24201 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24202 if(this.isLocked() || rowIndex < 0 ){
24205 if(e.shiftKey && this.last !== false){
24206 var last = this.last;
24207 this.selectRange(last, rowIndex, e.ctrlKey);
24208 this.last = last; // reset the last
24212 var isSelected = this.isSelected(rowIndex);
24213 //Roo.log("select row:" + rowIndex);
24215 this.deselectRow(rowIndex);
24217 this.selectRow(rowIndex, true);
24221 if(e.button !== 0 && isSelected){
24222 alert('rowIndex 2: ' + rowIndex);
24223 view.focusRow(rowIndex);
24224 }else if(e.ctrlKey && isSelected){
24225 this.deselectRow(rowIndex);
24226 }else if(!isSelected){
24227 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24228 view.focusRow(rowIndex);
24232 this.fireEvent("afterselectionchange", this);
24235 handleDragableRowClick : function(grid, rowIndex, e)
24237 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24238 this.selectRow(rowIndex, false);
24239 grid.view.focusRow(rowIndex);
24240 this.fireEvent("afterselectionchange", this);
24245 * Selects multiple rows.
24246 * @param {Array} rows Array of the indexes of the row to select
24247 * @param {Boolean} keepExisting (optional) True to keep existing selections
24249 selectRows : function(rows, keepExisting){
24251 this.clearSelections();
24253 for(var i = 0, len = rows.length; i < len; i++){
24254 this.selectRow(rows[i], true);
24259 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24260 * @param {Number} startRow The index of the first row in the range
24261 * @param {Number} endRow The index of the last row in the range
24262 * @param {Boolean} keepExisting (optional) True to retain existing selections
24264 selectRange : function(startRow, endRow, keepExisting){
24269 this.clearSelections();
24271 if(startRow <= endRow){
24272 for(var i = startRow; i <= endRow; i++){
24273 this.selectRow(i, true);
24276 for(var i = startRow; i >= endRow; i--){
24277 this.selectRow(i, true);
24283 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24284 * @param {Number} startRow The index of the first row in the range
24285 * @param {Number} endRow The index of the last row in the range
24287 deselectRange : function(startRow, endRow, preventViewNotify){
24291 for(var i = startRow; i <= endRow; i++){
24292 this.deselectRow(i, preventViewNotify);
24298 * @param {Number} row The index of the row to select
24299 * @param {Boolean} keepExisting (optional) True to keep existing selections
24301 selectRow : function(index, keepExisting, preventViewNotify)
24303 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24306 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24307 if(!keepExisting || this.singleSelect){
24308 this.clearSelections();
24311 var r = this.grid.store.getAt(index);
24312 //console.log('selectRow - record id :' + r.id);
24314 this.selections.add(r);
24315 this.last = this.lastActive = index;
24316 if(!preventViewNotify){
24317 var proxy = new Roo.Element(
24318 this.grid.getRowDom(index)
24320 proxy.addClass('bg-info info');
24322 this.fireEvent("rowselect", this, index, r);
24323 this.fireEvent("selectionchange", this);
24329 * @param {Number} row The index of the row to deselect
24331 deselectRow : function(index, preventViewNotify)
24336 if(this.last == index){
24339 if(this.lastActive == index){
24340 this.lastActive = false;
24343 var r = this.grid.store.getAt(index);
24348 this.selections.remove(r);
24349 //.console.log('deselectRow - record id :' + r.id);
24350 if(!preventViewNotify){
24352 var proxy = new Roo.Element(
24353 this.grid.getRowDom(index)
24355 proxy.removeClass('bg-info info');
24357 this.fireEvent("rowdeselect", this, index);
24358 this.fireEvent("selectionchange", this);
24362 restoreLast : function(){
24364 this.last = this._last;
24369 acceptsNav : function(row, col, cm){
24370 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24374 onEditorKey : function(field, e){
24375 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24380 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24382 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24384 }else if(k == e.ENTER && !e.ctrlKey){
24388 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24390 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24392 }else if(k == e.ESC){
24396 g.startEditing(newCell[0], newCell[1]);
24402 * Ext JS Library 1.1.1
24403 * Copyright(c) 2006-2007, Ext JS, LLC.
24405 * Originally Released Under LGPL - original licence link has changed is not relivant.
24408 * <script type="text/javascript">
24412 * @class Roo.bootstrap.PagingToolbar
24413 * @extends Roo.bootstrap.NavSimplebar
24414 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24416 * Create a new PagingToolbar
24417 * @param {Object} config The config object
24418 * @param {Roo.data.Store} store
24420 Roo.bootstrap.PagingToolbar = function(config)
24422 // old args format still supported... - xtype is prefered..
24423 // created from xtype...
24425 this.ds = config.dataSource;
24427 if (config.store && !this.ds) {
24428 this.store= Roo.factory(config.store, Roo.data);
24429 this.ds = this.store;
24430 this.ds.xmodule = this.xmodule || false;
24433 this.toolbarItems = [];
24434 if (config.items) {
24435 this.toolbarItems = config.items;
24438 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24443 this.bind(this.ds);
24446 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24450 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24452 * @cfg {Roo.data.Store} dataSource
24453 * The underlying data store providing the paged data
24456 * @cfg {String/HTMLElement/Element} container
24457 * container The id or element that will contain the toolbar
24460 * @cfg {Boolean} displayInfo
24461 * True to display the displayMsg (defaults to false)
24464 * @cfg {Number} pageSize
24465 * The number of records to display per page (defaults to 20)
24469 * @cfg {String} displayMsg
24470 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24472 displayMsg : 'Displaying {0} - {1} of {2}',
24474 * @cfg {String} emptyMsg
24475 * The message to display when no records are found (defaults to "No data to display")
24477 emptyMsg : 'No data to display',
24479 * Customizable piece of the default paging text (defaults to "Page")
24482 beforePageText : "Page",
24484 * Customizable piece of the default paging text (defaults to "of %0")
24487 afterPageText : "of {0}",
24489 * Customizable piece of the default paging text (defaults to "First Page")
24492 firstText : "First Page",
24494 * Customizable piece of the default paging text (defaults to "Previous Page")
24497 prevText : "Previous Page",
24499 * Customizable piece of the default paging text (defaults to "Next Page")
24502 nextText : "Next Page",
24504 * Customizable piece of the default paging text (defaults to "Last Page")
24507 lastText : "Last Page",
24509 * Customizable piece of the default paging text (defaults to "Refresh")
24512 refreshText : "Refresh",
24516 onRender : function(ct, position)
24518 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24519 this.navgroup.parentId = this.id;
24520 this.navgroup.onRender(this.el, null);
24521 // add the buttons to the navgroup
24523 if(this.displayInfo){
24524 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24525 this.displayEl = this.el.select('.x-paging-info', true).first();
24526 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24527 // this.displayEl = navel.el.select('span',true).first();
24533 Roo.each(_this.buttons, function(e){ // this might need to use render????
24534 Roo.factory(e).render(_this.el);
24538 Roo.each(_this.toolbarItems, function(e) {
24539 _this.navgroup.addItem(e);
24543 this.first = this.navgroup.addItem({
24544 tooltip: this.firstText,
24546 icon : 'fa fa-backward',
24548 preventDefault: true,
24549 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24552 this.prev = this.navgroup.addItem({
24553 tooltip: this.prevText,
24555 icon : 'fa fa-step-backward',
24557 preventDefault: true,
24558 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24560 //this.addSeparator();
24563 var field = this.navgroup.addItem( {
24565 cls : 'x-paging-position',
24567 html : this.beforePageText +
24568 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24569 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24572 this.field = field.el.select('input', true).first();
24573 this.field.on("keydown", this.onPagingKeydown, this);
24574 this.field.on("focus", function(){this.dom.select();});
24577 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24578 //this.field.setHeight(18);
24579 //this.addSeparator();
24580 this.next = this.navgroup.addItem({
24581 tooltip: this.nextText,
24583 html : ' <i class="fa fa-step-forward">',
24585 preventDefault: true,
24586 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24588 this.last = this.navgroup.addItem({
24589 tooltip: this.lastText,
24590 icon : 'fa fa-forward',
24593 preventDefault: true,
24594 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24596 //this.addSeparator();
24597 this.loading = this.navgroup.addItem({
24598 tooltip: this.refreshText,
24599 icon: 'fa fa-refresh',
24600 preventDefault: true,
24601 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24607 updateInfo : function(){
24608 if(this.displayEl){
24609 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24610 var msg = count == 0 ?
24614 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24616 this.displayEl.update(msg);
24621 onLoad : function(ds, r, o)
24623 this.cursor = o.params.start ? o.params.start : 0;
24625 var d = this.getPageData(),
24630 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24631 this.field.dom.value = ap;
24632 this.first.setDisabled(ap == 1);
24633 this.prev.setDisabled(ap == 1);
24634 this.next.setDisabled(ap == ps);
24635 this.last.setDisabled(ap == ps);
24636 this.loading.enable();
24641 getPageData : function(){
24642 var total = this.ds.getTotalCount();
24645 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24646 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24651 onLoadError : function(){
24652 this.loading.enable();
24656 onPagingKeydown : function(e){
24657 var k = e.getKey();
24658 var d = this.getPageData();
24660 var v = this.field.dom.value, pageNum;
24661 if(!v || isNaN(pageNum = parseInt(v, 10))){
24662 this.field.dom.value = d.activePage;
24665 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24666 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24669 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))
24671 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24672 this.field.dom.value = pageNum;
24673 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24676 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24678 var v = this.field.dom.value, pageNum;
24679 var increment = (e.shiftKey) ? 10 : 1;
24680 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24683 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24684 this.field.dom.value = d.activePage;
24687 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24689 this.field.dom.value = parseInt(v, 10) + increment;
24690 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24691 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24698 beforeLoad : function(){
24700 this.loading.disable();
24705 onClick : function(which){
24714 ds.load({params:{start: 0, limit: this.pageSize}});
24717 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24720 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24723 var total = ds.getTotalCount();
24724 var extra = total % this.pageSize;
24725 var lastStart = extra ? (total - extra) : total-this.pageSize;
24726 ds.load({params:{start: lastStart, limit: this.pageSize}});
24729 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24735 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24736 * @param {Roo.data.Store} store The data store to unbind
24738 unbind : function(ds){
24739 ds.un("beforeload", this.beforeLoad, this);
24740 ds.un("load", this.onLoad, this);
24741 ds.un("loadexception", this.onLoadError, this);
24742 ds.un("remove", this.updateInfo, this);
24743 ds.un("add", this.updateInfo, this);
24744 this.ds = undefined;
24748 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24749 * @param {Roo.data.Store} store The data store to bind
24751 bind : function(ds){
24752 ds.on("beforeload", this.beforeLoad, this);
24753 ds.on("load", this.onLoad, this);
24754 ds.on("loadexception", this.onLoadError, this);
24755 ds.on("remove", this.updateInfo, this);
24756 ds.on("add", this.updateInfo, this);
24767 * @class Roo.bootstrap.MessageBar
24768 * @extends Roo.bootstrap.Component
24769 * Bootstrap MessageBar class
24770 * @cfg {String} html contents of the MessageBar
24771 * @cfg {String} weight (info | success | warning | danger) default info
24772 * @cfg {String} beforeClass insert the bar before the given class
24773 * @cfg {Boolean} closable (true | false) default false
24774 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24777 * Create a new Element
24778 * @param {Object} config The config object
24781 Roo.bootstrap.MessageBar = function(config){
24782 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24785 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24791 beforeClass: 'bootstrap-sticky-wrap',
24793 getAutoCreate : function(){
24797 cls: 'alert alert-dismissable alert-' + this.weight,
24802 html: this.html || ''
24808 cfg.cls += ' alert-messages-fixed';
24822 onRender : function(ct, position)
24824 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24827 var cfg = Roo.apply({}, this.getAutoCreate());
24831 cfg.cls += ' ' + this.cls;
24834 cfg.style = this.style;
24836 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24838 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24841 this.el.select('>button.close').on('click', this.hide, this);
24847 if (!this.rendered) {
24853 this.fireEvent('show', this);
24859 if (!this.rendered) {
24865 this.fireEvent('hide', this);
24868 update : function()
24870 // var e = this.el.dom.firstChild;
24872 // if(this.closable){
24873 // e = e.nextSibling;
24876 // e.data = this.html || '';
24878 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24894 * @class Roo.bootstrap.Graph
24895 * @extends Roo.bootstrap.Component
24896 * Bootstrap Graph class
24900 @cfg {String} graphtype bar | vbar | pie
24901 @cfg {number} g_x coodinator | centre x (pie)
24902 @cfg {number} g_y coodinator | centre y (pie)
24903 @cfg {number} g_r radius (pie)
24904 @cfg {number} g_height height of the chart (respected by all elements in the set)
24905 @cfg {number} g_width width of the chart (respected by all elements in the set)
24906 @cfg {Object} title The title of the chart
24909 -opts (object) options for the chart
24911 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24912 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24914 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.
24915 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24917 o stretch (boolean)
24919 -opts (object) options for the pie
24922 o startAngle (number)
24923 o endAngle (number)
24927 * Create a new Input
24928 * @param {Object} config The config object
24931 Roo.bootstrap.Graph = function(config){
24932 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24938 * The img click event for the img.
24939 * @param {Roo.EventObject} e
24945 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24956 //g_colors: this.colors,
24963 getAutoCreate : function(){
24974 onRender : function(ct,position){
24977 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24979 if (typeof(Raphael) == 'undefined') {
24980 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24984 this.raphael = Raphael(this.el.dom);
24986 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24987 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24988 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24989 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24991 r.text(160, 10, "Single Series Chart").attr(txtattr);
24992 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24993 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24994 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24996 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24997 r.barchart(330, 10, 300, 220, data1);
24998 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24999 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25002 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25003 // r.barchart(30, 30, 560, 250, xdata, {
25004 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25005 // axis : "0 0 1 1",
25006 // axisxlabels : xdata
25007 // //yvalues : cols,
25010 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25012 // this.load(null,xdata,{
25013 // axis : "0 0 1 1",
25014 // axisxlabels : xdata
25019 load : function(graphtype,xdata,opts)
25021 this.raphael.clear();
25023 graphtype = this.graphtype;
25028 var r = this.raphael,
25029 fin = function () {
25030 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25032 fout = function () {
25033 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25035 pfin = function() {
25036 this.sector.stop();
25037 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25040 this.label[0].stop();
25041 this.label[0].attr({ r: 7.5 });
25042 this.label[1].attr({ "font-weight": 800 });
25045 pfout = function() {
25046 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25049 this.label[0].animate({ r: 5 }, 500, "bounce");
25050 this.label[1].attr({ "font-weight": 400 });
25056 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25059 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25062 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25063 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25065 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25072 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25077 setTitle: function(o)
25082 initEvents: function() {
25085 this.el.on('click', this.onClick, this);
25089 onClick : function(e)
25091 Roo.log('img onclick');
25092 this.fireEvent('click', this, e);
25104 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25107 * @class Roo.bootstrap.dash.NumberBox
25108 * @extends Roo.bootstrap.Component
25109 * Bootstrap NumberBox class
25110 * @cfg {String} headline Box headline
25111 * @cfg {String} content Box content
25112 * @cfg {String} icon Box icon
25113 * @cfg {String} footer Footer text
25114 * @cfg {String} fhref Footer href
25117 * Create a new NumberBox
25118 * @param {Object} config The config object
25122 Roo.bootstrap.dash.NumberBox = function(config){
25123 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25127 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25136 getAutoCreate : function(){
25140 cls : 'small-box ',
25148 cls : 'roo-headline',
25149 html : this.headline
25153 cls : 'roo-content',
25154 html : this.content
25168 cls : 'ion ' + this.icon
25177 cls : 'small-box-footer',
25178 href : this.fhref || '#',
25182 cfg.cn.push(footer);
25189 onRender : function(ct,position){
25190 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25197 setHeadline: function (value)
25199 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25202 setFooter: function (value, href)
25204 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25207 this.el.select('a.small-box-footer',true).first().attr('href', href);
25212 setContent: function (value)
25214 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25217 initEvents: function()
25231 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25234 * @class Roo.bootstrap.dash.TabBox
25235 * @extends Roo.bootstrap.Component
25236 * Bootstrap TabBox class
25237 * @cfg {String} title Title of the TabBox
25238 * @cfg {String} icon Icon of the TabBox
25239 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25240 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25243 * Create a new TabBox
25244 * @param {Object} config The config object
25248 Roo.bootstrap.dash.TabBox = function(config){
25249 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25254 * When a pane is added
25255 * @param {Roo.bootstrap.dash.TabPane} pane
25259 * @event activatepane
25260 * When a pane is activated
25261 * @param {Roo.bootstrap.dash.TabPane} pane
25263 "activatepane" : true
25271 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25276 tabScrollable : false,
25278 getChildContainer : function()
25280 return this.el.select('.tab-content', true).first();
25283 getAutoCreate : function(){
25287 cls: 'pull-left header',
25295 cls: 'fa ' + this.icon
25301 cls: 'nav nav-tabs pull-right',
25307 if(this.tabScrollable){
25314 cls: 'nav nav-tabs pull-right',
25325 cls: 'nav-tabs-custom',
25330 cls: 'tab-content no-padding',
25338 initEvents : function()
25340 //Roo.log('add add pane handler');
25341 this.on('addpane', this.onAddPane, this);
25344 * Updates the box title
25345 * @param {String} html to set the title to.
25347 setTitle : function(value)
25349 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25351 onAddPane : function(pane)
25353 this.panes.push(pane);
25354 //Roo.log('addpane');
25356 // tabs are rendere left to right..
25357 if(!this.showtabs){
25361 var ctr = this.el.select('.nav-tabs', true).first();
25364 var existing = ctr.select('.nav-tab',true);
25365 var qty = existing.getCount();;
25368 var tab = ctr.createChild({
25370 cls : 'nav-tab' + (qty ? '' : ' active'),
25378 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25381 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25383 pane.el.addClass('active');
25388 onTabClick : function(ev,un,ob,pane)
25390 //Roo.log('tab - prev default');
25391 ev.preventDefault();
25394 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25395 pane.tab.addClass('active');
25396 //Roo.log(pane.title);
25397 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25398 // technically we should have a deactivate event.. but maybe add later.
25399 // and it should not de-activate the selected tab...
25400 this.fireEvent('activatepane', pane);
25401 pane.el.addClass('active');
25402 pane.fireEvent('activate');
25407 getActivePane : function()
25410 Roo.each(this.panes, function(p) {
25411 if(p.el.hasClass('active')){
25432 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25434 * @class Roo.bootstrap.TabPane
25435 * @extends Roo.bootstrap.Component
25436 * Bootstrap TabPane class
25437 * @cfg {Boolean} active (false | true) Default false
25438 * @cfg {String} title title of panel
25442 * Create a new TabPane
25443 * @param {Object} config The config object
25446 Roo.bootstrap.dash.TabPane = function(config){
25447 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25453 * When a pane is activated
25454 * @param {Roo.bootstrap.dash.TabPane} pane
25461 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25466 // the tabBox that this is attached to.
25469 getAutoCreate : function()
25477 cfg.cls += ' active';
25482 initEvents : function()
25484 //Roo.log('trigger add pane handler');
25485 this.parent().fireEvent('addpane', this)
25489 * Updates the tab title
25490 * @param {String} html to set the title to.
25492 setTitle: function(str)
25498 this.tab.select('a', true).first().dom.innerHTML = str;
25515 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25518 * @class Roo.bootstrap.menu.Menu
25519 * @extends Roo.bootstrap.Component
25520 * Bootstrap Menu class - container for Menu
25521 * @cfg {String} html Text of the menu
25522 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25523 * @cfg {String} icon Font awesome icon
25524 * @cfg {String} pos Menu align to (top | bottom) default bottom
25528 * Create a new Menu
25529 * @param {Object} config The config object
25533 Roo.bootstrap.menu.Menu = function(config){
25534 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25538 * @event beforeshow
25539 * Fires before this menu is displayed
25540 * @param {Roo.bootstrap.menu.Menu} this
25544 * @event beforehide
25545 * Fires before this menu is hidden
25546 * @param {Roo.bootstrap.menu.Menu} this
25551 * Fires after this menu is displayed
25552 * @param {Roo.bootstrap.menu.Menu} this
25557 * Fires after this menu is hidden
25558 * @param {Roo.bootstrap.menu.Menu} this
25563 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25564 * @param {Roo.bootstrap.menu.Menu} this
25565 * @param {Roo.EventObject} e
25572 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25576 weight : 'default',
25581 getChildContainer : function() {
25582 if(this.isSubMenu){
25586 return this.el.select('ul.dropdown-menu', true).first();
25589 getAutoCreate : function()
25594 cls : 'roo-menu-text',
25602 cls : 'fa ' + this.icon
25613 cls : 'dropdown-button btn btn-' + this.weight,
25618 cls : 'dropdown-toggle btn btn-' + this.weight,
25628 cls : 'dropdown-menu'
25634 if(this.pos == 'top'){
25635 cfg.cls += ' dropup';
25638 if(this.isSubMenu){
25641 cls : 'dropdown-menu'
25648 onRender : function(ct, position)
25650 this.isSubMenu = ct.hasClass('dropdown-submenu');
25652 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25655 initEvents : function()
25657 if(this.isSubMenu){
25661 this.hidden = true;
25663 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25664 this.triggerEl.on('click', this.onTriggerPress, this);
25666 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25667 this.buttonEl.on('click', this.onClick, this);
25673 if(this.isSubMenu){
25677 return this.el.select('ul.dropdown-menu', true).first();
25680 onClick : function(e)
25682 this.fireEvent("click", this, e);
25685 onTriggerPress : function(e)
25687 if (this.isVisible()) {
25694 isVisible : function(){
25695 return !this.hidden;
25700 this.fireEvent("beforeshow", this);
25702 this.hidden = false;
25703 this.el.addClass('open');
25705 Roo.get(document).on("mouseup", this.onMouseUp, this);
25707 this.fireEvent("show", this);
25714 this.fireEvent("beforehide", this);
25716 this.hidden = true;
25717 this.el.removeClass('open');
25719 Roo.get(document).un("mouseup", this.onMouseUp);
25721 this.fireEvent("hide", this);
25724 onMouseUp : function()
25738 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25741 * @class Roo.bootstrap.menu.Item
25742 * @extends Roo.bootstrap.Component
25743 * Bootstrap MenuItem class
25744 * @cfg {Boolean} submenu (true | false) default false
25745 * @cfg {String} html text of the item
25746 * @cfg {String} href the link
25747 * @cfg {Boolean} disable (true | false) default false
25748 * @cfg {Boolean} preventDefault (true | false) default true
25749 * @cfg {String} icon Font awesome icon
25750 * @cfg {String} pos Submenu align to (left | right) default right
25754 * Create a new Item
25755 * @param {Object} config The config object
25759 Roo.bootstrap.menu.Item = function(config){
25760 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25764 * Fires when the mouse is hovering over this menu
25765 * @param {Roo.bootstrap.menu.Item} this
25766 * @param {Roo.EventObject} e
25771 * Fires when the mouse exits this menu
25772 * @param {Roo.bootstrap.menu.Item} this
25773 * @param {Roo.EventObject} e
25779 * The raw click event for the entire grid.
25780 * @param {Roo.EventObject} e
25786 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25791 preventDefault: true,
25796 getAutoCreate : function()
25801 cls : 'roo-menu-item-text',
25809 cls : 'fa ' + this.icon
25818 href : this.href || '#',
25825 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25829 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25831 if(this.pos == 'left'){
25832 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25839 initEvents : function()
25841 this.el.on('mouseover', this.onMouseOver, this);
25842 this.el.on('mouseout', this.onMouseOut, this);
25844 this.el.select('a', true).first().on('click', this.onClick, this);
25848 onClick : function(e)
25850 if(this.preventDefault){
25851 e.preventDefault();
25854 this.fireEvent("click", this, e);
25857 onMouseOver : function(e)
25859 if(this.submenu && this.pos == 'left'){
25860 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25863 this.fireEvent("mouseover", this, e);
25866 onMouseOut : function(e)
25868 this.fireEvent("mouseout", this, e);
25880 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25883 * @class Roo.bootstrap.menu.Separator
25884 * @extends Roo.bootstrap.Component
25885 * Bootstrap Separator class
25888 * Create a new Separator
25889 * @param {Object} config The config object
25893 Roo.bootstrap.menu.Separator = function(config){
25894 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25897 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25899 getAutoCreate : function(){
25920 * @class Roo.bootstrap.Tooltip
25921 * Bootstrap Tooltip class
25922 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25923 * to determine which dom element triggers the tooltip.
25925 * It needs to add support for additional attributes like tooltip-position
25928 * Create a new Toolti
25929 * @param {Object} config The config object
25932 Roo.bootstrap.Tooltip = function(config){
25933 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25935 this.alignment = Roo.bootstrap.Tooltip.alignment;
25937 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25938 this.alignment = config.alignment;
25943 Roo.apply(Roo.bootstrap.Tooltip, {
25945 * @function init initialize tooltip monitoring.
25949 currentTip : false,
25950 currentRegion : false,
25956 Roo.get(document).on('mouseover', this.enter ,this);
25957 Roo.get(document).on('mouseout', this.leave, this);
25960 this.currentTip = new Roo.bootstrap.Tooltip();
25963 enter : function(ev)
25965 var dom = ev.getTarget();
25967 //Roo.log(['enter',dom]);
25968 var el = Roo.fly(dom);
25969 if (this.currentEl) {
25971 //Roo.log(this.currentEl);
25972 //Roo.log(this.currentEl.contains(dom));
25973 if (this.currentEl == el) {
25976 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25982 if (this.currentTip.el) {
25983 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25987 if(!el || el.dom == document){
25993 // you can not look for children, as if el is the body.. then everythign is the child..
25994 if (!el.attr('tooltip')) { //
25995 if (!el.select("[tooltip]").elements.length) {
25998 // is the mouse over this child...?
25999 bindEl = el.select("[tooltip]").first();
26000 var xy = ev.getXY();
26001 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26002 //Roo.log("not in region.");
26005 //Roo.log("child element over..");
26008 this.currentEl = bindEl;
26009 this.currentTip.bind(bindEl);
26010 this.currentRegion = Roo.lib.Region.getRegion(dom);
26011 this.currentTip.enter();
26014 leave : function(ev)
26016 var dom = ev.getTarget();
26017 //Roo.log(['leave',dom]);
26018 if (!this.currentEl) {
26023 if (dom != this.currentEl.dom) {
26026 var xy = ev.getXY();
26027 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26030 // only activate leave if mouse cursor is outside... bounding box..
26035 if (this.currentTip) {
26036 this.currentTip.leave();
26038 //Roo.log('clear currentEl');
26039 this.currentEl = false;
26044 'left' : ['r-l', [-2,0], 'right'],
26045 'right' : ['l-r', [2,0], 'left'],
26046 'bottom' : ['t-b', [0,2], 'top'],
26047 'top' : [ 'b-t', [0,-2], 'bottom']
26053 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26058 delay : null, // can be { show : 300 , hide: 500}
26062 hoverState : null, //???
26064 placement : 'bottom',
26068 getAutoCreate : function(){
26075 cls : 'tooltip-arrow'
26078 cls : 'tooltip-inner'
26085 bind : function(el)
26091 enter : function () {
26093 if (this.timeout != null) {
26094 clearTimeout(this.timeout);
26097 this.hoverState = 'in';
26098 //Roo.log("enter - show");
26099 if (!this.delay || !this.delay.show) {
26104 this.timeout = setTimeout(function () {
26105 if (_t.hoverState == 'in') {
26108 }, this.delay.show);
26112 clearTimeout(this.timeout);
26114 this.hoverState = 'out';
26115 if (!this.delay || !this.delay.hide) {
26121 this.timeout = setTimeout(function () {
26122 //Roo.log("leave - timeout");
26124 if (_t.hoverState == 'out') {
26126 Roo.bootstrap.Tooltip.currentEl = false;
26131 show : function (msg)
26134 this.render(document.body);
26137 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26139 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26141 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26143 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26145 var placement = typeof this.placement == 'function' ?
26146 this.placement.call(this, this.el, on_el) :
26149 var autoToken = /\s?auto?\s?/i;
26150 var autoPlace = autoToken.test(placement);
26152 placement = placement.replace(autoToken, '') || 'top';
26156 //this.el.setXY([0,0]);
26158 //this.el.dom.style.display='block';
26160 //this.el.appendTo(on_el);
26162 var p = this.getPosition();
26163 var box = this.el.getBox();
26169 var align = this.alignment[placement];
26171 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26173 if(placement == 'top' || placement == 'bottom'){
26175 placement = 'right';
26178 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26179 placement = 'left';
26182 var scroll = Roo.select('body', true).first().getScroll();
26184 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26190 this.el.alignTo(this.bindEl, align[0],align[1]);
26191 //var arrow = this.el.select('.arrow',true).first();
26192 //arrow.set(align[2],
26194 this.el.addClass(placement);
26196 this.el.addClass('in fade');
26198 this.hoverState = null;
26200 if (this.el.hasClass('fade')) {
26211 //this.el.setXY([0,0]);
26212 this.el.removeClass('in');
26228 * @class Roo.bootstrap.LocationPicker
26229 * @extends Roo.bootstrap.Component
26230 * Bootstrap LocationPicker class
26231 * @cfg {Number} latitude Position when init default 0
26232 * @cfg {Number} longitude Position when init default 0
26233 * @cfg {Number} zoom default 15
26234 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26235 * @cfg {Boolean} mapTypeControl default false
26236 * @cfg {Boolean} disableDoubleClickZoom default false
26237 * @cfg {Boolean} scrollwheel default true
26238 * @cfg {Boolean} streetViewControl default false
26239 * @cfg {Number} radius default 0
26240 * @cfg {String} locationName
26241 * @cfg {Boolean} draggable default true
26242 * @cfg {Boolean} enableAutocomplete default false
26243 * @cfg {Boolean} enableReverseGeocode default true
26244 * @cfg {String} markerTitle
26247 * Create a new LocationPicker
26248 * @param {Object} config The config object
26252 Roo.bootstrap.LocationPicker = function(config){
26254 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26259 * Fires when the picker initialized.
26260 * @param {Roo.bootstrap.LocationPicker} this
26261 * @param {Google Location} location
26265 * @event positionchanged
26266 * Fires when the picker position changed.
26267 * @param {Roo.bootstrap.LocationPicker} this
26268 * @param {Google Location} location
26270 positionchanged : true,
26273 * Fires when the map resize.
26274 * @param {Roo.bootstrap.LocationPicker} this
26279 * Fires when the map show.
26280 * @param {Roo.bootstrap.LocationPicker} this
26285 * Fires when the map hide.
26286 * @param {Roo.bootstrap.LocationPicker} this
26291 * Fires when click the map.
26292 * @param {Roo.bootstrap.LocationPicker} this
26293 * @param {Map event} e
26297 * @event mapRightClick
26298 * Fires when right click the map.
26299 * @param {Roo.bootstrap.LocationPicker} this
26300 * @param {Map event} e
26302 mapRightClick : true,
26304 * @event markerClick
26305 * Fires when click the marker.
26306 * @param {Roo.bootstrap.LocationPicker} this
26307 * @param {Map event} e
26309 markerClick : true,
26311 * @event markerRightClick
26312 * Fires when right click the marker.
26313 * @param {Roo.bootstrap.LocationPicker} this
26314 * @param {Map event} e
26316 markerRightClick : true,
26318 * @event OverlayViewDraw
26319 * Fires when OverlayView Draw
26320 * @param {Roo.bootstrap.LocationPicker} this
26322 OverlayViewDraw : true,
26324 * @event OverlayViewOnAdd
26325 * Fires when OverlayView Draw
26326 * @param {Roo.bootstrap.LocationPicker} this
26328 OverlayViewOnAdd : true,
26330 * @event OverlayViewOnRemove
26331 * Fires when OverlayView Draw
26332 * @param {Roo.bootstrap.LocationPicker} this
26334 OverlayViewOnRemove : true,
26336 * @event OverlayViewShow
26337 * Fires when OverlayView Draw
26338 * @param {Roo.bootstrap.LocationPicker} this
26339 * @param {Pixel} cpx
26341 OverlayViewShow : true,
26343 * @event OverlayViewHide
26344 * Fires when OverlayView Draw
26345 * @param {Roo.bootstrap.LocationPicker} this
26347 OverlayViewHide : true,
26349 * @event loadexception
26350 * Fires when load google lib failed.
26351 * @param {Roo.bootstrap.LocationPicker} this
26353 loadexception : true
26358 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26360 gMapContext: false,
26366 mapTypeControl: false,
26367 disableDoubleClickZoom: false,
26369 streetViewControl: false,
26373 enableAutocomplete: false,
26374 enableReverseGeocode: true,
26377 getAutoCreate: function()
26382 cls: 'roo-location-picker'
26388 initEvents: function(ct, position)
26390 if(!this.el.getWidth() || this.isApplied()){
26394 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26399 initial: function()
26401 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26402 this.fireEvent('loadexception', this);
26406 if(!this.mapTypeId){
26407 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26410 this.gMapContext = this.GMapContext();
26412 this.initOverlayView();
26414 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26418 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26419 _this.setPosition(_this.gMapContext.marker.position);
26422 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26423 _this.fireEvent('mapClick', this, event);
26427 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26428 _this.fireEvent('mapRightClick', this, event);
26432 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26433 _this.fireEvent('markerClick', this, event);
26437 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26438 _this.fireEvent('markerRightClick', this, event);
26442 this.setPosition(this.gMapContext.location);
26444 this.fireEvent('initial', this, this.gMapContext.location);
26447 initOverlayView: function()
26451 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26455 _this.fireEvent('OverlayViewDraw', _this);
26460 _this.fireEvent('OverlayViewOnAdd', _this);
26463 onRemove: function()
26465 _this.fireEvent('OverlayViewOnRemove', _this);
26468 show: function(cpx)
26470 _this.fireEvent('OverlayViewShow', _this, cpx);
26475 _this.fireEvent('OverlayViewHide', _this);
26481 fromLatLngToContainerPixel: function(event)
26483 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26486 isApplied: function()
26488 return this.getGmapContext() == false ? false : true;
26491 getGmapContext: function()
26493 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26496 GMapContext: function()
26498 var position = new google.maps.LatLng(this.latitude, this.longitude);
26500 var _map = new google.maps.Map(this.el.dom, {
26503 mapTypeId: this.mapTypeId,
26504 mapTypeControl: this.mapTypeControl,
26505 disableDoubleClickZoom: this.disableDoubleClickZoom,
26506 scrollwheel: this.scrollwheel,
26507 streetViewControl: this.streetViewControl,
26508 locationName: this.locationName,
26509 draggable: this.draggable,
26510 enableAutocomplete: this.enableAutocomplete,
26511 enableReverseGeocode: this.enableReverseGeocode
26514 var _marker = new google.maps.Marker({
26515 position: position,
26517 title: this.markerTitle,
26518 draggable: this.draggable
26525 location: position,
26526 radius: this.radius,
26527 locationName: this.locationName,
26528 addressComponents: {
26529 formatted_address: null,
26530 addressLine1: null,
26531 addressLine2: null,
26533 streetNumber: null,
26537 stateOrProvince: null
26540 domContainer: this.el.dom,
26541 geodecoder: new google.maps.Geocoder()
26545 drawCircle: function(center, radius, options)
26547 if (this.gMapContext.circle != null) {
26548 this.gMapContext.circle.setMap(null);
26552 options = Roo.apply({}, options, {
26553 strokeColor: "#0000FF",
26554 strokeOpacity: .35,
26556 fillColor: "#0000FF",
26560 options.map = this.gMapContext.map;
26561 options.radius = radius;
26562 options.center = center;
26563 this.gMapContext.circle = new google.maps.Circle(options);
26564 return this.gMapContext.circle;
26570 setPosition: function(location)
26572 this.gMapContext.location = location;
26573 this.gMapContext.marker.setPosition(location);
26574 this.gMapContext.map.panTo(location);
26575 this.drawCircle(location, this.gMapContext.radius, {});
26579 if (this.gMapContext.settings.enableReverseGeocode) {
26580 this.gMapContext.geodecoder.geocode({
26581 latLng: this.gMapContext.location
26582 }, function(results, status) {
26584 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26585 _this.gMapContext.locationName = results[0].formatted_address;
26586 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26588 _this.fireEvent('positionchanged', this, location);
26595 this.fireEvent('positionchanged', this, location);
26600 google.maps.event.trigger(this.gMapContext.map, "resize");
26602 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26604 this.fireEvent('resize', this);
26607 setPositionByLatLng: function(latitude, longitude)
26609 this.setPosition(new google.maps.LatLng(latitude, longitude));
26612 getCurrentPosition: function()
26615 latitude: this.gMapContext.location.lat(),
26616 longitude: this.gMapContext.location.lng()
26620 getAddressName: function()
26622 return this.gMapContext.locationName;
26625 getAddressComponents: function()
26627 return this.gMapContext.addressComponents;
26630 address_component_from_google_geocode: function(address_components)
26634 for (var i = 0; i < address_components.length; i++) {
26635 var component = address_components[i];
26636 if (component.types.indexOf("postal_code") >= 0) {
26637 result.postalCode = component.short_name;
26638 } else if (component.types.indexOf("street_number") >= 0) {
26639 result.streetNumber = component.short_name;
26640 } else if (component.types.indexOf("route") >= 0) {
26641 result.streetName = component.short_name;
26642 } else if (component.types.indexOf("neighborhood") >= 0) {
26643 result.city = component.short_name;
26644 } else if (component.types.indexOf("locality") >= 0) {
26645 result.city = component.short_name;
26646 } else if (component.types.indexOf("sublocality") >= 0) {
26647 result.district = component.short_name;
26648 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26649 result.stateOrProvince = component.short_name;
26650 } else if (component.types.indexOf("country") >= 0) {
26651 result.country = component.short_name;
26655 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26656 result.addressLine2 = "";
26660 setZoomLevel: function(zoom)
26662 this.gMapContext.map.setZoom(zoom);
26675 this.fireEvent('show', this);
26686 this.fireEvent('hide', this);
26691 Roo.apply(Roo.bootstrap.LocationPicker, {
26693 OverlayView : function(map, options)
26695 options = options || {};
26709 * @class Roo.bootstrap.Alert
26710 * @extends Roo.bootstrap.Component
26711 * Bootstrap Alert class
26712 * @cfg {String} title The title of alert
26713 * @cfg {String} html The content of alert
26714 * @cfg {String} weight ( success | info | warning | danger )
26715 * @cfg {String} faicon font-awesomeicon
26718 * Create a new alert
26719 * @param {Object} config The config object
26723 Roo.bootstrap.Alert = function(config){
26724 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26728 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26735 getAutoCreate : function()
26744 cls : 'roo-alert-icon'
26749 cls : 'roo-alert-title',
26754 cls : 'roo-alert-text',
26761 cfg.cn[0].cls += ' fa ' + this.faicon;
26765 cfg.cls += ' alert-' + this.weight;
26771 initEvents: function()
26773 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26776 setTitle : function(str)
26778 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26781 setText : function(str)
26783 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26786 setWeight : function(weight)
26789 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26792 this.weight = weight;
26794 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26797 setIcon : function(icon)
26800 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26803 this.faicon = icon;
26805 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26826 * @class Roo.bootstrap.UploadCropbox
26827 * @extends Roo.bootstrap.Component
26828 * Bootstrap UploadCropbox class
26829 * @cfg {String} emptyText show when image has been loaded
26830 * @cfg {String} rotateNotify show when image too small to rotate
26831 * @cfg {Number} errorTimeout default 3000
26832 * @cfg {Number} minWidth default 300
26833 * @cfg {Number} minHeight default 300
26834 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26835 * @cfg {Boolean} isDocument (true|false) default false
26836 * @cfg {String} url action url
26837 * @cfg {String} paramName default 'imageUpload'
26838 * @cfg {String} method default POST
26839 * @cfg {Boolean} loadMask (true|false) default true
26840 * @cfg {Boolean} loadingText default 'Loading...'
26843 * Create a new UploadCropbox
26844 * @param {Object} config The config object
26847 Roo.bootstrap.UploadCropbox = function(config){
26848 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26852 * @event beforeselectfile
26853 * Fire before select file
26854 * @param {Roo.bootstrap.UploadCropbox} this
26856 "beforeselectfile" : true,
26859 * Fire after initEvent
26860 * @param {Roo.bootstrap.UploadCropbox} this
26865 * Fire after initEvent
26866 * @param {Roo.bootstrap.UploadCropbox} this
26867 * @param {String} data
26872 * Fire when preparing the file data
26873 * @param {Roo.bootstrap.UploadCropbox} this
26874 * @param {Object} file
26879 * Fire when get exception
26880 * @param {Roo.bootstrap.UploadCropbox} this
26881 * @param {XMLHttpRequest} xhr
26883 "exception" : true,
26885 * @event beforeloadcanvas
26886 * Fire before load the canvas
26887 * @param {Roo.bootstrap.UploadCropbox} this
26888 * @param {String} src
26890 "beforeloadcanvas" : true,
26893 * Fire when trash image
26894 * @param {Roo.bootstrap.UploadCropbox} this
26899 * Fire when download the image
26900 * @param {Roo.bootstrap.UploadCropbox} this
26904 * @event footerbuttonclick
26905 * Fire when footerbuttonclick
26906 * @param {Roo.bootstrap.UploadCropbox} this
26907 * @param {String} type
26909 "footerbuttonclick" : true,
26913 * @param {Roo.bootstrap.UploadCropbox} this
26918 * Fire when rotate the image
26919 * @param {Roo.bootstrap.UploadCropbox} this
26920 * @param {String} pos
26925 * Fire when inspect the file
26926 * @param {Roo.bootstrap.UploadCropbox} this
26927 * @param {Object} file
26932 * Fire when xhr upload the file
26933 * @param {Roo.bootstrap.UploadCropbox} this
26934 * @param {Object} data
26939 * Fire when arrange the file data
26940 * @param {Roo.bootstrap.UploadCropbox} this
26941 * @param {Object} formData
26946 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26949 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26951 emptyText : 'Click to upload image',
26952 rotateNotify : 'Image is too small to rotate',
26953 errorTimeout : 3000,
26967 cropType : 'image/jpeg',
26969 canvasLoaded : false,
26970 isDocument : false,
26972 paramName : 'imageUpload',
26974 loadingText : 'Loading...',
26977 getAutoCreate : function()
26981 cls : 'roo-upload-cropbox',
26985 cls : 'roo-upload-cropbox-selector',
26990 cls : 'roo-upload-cropbox-body',
26991 style : 'cursor:pointer',
26995 cls : 'roo-upload-cropbox-preview'
26999 cls : 'roo-upload-cropbox-thumb'
27003 cls : 'roo-upload-cropbox-empty-notify',
27004 html : this.emptyText
27008 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27009 html : this.rotateNotify
27015 cls : 'roo-upload-cropbox-footer',
27018 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27028 onRender : function(ct, position)
27030 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27032 if (this.buttons.length) {
27034 Roo.each(this.buttons, function(bb) {
27036 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27038 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27044 this.maskEl = this.el;
27048 initEvents : function()
27050 this.urlAPI = (window.createObjectURL && window) ||
27051 (window.URL && URL.revokeObjectURL && URL) ||
27052 (window.webkitURL && webkitURL);
27054 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27055 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27057 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27058 this.selectorEl.hide();
27060 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27061 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27063 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27064 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27065 this.thumbEl.hide();
27067 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27068 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27070 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27071 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27072 this.errorEl.hide();
27074 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27075 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27076 this.footerEl.hide();
27078 this.setThumbBoxSize();
27084 this.fireEvent('initial', this);
27091 window.addEventListener("resize", function() { _this.resize(); } );
27093 this.bodyEl.on('click', this.beforeSelectFile, this);
27096 this.bodyEl.on('touchstart', this.onTouchStart, this);
27097 this.bodyEl.on('touchmove', this.onTouchMove, this);
27098 this.bodyEl.on('touchend', this.onTouchEnd, this);
27102 this.bodyEl.on('mousedown', this.onMouseDown, this);
27103 this.bodyEl.on('mousemove', this.onMouseMove, this);
27104 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27105 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27106 Roo.get(document).on('mouseup', this.onMouseUp, this);
27109 this.selectorEl.on('change', this.onFileSelected, this);
27115 this.baseScale = 1;
27117 this.baseRotate = 1;
27118 this.dragable = false;
27119 this.pinching = false;
27122 this.cropData = false;
27123 this.notifyEl.dom.innerHTML = this.emptyText;
27125 this.selectorEl.dom.value = '';
27129 resize : function()
27131 if(this.fireEvent('resize', this) != false){
27132 this.setThumbBoxPosition();
27133 this.setCanvasPosition();
27137 onFooterButtonClick : function(e, el, o, type)
27140 case 'rotate-left' :
27141 this.onRotateLeft(e);
27143 case 'rotate-right' :
27144 this.onRotateRight(e);
27147 this.beforeSelectFile(e);
27162 this.fireEvent('footerbuttonclick', this, type);
27165 beforeSelectFile : function(e)
27167 e.preventDefault();
27169 if(this.fireEvent('beforeselectfile', this) != false){
27170 this.selectorEl.dom.click();
27174 onFileSelected : function(e)
27176 e.preventDefault();
27178 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27182 var file = this.selectorEl.dom.files[0];
27184 if(this.fireEvent('inspect', this, file) != false){
27185 this.prepare(file);
27190 trash : function(e)
27192 this.fireEvent('trash', this);
27195 download : function(e)
27197 this.fireEvent('download', this);
27200 loadCanvas : function(src)
27202 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27206 this.imageEl = document.createElement('img');
27210 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27212 this.imageEl.src = src;
27216 onLoadCanvas : function()
27218 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27219 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27221 this.bodyEl.un('click', this.beforeSelectFile, this);
27223 this.notifyEl.hide();
27224 this.thumbEl.show();
27225 this.footerEl.show();
27227 this.baseRotateLevel();
27229 if(this.isDocument){
27230 this.setThumbBoxSize();
27233 this.setThumbBoxPosition();
27235 this.baseScaleLevel();
27241 this.canvasLoaded = true;
27244 this.maskEl.unmask();
27249 setCanvasPosition : function()
27251 if(!this.canvasEl){
27255 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27256 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27258 this.previewEl.setLeft(pw);
27259 this.previewEl.setTop(ph);
27263 onMouseDown : function(e)
27267 this.dragable = true;
27268 this.pinching = false;
27270 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27271 this.dragable = false;
27275 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27276 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27280 onMouseMove : function(e)
27284 if(!this.canvasLoaded){
27288 if (!this.dragable){
27292 var minX = Math.ceil(this.thumbEl.getLeft(true));
27293 var minY = Math.ceil(this.thumbEl.getTop(true));
27295 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27296 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27298 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27299 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27301 x = x - this.mouseX;
27302 y = y - this.mouseY;
27304 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27305 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27307 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27308 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27310 this.previewEl.setLeft(bgX);
27311 this.previewEl.setTop(bgY);
27313 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27314 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27317 onMouseUp : function(e)
27321 this.dragable = false;
27324 onMouseWheel : function(e)
27328 this.startScale = this.scale;
27330 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27332 if(!this.zoomable()){
27333 this.scale = this.startScale;
27342 zoomable : function()
27344 var minScale = this.thumbEl.getWidth() / this.minWidth;
27346 if(this.minWidth < this.minHeight){
27347 minScale = this.thumbEl.getHeight() / this.minHeight;
27350 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27351 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27355 (this.rotate == 0 || this.rotate == 180) &&
27357 width > this.imageEl.OriginWidth ||
27358 height > this.imageEl.OriginHeight ||
27359 (width < this.minWidth && height < this.minHeight)
27367 (this.rotate == 90 || this.rotate == 270) &&
27369 width > this.imageEl.OriginWidth ||
27370 height > this.imageEl.OriginHeight ||
27371 (width < this.minHeight && height < this.minWidth)
27378 !this.isDocument &&
27379 (this.rotate == 0 || this.rotate == 180) &&
27381 width < this.minWidth ||
27382 width > this.imageEl.OriginWidth ||
27383 height < this.minHeight ||
27384 height > this.imageEl.OriginHeight
27391 !this.isDocument &&
27392 (this.rotate == 90 || this.rotate == 270) &&
27394 width < this.minHeight ||
27395 width > this.imageEl.OriginWidth ||
27396 height < this.minWidth ||
27397 height > this.imageEl.OriginHeight
27407 onRotateLeft : function(e)
27409 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27411 var minScale = this.thumbEl.getWidth() / this.minWidth;
27413 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27414 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27416 this.startScale = this.scale;
27418 while (this.getScaleLevel() < minScale){
27420 this.scale = this.scale + 1;
27422 if(!this.zoomable()){
27427 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27428 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27433 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27440 this.scale = this.startScale;
27442 this.onRotateFail();
27447 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27449 if(this.isDocument){
27450 this.setThumbBoxSize();
27451 this.setThumbBoxPosition();
27452 this.setCanvasPosition();
27457 this.fireEvent('rotate', this, 'left');
27461 onRotateRight : function(e)
27463 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27465 var minScale = this.thumbEl.getWidth() / this.minWidth;
27467 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27468 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27470 this.startScale = this.scale;
27472 while (this.getScaleLevel() < minScale){
27474 this.scale = this.scale + 1;
27476 if(!this.zoomable()){
27481 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27482 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27487 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27494 this.scale = this.startScale;
27496 this.onRotateFail();
27501 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27503 if(this.isDocument){
27504 this.setThumbBoxSize();
27505 this.setThumbBoxPosition();
27506 this.setCanvasPosition();
27511 this.fireEvent('rotate', this, 'right');
27514 onRotateFail : function()
27516 this.errorEl.show(true);
27520 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27525 this.previewEl.dom.innerHTML = '';
27527 var canvasEl = document.createElement("canvas");
27529 var contextEl = canvasEl.getContext("2d");
27531 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27532 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27533 var center = this.imageEl.OriginWidth / 2;
27535 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27536 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27537 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27538 center = this.imageEl.OriginHeight / 2;
27541 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27543 contextEl.translate(center, center);
27544 contextEl.rotate(this.rotate * Math.PI / 180);
27546 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27548 this.canvasEl = document.createElement("canvas");
27550 this.contextEl = this.canvasEl.getContext("2d");
27552 switch (this.rotate) {
27555 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27556 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27558 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27563 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27564 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27566 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27567 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);
27571 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27576 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27577 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27579 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27580 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);
27584 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);
27589 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27590 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27592 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27593 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27597 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);
27604 this.previewEl.appendChild(this.canvasEl);
27606 this.setCanvasPosition();
27611 if(!this.canvasLoaded){
27615 var imageCanvas = document.createElement("canvas");
27617 var imageContext = imageCanvas.getContext("2d");
27619 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27620 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27622 var center = imageCanvas.width / 2;
27624 imageContext.translate(center, center);
27626 imageContext.rotate(this.rotate * Math.PI / 180);
27628 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27630 var canvas = document.createElement("canvas");
27632 var context = canvas.getContext("2d");
27634 canvas.width = this.minWidth;
27635 canvas.height = this.minHeight;
27637 switch (this.rotate) {
27640 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27641 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27643 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27644 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27646 var targetWidth = this.minWidth - 2 * x;
27647 var targetHeight = this.minHeight - 2 * y;
27651 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27652 scale = targetWidth / width;
27655 if(x > 0 && y == 0){
27656 scale = targetHeight / height;
27659 if(x > 0 && y > 0){
27660 scale = targetWidth / width;
27662 if(width < height){
27663 scale = targetHeight / height;
27667 context.scale(scale, scale);
27669 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27670 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27672 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27673 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27675 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27680 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27681 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27683 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27684 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27686 var targetWidth = this.minWidth - 2 * x;
27687 var targetHeight = this.minHeight - 2 * y;
27691 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27692 scale = targetWidth / width;
27695 if(x > 0 && y == 0){
27696 scale = targetHeight / height;
27699 if(x > 0 && y > 0){
27700 scale = targetWidth / width;
27702 if(width < height){
27703 scale = targetHeight / height;
27707 context.scale(scale, scale);
27709 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27710 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27712 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27713 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27715 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27717 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27722 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27723 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27725 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27726 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27728 var targetWidth = this.minWidth - 2 * x;
27729 var targetHeight = this.minHeight - 2 * y;
27733 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27734 scale = targetWidth / width;
27737 if(x > 0 && y == 0){
27738 scale = targetHeight / height;
27741 if(x > 0 && y > 0){
27742 scale = targetWidth / width;
27744 if(width < height){
27745 scale = targetHeight / height;
27749 context.scale(scale, scale);
27751 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27752 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27754 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27755 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27757 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27758 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27760 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27765 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27766 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27768 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27769 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27771 var targetWidth = this.minWidth - 2 * x;
27772 var targetHeight = this.minHeight - 2 * y;
27776 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27777 scale = targetWidth / width;
27780 if(x > 0 && y == 0){
27781 scale = targetHeight / height;
27784 if(x > 0 && y > 0){
27785 scale = targetWidth / width;
27787 if(width < height){
27788 scale = targetHeight / height;
27792 context.scale(scale, scale);
27794 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27795 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27797 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27798 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27800 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27802 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27809 this.cropData = canvas.toDataURL(this.cropType);
27811 if(this.fireEvent('crop', this, this.cropData) !== false){
27812 this.process(this.file, this.cropData);
27819 setThumbBoxSize : function()
27823 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27824 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27825 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27827 this.minWidth = width;
27828 this.minHeight = height;
27830 if(this.rotate == 90 || this.rotate == 270){
27831 this.minWidth = height;
27832 this.minHeight = width;
27837 width = Math.ceil(this.minWidth * height / this.minHeight);
27839 if(this.minWidth > this.minHeight){
27841 height = Math.ceil(this.minHeight * width / this.minWidth);
27844 this.thumbEl.setStyle({
27845 width : width + 'px',
27846 height : height + 'px'
27853 setThumbBoxPosition : function()
27855 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27856 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27858 this.thumbEl.setLeft(x);
27859 this.thumbEl.setTop(y);
27863 baseRotateLevel : function()
27865 this.baseRotate = 1;
27868 typeof(this.exif) != 'undefined' &&
27869 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27870 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27872 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27875 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27879 baseScaleLevel : function()
27883 if(this.isDocument){
27885 if(this.baseRotate == 6 || this.baseRotate == 8){
27887 height = this.thumbEl.getHeight();
27888 this.baseScale = height / this.imageEl.OriginWidth;
27890 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27891 width = this.thumbEl.getWidth();
27892 this.baseScale = width / this.imageEl.OriginHeight;
27898 height = this.thumbEl.getHeight();
27899 this.baseScale = height / this.imageEl.OriginHeight;
27901 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27902 width = this.thumbEl.getWidth();
27903 this.baseScale = width / this.imageEl.OriginWidth;
27909 if(this.baseRotate == 6 || this.baseRotate == 8){
27911 width = this.thumbEl.getHeight();
27912 this.baseScale = width / this.imageEl.OriginHeight;
27914 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27915 height = this.thumbEl.getWidth();
27916 this.baseScale = height / this.imageEl.OriginHeight;
27919 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27920 height = this.thumbEl.getWidth();
27921 this.baseScale = height / this.imageEl.OriginHeight;
27923 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27924 width = this.thumbEl.getHeight();
27925 this.baseScale = width / this.imageEl.OriginWidth;
27932 width = this.thumbEl.getWidth();
27933 this.baseScale = width / this.imageEl.OriginWidth;
27935 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27936 height = this.thumbEl.getHeight();
27937 this.baseScale = height / this.imageEl.OriginHeight;
27940 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27942 height = this.thumbEl.getHeight();
27943 this.baseScale = height / this.imageEl.OriginHeight;
27945 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27946 width = this.thumbEl.getWidth();
27947 this.baseScale = width / this.imageEl.OriginWidth;
27955 getScaleLevel : function()
27957 return this.baseScale * Math.pow(1.1, this.scale);
27960 onTouchStart : function(e)
27962 if(!this.canvasLoaded){
27963 this.beforeSelectFile(e);
27967 var touches = e.browserEvent.touches;
27973 if(touches.length == 1){
27974 this.onMouseDown(e);
27978 if(touches.length != 2){
27984 for(var i = 0, finger; finger = touches[i]; i++){
27985 coords.push(finger.pageX, finger.pageY);
27988 var x = Math.pow(coords[0] - coords[2], 2);
27989 var y = Math.pow(coords[1] - coords[3], 2);
27991 this.startDistance = Math.sqrt(x + y);
27993 this.startScale = this.scale;
27995 this.pinching = true;
27996 this.dragable = false;
28000 onTouchMove : function(e)
28002 if(!this.pinching && !this.dragable){
28006 var touches = e.browserEvent.touches;
28013 this.onMouseMove(e);
28019 for(var i = 0, finger; finger = touches[i]; i++){
28020 coords.push(finger.pageX, finger.pageY);
28023 var x = Math.pow(coords[0] - coords[2], 2);
28024 var y = Math.pow(coords[1] - coords[3], 2);
28026 this.endDistance = Math.sqrt(x + y);
28028 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28030 if(!this.zoomable()){
28031 this.scale = this.startScale;
28039 onTouchEnd : function(e)
28041 this.pinching = false;
28042 this.dragable = false;
28046 process : function(file, crop)
28049 this.maskEl.mask(this.loadingText);
28052 this.xhr = new XMLHttpRequest();
28054 file.xhr = this.xhr;
28056 this.xhr.open(this.method, this.url, true);
28059 "Accept": "application/json",
28060 "Cache-Control": "no-cache",
28061 "X-Requested-With": "XMLHttpRequest"
28064 for (var headerName in headers) {
28065 var headerValue = headers[headerName];
28067 this.xhr.setRequestHeader(headerName, headerValue);
28073 this.xhr.onload = function()
28075 _this.xhrOnLoad(_this.xhr);
28078 this.xhr.onerror = function()
28080 _this.xhrOnError(_this.xhr);
28083 var formData = new FormData();
28085 formData.append('returnHTML', 'NO');
28088 formData.append('crop', crop);
28091 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28092 formData.append(this.paramName, file, file.name);
28095 if(typeof(file.filename) != 'undefined'){
28096 formData.append('filename', file.filename);
28099 if(typeof(file.mimetype) != 'undefined'){
28100 formData.append('mimetype', file.mimetype);
28103 if(this.fireEvent('arrange', this, formData) != false){
28104 this.xhr.send(formData);
28108 xhrOnLoad : function(xhr)
28111 this.maskEl.unmask();
28114 if (xhr.readyState !== 4) {
28115 this.fireEvent('exception', this, xhr);
28119 var response = Roo.decode(xhr.responseText);
28121 if(!response.success){
28122 this.fireEvent('exception', this, xhr);
28126 var response = Roo.decode(xhr.responseText);
28128 this.fireEvent('upload', this, response);
28132 xhrOnError : function()
28135 this.maskEl.unmask();
28138 Roo.log('xhr on error');
28140 var response = Roo.decode(xhr.responseText);
28146 prepare : function(file)
28149 this.maskEl.mask(this.loadingText);
28155 if(typeof(file) === 'string'){
28156 this.loadCanvas(file);
28160 if(!file || !this.urlAPI){
28165 this.cropType = file.type;
28169 if(this.fireEvent('prepare', this, this.file) != false){
28171 var reader = new FileReader();
28173 reader.onload = function (e) {
28174 if (e.target.error) {
28175 Roo.log(e.target.error);
28179 var buffer = e.target.result,
28180 dataView = new DataView(buffer),
28182 maxOffset = dataView.byteLength - 4,
28186 if (dataView.getUint16(0) === 0xffd8) {
28187 while (offset < maxOffset) {
28188 markerBytes = dataView.getUint16(offset);
28190 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28191 markerLength = dataView.getUint16(offset + 2) + 2;
28192 if (offset + markerLength > dataView.byteLength) {
28193 Roo.log('Invalid meta data: Invalid segment size.');
28197 if(markerBytes == 0xffe1){
28198 _this.parseExifData(
28205 offset += markerLength;
28215 var url = _this.urlAPI.createObjectURL(_this.file);
28217 _this.loadCanvas(url);
28222 reader.readAsArrayBuffer(this.file);
28228 parseExifData : function(dataView, offset, length)
28230 var tiffOffset = offset + 10,
28234 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28235 // No Exif data, might be XMP data instead
28239 // Check for the ASCII code for "Exif" (0x45786966):
28240 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28241 // No Exif data, might be XMP data instead
28244 if (tiffOffset + 8 > dataView.byteLength) {
28245 Roo.log('Invalid Exif data: Invalid segment size.');
28248 // Check for the two null bytes:
28249 if (dataView.getUint16(offset + 8) !== 0x0000) {
28250 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28253 // Check the byte alignment:
28254 switch (dataView.getUint16(tiffOffset)) {
28256 littleEndian = true;
28259 littleEndian = false;
28262 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28265 // Check for the TIFF tag marker (0x002A):
28266 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28267 Roo.log('Invalid Exif data: Missing TIFF marker.');
28270 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28271 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28273 this.parseExifTags(
28276 tiffOffset + dirOffset,
28281 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28286 if (dirOffset + 6 > dataView.byteLength) {
28287 Roo.log('Invalid Exif data: Invalid directory offset.');
28290 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28291 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28292 if (dirEndOffset + 4 > dataView.byteLength) {
28293 Roo.log('Invalid Exif data: Invalid directory size.');
28296 for (i = 0; i < tagsNumber; i += 1) {
28300 dirOffset + 2 + 12 * i, // tag offset
28304 // Return the offset to the next directory:
28305 return dataView.getUint32(dirEndOffset, littleEndian);
28308 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28310 var tag = dataView.getUint16(offset, littleEndian);
28312 this.exif[tag] = this.getExifValue(
28316 dataView.getUint16(offset + 2, littleEndian), // tag type
28317 dataView.getUint32(offset + 4, littleEndian), // tag length
28322 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28324 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28333 Roo.log('Invalid Exif data: Invalid tag type.');
28337 tagSize = tagType.size * length;
28338 // Determine if the value is contained in the dataOffset bytes,
28339 // or if the value at the dataOffset is a pointer to the actual data:
28340 dataOffset = tagSize > 4 ?
28341 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28342 if (dataOffset + tagSize > dataView.byteLength) {
28343 Roo.log('Invalid Exif data: Invalid data offset.');
28346 if (length === 1) {
28347 return tagType.getValue(dataView, dataOffset, littleEndian);
28350 for (i = 0; i < length; i += 1) {
28351 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28354 if (tagType.ascii) {
28356 // Concatenate the chars:
28357 for (i = 0; i < values.length; i += 1) {
28359 // Ignore the terminating NULL byte(s):
28360 if (c === '\u0000') {
28372 Roo.apply(Roo.bootstrap.UploadCropbox, {
28374 'Orientation': 0x0112
28378 1: 0, //'top-left',
28380 3: 180, //'bottom-right',
28381 // 4: 'bottom-left',
28383 6: 90, //'right-top',
28384 // 7: 'right-bottom',
28385 8: 270 //'left-bottom'
28389 // byte, 8-bit unsigned int:
28391 getValue: function (dataView, dataOffset) {
28392 return dataView.getUint8(dataOffset);
28396 // ascii, 8-bit byte:
28398 getValue: function (dataView, dataOffset) {
28399 return String.fromCharCode(dataView.getUint8(dataOffset));
28404 // short, 16 bit int:
28406 getValue: function (dataView, dataOffset, littleEndian) {
28407 return dataView.getUint16(dataOffset, littleEndian);
28411 // long, 32 bit int:
28413 getValue: function (dataView, dataOffset, littleEndian) {
28414 return dataView.getUint32(dataOffset, littleEndian);
28418 // rational = two long values, first is numerator, second is denominator:
28420 getValue: function (dataView, dataOffset, littleEndian) {
28421 return dataView.getUint32(dataOffset, littleEndian) /
28422 dataView.getUint32(dataOffset + 4, littleEndian);
28426 // slong, 32 bit signed int:
28428 getValue: function (dataView, dataOffset, littleEndian) {
28429 return dataView.getInt32(dataOffset, littleEndian);
28433 // srational, two slongs, first is numerator, second is denominator:
28435 getValue: function (dataView, dataOffset, littleEndian) {
28436 return dataView.getInt32(dataOffset, littleEndian) /
28437 dataView.getInt32(dataOffset + 4, littleEndian);
28447 cls : 'btn-group roo-upload-cropbox-rotate-left',
28448 action : 'rotate-left',
28452 cls : 'btn btn-default',
28453 html : '<i class="fa fa-undo"></i>'
28459 cls : 'btn-group roo-upload-cropbox-picture',
28460 action : 'picture',
28464 cls : 'btn btn-default',
28465 html : '<i class="fa fa-picture-o"></i>'
28471 cls : 'btn-group roo-upload-cropbox-rotate-right',
28472 action : 'rotate-right',
28476 cls : 'btn btn-default',
28477 html : '<i class="fa fa-repeat"></i>'
28485 cls : 'btn-group roo-upload-cropbox-rotate-left',
28486 action : 'rotate-left',
28490 cls : 'btn btn-default',
28491 html : '<i class="fa fa-undo"></i>'
28497 cls : 'btn-group roo-upload-cropbox-download',
28498 action : 'download',
28502 cls : 'btn btn-default',
28503 html : '<i class="fa fa-download"></i>'
28509 cls : 'btn-group roo-upload-cropbox-crop',
28514 cls : 'btn btn-default',
28515 html : '<i class="fa fa-crop"></i>'
28521 cls : 'btn-group roo-upload-cropbox-trash',
28526 cls : 'btn btn-default',
28527 html : '<i class="fa fa-trash"></i>'
28533 cls : 'btn-group roo-upload-cropbox-rotate-right',
28534 action : 'rotate-right',
28538 cls : 'btn btn-default',
28539 html : '<i class="fa fa-repeat"></i>'
28547 cls : 'btn-group roo-upload-cropbox-rotate-left',
28548 action : 'rotate-left',
28552 cls : 'btn btn-default',
28553 html : '<i class="fa fa-undo"></i>'
28559 cls : 'btn-group roo-upload-cropbox-rotate-right',
28560 action : 'rotate-right',
28564 cls : 'btn btn-default',
28565 html : '<i class="fa fa-repeat"></i>'
28578 * @class Roo.bootstrap.DocumentManager
28579 * @extends Roo.bootstrap.Component
28580 * Bootstrap DocumentManager class
28581 * @cfg {String} paramName default 'imageUpload'
28582 * @cfg {String} toolTipName default 'filename'
28583 * @cfg {String} method default POST
28584 * @cfg {String} url action url
28585 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28586 * @cfg {Boolean} multiple multiple upload default true
28587 * @cfg {Number} thumbSize default 300
28588 * @cfg {String} fieldLabel
28589 * @cfg {Number} labelWidth default 4
28590 * @cfg {String} labelAlign (left|top) default left
28591 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28592 * @cfg {Number} labellg set the width of label (1-12)
28593 * @cfg {Number} labelmd set the width of label (1-12)
28594 * @cfg {Number} labelsm set the width of label (1-12)
28595 * @cfg {Number} labelxs set the width of label (1-12)
28598 * Create a new DocumentManager
28599 * @param {Object} config The config object
28602 Roo.bootstrap.DocumentManager = function(config){
28603 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28606 this.delegates = [];
28611 * Fire when initial the DocumentManager
28612 * @param {Roo.bootstrap.DocumentManager} this
28617 * inspect selected file
28618 * @param {Roo.bootstrap.DocumentManager} this
28619 * @param {File} file
28624 * Fire when xhr load exception
28625 * @param {Roo.bootstrap.DocumentManager} this
28626 * @param {XMLHttpRequest} xhr
28628 "exception" : true,
28630 * @event afterupload
28631 * Fire when xhr load exception
28632 * @param {Roo.bootstrap.DocumentManager} this
28633 * @param {XMLHttpRequest} xhr
28635 "afterupload" : true,
28638 * prepare the form data
28639 * @param {Roo.bootstrap.DocumentManager} this
28640 * @param {Object} formData
28645 * Fire when remove the file
28646 * @param {Roo.bootstrap.DocumentManager} this
28647 * @param {Object} file
28652 * Fire after refresh the file
28653 * @param {Roo.bootstrap.DocumentManager} this
28658 * Fire after click the image
28659 * @param {Roo.bootstrap.DocumentManager} this
28660 * @param {Object} file
28665 * Fire when upload a image and editable set to true
28666 * @param {Roo.bootstrap.DocumentManager} this
28667 * @param {Object} file
28671 * @event beforeselectfile
28672 * Fire before select file
28673 * @param {Roo.bootstrap.DocumentManager} this
28675 "beforeselectfile" : true,
28678 * Fire before process file
28679 * @param {Roo.bootstrap.DocumentManager} this
28680 * @param {Object} file
28684 * @event previewrendered
28685 * Fire when preview rendered
28686 * @param {Roo.bootstrap.DocumentManager} this
28687 * @param {Object} file
28689 "previewrendered" : true
28694 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28703 paramName : 'imageUpload',
28704 toolTipName : 'filename',
28707 labelAlign : 'left',
28717 getAutoCreate : function()
28719 var managerWidget = {
28721 cls : 'roo-document-manager',
28725 cls : 'roo-document-manager-selector',
28730 cls : 'roo-document-manager-uploader',
28734 cls : 'roo-document-manager-upload-btn',
28735 html : '<i class="fa fa-plus"></i>'
28746 cls : 'column col-md-12',
28751 if(this.fieldLabel.length){
28756 cls : 'column col-md-12',
28757 html : this.fieldLabel
28761 cls : 'column col-md-12',
28766 if(this.labelAlign == 'left'){
28771 html : this.fieldLabel
28780 if(this.labelWidth > 12){
28781 content[0].style = "width: " + this.labelWidth + 'px';
28784 if(this.labelWidth < 13 && this.labelmd == 0){
28785 this.labelmd = this.labelWidth;
28788 if(this.labellg > 0){
28789 content[0].cls += ' col-lg-' + this.labellg;
28790 content[1].cls += ' col-lg-' + (12 - this.labellg);
28793 if(this.labelmd > 0){
28794 content[0].cls += ' col-md-' + this.labelmd;
28795 content[1].cls += ' col-md-' + (12 - this.labelmd);
28798 if(this.labelsm > 0){
28799 content[0].cls += ' col-sm-' + this.labelsm;
28800 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28803 if(this.labelxs > 0){
28804 content[0].cls += ' col-xs-' + this.labelxs;
28805 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28813 cls : 'row clearfix',
28821 initEvents : function()
28823 this.managerEl = this.el.select('.roo-document-manager', true).first();
28824 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28826 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28827 this.selectorEl.hide();
28830 this.selectorEl.attr('multiple', 'multiple');
28833 this.selectorEl.on('change', this.onFileSelected, this);
28835 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28836 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28838 this.uploader.on('click', this.onUploaderClick, this);
28840 this.renderProgressDialog();
28844 window.addEventListener("resize", function() { _this.refresh(); } );
28846 this.fireEvent('initial', this);
28849 renderProgressDialog : function()
28853 this.progressDialog = new Roo.bootstrap.Modal({
28854 cls : 'roo-document-manager-progress-dialog',
28855 allow_close : false,
28865 btnclick : function() {
28866 _this.uploadCancel();
28872 this.progressDialog.render(Roo.get(document.body));
28874 this.progress = new Roo.bootstrap.Progress({
28875 cls : 'roo-document-manager-progress',
28880 this.progress.render(this.progressDialog.getChildContainer());
28882 this.progressBar = new Roo.bootstrap.ProgressBar({
28883 cls : 'roo-document-manager-progress-bar',
28886 aria_valuemax : 12,
28890 this.progressBar.render(this.progress.getChildContainer());
28893 onUploaderClick : function(e)
28895 e.preventDefault();
28897 if(this.fireEvent('beforeselectfile', this) != false){
28898 this.selectorEl.dom.click();
28903 onFileSelected : function(e)
28905 e.preventDefault();
28907 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28911 Roo.each(this.selectorEl.dom.files, function(file){
28912 if(this.fireEvent('inspect', this, file) != false){
28913 this.files.push(file);
28923 this.selectorEl.dom.value = '';
28925 if(!this.files || !this.files.length){
28929 if(this.boxes > 0 && this.files.length > this.boxes){
28930 this.files = this.files.slice(0, this.boxes);
28933 this.uploader.show();
28935 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28936 this.uploader.hide();
28945 Roo.each(this.files, function(file){
28947 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28948 var f = this.renderPreview(file);
28953 if(file.type.indexOf('image') != -1){
28954 this.delegates.push(
28956 _this.process(file);
28957 }).createDelegate(this)
28965 _this.process(file);
28966 }).createDelegate(this)
28971 this.files = files;
28973 this.delegates = this.delegates.concat(docs);
28975 if(!this.delegates.length){
28980 this.progressBar.aria_valuemax = this.delegates.length;
28987 arrange : function()
28989 if(!this.delegates.length){
28990 this.progressDialog.hide();
28995 var delegate = this.delegates.shift();
28997 this.progressDialog.show();
28999 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29001 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29006 refresh : function()
29008 this.uploader.show();
29010 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29011 this.uploader.hide();
29014 Roo.isTouch ? this.closable(false) : this.closable(true);
29016 this.fireEvent('refresh', this);
29019 onRemove : function(e, el, o)
29021 e.preventDefault();
29023 this.fireEvent('remove', this, o);
29027 remove : function(o)
29031 Roo.each(this.files, function(file){
29032 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29041 this.files = files;
29048 Roo.each(this.files, function(file){
29053 file.target.remove();
29062 onClick : function(e, el, o)
29064 e.preventDefault();
29066 this.fireEvent('click', this, o);
29070 closable : function(closable)
29072 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29074 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29086 xhrOnLoad : function(xhr)
29088 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29092 if (xhr.readyState !== 4) {
29094 this.fireEvent('exception', this, xhr);
29098 var response = Roo.decode(xhr.responseText);
29100 if(!response.success){
29102 this.fireEvent('exception', this, xhr);
29106 var file = this.renderPreview(response.data);
29108 this.files.push(file);
29112 this.fireEvent('afterupload', this, xhr);
29116 xhrOnError : function(xhr)
29118 Roo.log('xhr on error');
29120 var response = Roo.decode(xhr.responseText);
29127 process : function(file)
29129 if(this.fireEvent('process', this, file) !== false){
29130 if(this.editable && file.type.indexOf('image') != -1){
29131 this.fireEvent('edit', this, file);
29135 this.uploadStart(file, false);
29142 uploadStart : function(file, crop)
29144 this.xhr = new XMLHttpRequest();
29146 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29151 file.xhr = this.xhr;
29153 this.managerEl.createChild({
29155 cls : 'roo-document-manager-loading',
29159 tooltip : file.name,
29160 cls : 'roo-document-manager-thumb',
29161 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29167 this.xhr.open(this.method, this.url, true);
29170 "Accept": "application/json",
29171 "Cache-Control": "no-cache",
29172 "X-Requested-With": "XMLHttpRequest"
29175 for (var headerName in headers) {
29176 var headerValue = headers[headerName];
29178 this.xhr.setRequestHeader(headerName, headerValue);
29184 this.xhr.onload = function()
29186 _this.xhrOnLoad(_this.xhr);
29189 this.xhr.onerror = function()
29191 _this.xhrOnError(_this.xhr);
29194 var formData = new FormData();
29196 formData.append('returnHTML', 'NO');
29199 formData.append('crop', crop);
29202 formData.append(this.paramName, file, file.name);
29209 if(this.fireEvent('prepare', this, formData, options) != false){
29211 if(options.manually){
29215 this.xhr.send(formData);
29219 this.uploadCancel();
29222 uploadCancel : function()
29228 this.delegates = [];
29230 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29237 renderPreview : function(file)
29239 if(typeof(file.target) != 'undefined' && file.target){
29243 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29245 var previewEl = this.managerEl.createChild({
29247 cls : 'roo-document-manager-preview',
29251 tooltip : file[this.toolTipName],
29252 cls : 'roo-document-manager-thumb',
29253 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29258 html : '<i class="fa fa-times-circle"></i>'
29263 var close = previewEl.select('button.close', true).first();
29265 close.on('click', this.onRemove, this, file);
29267 file.target = previewEl;
29269 var image = previewEl.select('img', true).first();
29273 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29275 image.on('click', this.onClick, this, file);
29277 this.fireEvent('previewrendered', this, file);
29283 onPreviewLoad : function(file, image)
29285 if(typeof(file.target) == 'undefined' || !file.target){
29289 var width = image.dom.naturalWidth || image.dom.width;
29290 var height = image.dom.naturalHeight || image.dom.height;
29292 if(width > height){
29293 file.target.addClass('wide');
29297 file.target.addClass('tall');
29302 uploadFromSource : function(file, crop)
29304 this.xhr = new XMLHttpRequest();
29306 this.managerEl.createChild({
29308 cls : 'roo-document-manager-loading',
29312 tooltip : file.name,
29313 cls : 'roo-document-manager-thumb',
29314 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29320 this.xhr.open(this.method, this.url, true);
29323 "Accept": "application/json",
29324 "Cache-Control": "no-cache",
29325 "X-Requested-With": "XMLHttpRequest"
29328 for (var headerName in headers) {
29329 var headerValue = headers[headerName];
29331 this.xhr.setRequestHeader(headerName, headerValue);
29337 this.xhr.onload = function()
29339 _this.xhrOnLoad(_this.xhr);
29342 this.xhr.onerror = function()
29344 _this.xhrOnError(_this.xhr);
29347 var formData = new FormData();
29349 formData.append('returnHTML', 'NO');
29351 formData.append('crop', crop);
29353 if(typeof(file.filename) != 'undefined'){
29354 formData.append('filename', file.filename);
29357 if(typeof(file.mimetype) != 'undefined'){
29358 formData.append('mimetype', file.mimetype);
29363 if(this.fireEvent('prepare', this, formData) != false){
29364 this.xhr.send(formData);
29374 * @class Roo.bootstrap.DocumentViewer
29375 * @extends Roo.bootstrap.Component
29376 * Bootstrap DocumentViewer class
29377 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29378 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29381 * Create a new DocumentViewer
29382 * @param {Object} config The config object
29385 Roo.bootstrap.DocumentViewer = function(config){
29386 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29391 * Fire after initEvent
29392 * @param {Roo.bootstrap.DocumentViewer} this
29398 * @param {Roo.bootstrap.DocumentViewer} this
29403 * Fire after download button
29404 * @param {Roo.bootstrap.DocumentViewer} this
29409 * Fire after trash button
29410 * @param {Roo.bootstrap.DocumentViewer} this
29417 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29419 showDownload : true,
29423 getAutoCreate : function()
29427 cls : 'roo-document-viewer',
29431 cls : 'roo-document-viewer-body',
29435 cls : 'roo-document-viewer-thumb',
29439 cls : 'roo-document-viewer-image'
29447 cls : 'roo-document-viewer-footer',
29450 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29454 cls : 'btn-group roo-document-viewer-download',
29458 cls : 'btn btn-default',
29459 html : '<i class="fa fa-download"></i>'
29465 cls : 'btn-group roo-document-viewer-trash',
29469 cls : 'btn btn-default',
29470 html : '<i class="fa fa-trash"></i>'
29483 initEvents : function()
29485 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29486 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29488 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29489 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29491 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29492 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29494 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29495 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29497 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29498 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29500 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29501 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29503 this.bodyEl.on('click', this.onClick, this);
29504 this.downloadBtn.on('click', this.onDownload, this);
29505 this.trashBtn.on('click', this.onTrash, this);
29507 this.downloadBtn.hide();
29508 this.trashBtn.hide();
29510 if(this.showDownload){
29511 this.downloadBtn.show();
29514 if(this.showTrash){
29515 this.trashBtn.show();
29518 if(!this.showDownload && !this.showTrash) {
29519 this.footerEl.hide();
29524 initial : function()
29526 this.fireEvent('initial', this);
29530 onClick : function(e)
29532 e.preventDefault();
29534 this.fireEvent('click', this);
29537 onDownload : function(e)
29539 e.preventDefault();
29541 this.fireEvent('download', this);
29544 onTrash : function(e)
29546 e.preventDefault();
29548 this.fireEvent('trash', this);
29560 * @class Roo.bootstrap.NavProgressBar
29561 * @extends Roo.bootstrap.Component
29562 * Bootstrap NavProgressBar class
29565 * Create a new nav progress bar
29566 * @param {Object} config The config object
29569 Roo.bootstrap.NavProgressBar = function(config){
29570 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29572 this.bullets = this.bullets || [];
29574 // Roo.bootstrap.NavProgressBar.register(this);
29578 * Fires when the active item changes
29579 * @param {Roo.bootstrap.NavProgressBar} this
29580 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29581 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29588 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29593 getAutoCreate : function()
29595 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29599 cls : 'roo-navigation-bar-group',
29603 cls : 'roo-navigation-top-bar'
29607 cls : 'roo-navigation-bullets-bar',
29611 cls : 'roo-navigation-bar'
29618 cls : 'roo-navigation-bottom-bar'
29628 initEvents: function()
29633 onRender : function(ct, position)
29635 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29637 if(this.bullets.length){
29638 Roo.each(this.bullets, function(b){
29647 addItem : function(cfg)
29649 var item = new Roo.bootstrap.NavProgressItem(cfg);
29651 item.parentId = this.id;
29652 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29655 var top = new Roo.bootstrap.Element({
29657 cls : 'roo-navigation-bar-text'
29660 var bottom = new Roo.bootstrap.Element({
29662 cls : 'roo-navigation-bar-text'
29665 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29666 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29668 var topText = new Roo.bootstrap.Element({
29670 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29673 var bottomText = new Roo.bootstrap.Element({
29675 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29678 topText.onRender(top.el, null);
29679 bottomText.onRender(bottom.el, null);
29682 item.bottomEl = bottom;
29685 this.barItems.push(item);
29690 getActive : function()
29692 var active = false;
29694 Roo.each(this.barItems, function(v){
29696 if (!v.isActive()) {
29708 setActiveItem : function(item)
29712 Roo.each(this.barItems, function(v){
29713 if (v.rid == item.rid) {
29717 if (v.isActive()) {
29718 v.setActive(false);
29723 item.setActive(true);
29725 this.fireEvent('changed', this, item, prev);
29728 getBarItem: function(rid)
29732 Roo.each(this.barItems, function(e) {
29733 if (e.rid != rid) {
29744 indexOfItem : function(item)
29748 Roo.each(this.barItems, function(v, i){
29750 if (v.rid != item.rid) {
29761 setActiveNext : function()
29763 var i = this.indexOfItem(this.getActive());
29765 if (i > this.barItems.length) {
29769 this.setActiveItem(this.barItems[i+1]);
29772 setActivePrev : function()
29774 var i = this.indexOfItem(this.getActive());
29780 this.setActiveItem(this.barItems[i-1]);
29783 format : function()
29785 if(!this.barItems.length){
29789 var width = 100 / this.barItems.length;
29791 Roo.each(this.barItems, function(i){
29792 i.el.setStyle('width', width + '%');
29793 i.topEl.el.setStyle('width', width + '%');
29794 i.bottomEl.el.setStyle('width', width + '%');
29803 * Nav Progress Item
29808 * @class Roo.bootstrap.NavProgressItem
29809 * @extends Roo.bootstrap.Component
29810 * Bootstrap NavProgressItem class
29811 * @cfg {String} rid the reference id
29812 * @cfg {Boolean} active (true|false) Is item active default false
29813 * @cfg {Boolean} disabled (true|false) Is item active default false
29814 * @cfg {String} html
29815 * @cfg {String} position (top|bottom) text position default bottom
29816 * @cfg {String} icon show icon instead of number
29819 * Create a new NavProgressItem
29820 * @param {Object} config The config object
29822 Roo.bootstrap.NavProgressItem = function(config){
29823 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29828 * The raw click event for the entire grid.
29829 * @param {Roo.bootstrap.NavProgressItem} this
29830 * @param {Roo.EventObject} e
29837 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29843 position : 'bottom',
29846 getAutoCreate : function()
29848 var iconCls = 'roo-navigation-bar-item-icon';
29850 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29854 cls: 'roo-navigation-bar-item',
29864 cfg.cls += ' active';
29867 cfg.cls += ' disabled';
29873 disable : function()
29875 this.setDisabled(true);
29878 enable : function()
29880 this.setDisabled(false);
29883 initEvents: function()
29885 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29887 this.iconEl.on('click', this.onClick, this);
29890 onClick : function(e)
29892 e.preventDefault();
29898 if(this.fireEvent('click', this, e) === false){
29902 this.parent().setActiveItem(this);
29905 isActive: function ()
29907 return this.active;
29910 setActive : function(state)
29912 if(this.active == state){
29916 this.active = state;
29919 this.el.addClass('active');
29923 this.el.removeClass('active');
29928 setDisabled : function(state)
29930 if(this.disabled == state){
29934 this.disabled = state;
29937 this.el.addClass('disabled');
29941 this.el.removeClass('disabled');
29944 tooltipEl : function()
29946 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29959 * @class Roo.bootstrap.FieldLabel
29960 * @extends Roo.bootstrap.Component
29961 * Bootstrap FieldLabel class
29962 * @cfg {String} html contents of the element
29963 * @cfg {String} tag tag of the element default label
29964 * @cfg {String} cls class of the element
29965 * @cfg {String} target label target
29966 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29967 * @cfg {String} invalidClass default "text-warning"
29968 * @cfg {String} validClass default "text-success"
29969 * @cfg {String} iconTooltip default "This field is required"
29970 * @cfg {String} indicatorpos (left|right) default left
29973 * Create a new FieldLabel
29974 * @param {Object} config The config object
29977 Roo.bootstrap.FieldLabel = function(config){
29978 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29983 * Fires after the field has been marked as invalid.
29984 * @param {Roo.form.FieldLabel} this
29985 * @param {String} msg The validation message
29990 * Fires after the field has been validated with no errors.
29991 * @param {Roo.form.FieldLabel} this
29997 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30004 invalidClass : 'has-warning',
30005 validClass : 'has-success',
30006 iconTooltip : 'This field is required',
30007 indicatorpos : 'left',
30009 getAutoCreate : function(){
30012 if (!this.allowBlank) {
30018 cls : 'roo-bootstrap-field-label ' + this.cls,
30023 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30024 tooltip : this.iconTooltip
30033 if(this.indicatorpos == 'right'){
30036 cls : 'roo-bootstrap-field-label ' + this.cls,
30045 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30046 tooltip : this.iconTooltip
30055 initEvents: function()
30057 Roo.bootstrap.Element.superclass.initEvents.call(this);
30059 this.indicator = this.indicatorEl();
30061 if(this.indicator){
30062 this.indicator.removeClass('visible');
30063 this.indicator.addClass('invisible');
30066 Roo.bootstrap.FieldLabel.register(this);
30069 indicatorEl : function()
30071 var indicator = this.el.select('i.roo-required-indicator',true).first();
30082 * Mark this field as valid
30084 markValid : function()
30086 if(this.indicator){
30087 this.indicator.removeClass('visible');
30088 this.indicator.addClass('invisible');
30091 this.el.removeClass(this.invalidClass);
30093 this.el.addClass(this.validClass);
30095 this.fireEvent('valid', this);
30099 * Mark this field as invalid
30100 * @param {String} msg The validation message
30102 markInvalid : function(msg)
30104 if(this.indicator){
30105 this.indicator.removeClass('invisible');
30106 this.indicator.addClass('visible');
30109 this.el.removeClass(this.validClass);
30111 this.el.addClass(this.invalidClass);
30113 this.fireEvent('invalid', this, msg);
30119 Roo.apply(Roo.bootstrap.FieldLabel, {
30124 * register a FieldLabel Group
30125 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30127 register : function(label)
30129 if(this.groups.hasOwnProperty(label.target)){
30133 this.groups[label.target] = label;
30137 * fetch a FieldLabel Group based on the target
30138 * @param {string} target
30139 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30141 get: function(target) {
30142 if (typeof(this.groups[target]) == 'undefined') {
30146 return this.groups[target] ;
30155 * page DateSplitField.
30161 * @class Roo.bootstrap.DateSplitField
30162 * @extends Roo.bootstrap.Component
30163 * Bootstrap DateSplitField class
30164 * @cfg {string} fieldLabel - the label associated
30165 * @cfg {Number} labelWidth set the width of label (0-12)
30166 * @cfg {String} labelAlign (top|left)
30167 * @cfg {Boolean} dayAllowBlank (true|false) default false
30168 * @cfg {Boolean} monthAllowBlank (true|false) default false
30169 * @cfg {Boolean} yearAllowBlank (true|false) default false
30170 * @cfg {string} dayPlaceholder
30171 * @cfg {string} monthPlaceholder
30172 * @cfg {string} yearPlaceholder
30173 * @cfg {string} dayFormat default 'd'
30174 * @cfg {string} monthFormat default 'm'
30175 * @cfg {string} yearFormat default 'Y'
30176 * @cfg {Number} labellg set the width of label (1-12)
30177 * @cfg {Number} labelmd set the width of label (1-12)
30178 * @cfg {Number} labelsm set the width of label (1-12)
30179 * @cfg {Number} labelxs set the width of label (1-12)
30183 * Create a new DateSplitField
30184 * @param {Object} config The config object
30187 Roo.bootstrap.DateSplitField = function(config){
30188 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30194 * getting the data of years
30195 * @param {Roo.bootstrap.DateSplitField} this
30196 * @param {Object} years
30201 * getting the data of days
30202 * @param {Roo.bootstrap.DateSplitField} this
30203 * @param {Object} days
30208 * Fires after the field has been marked as invalid.
30209 * @param {Roo.form.Field} this
30210 * @param {String} msg The validation message
30215 * Fires after the field has been validated with no errors.
30216 * @param {Roo.form.Field} this
30222 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30225 labelAlign : 'top',
30227 dayAllowBlank : false,
30228 monthAllowBlank : false,
30229 yearAllowBlank : false,
30230 dayPlaceholder : '',
30231 monthPlaceholder : '',
30232 yearPlaceholder : '',
30236 isFormField : true,
30242 getAutoCreate : function()
30246 cls : 'row roo-date-split-field-group',
30251 cls : 'form-hidden-field roo-date-split-field-group-value',
30257 var labelCls = 'col-md-12';
30258 var contentCls = 'col-md-4';
30260 if(this.fieldLabel){
30264 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30268 html : this.fieldLabel
30273 if(this.labelAlign == 'left'){
30275 if(this.labelWidth > 12){
30276 label.style = "width: " + this.labelWidth + 'px';
30279 if(this.labelWidth < 13 && this.labelmd == 0){
30280 this.labelmd = this.labelWidth;
30283 if(this.labellg > 0){
30284 labelCls = ' col-lg-' + this.labellg;
30285 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30288 if(this.labelmd > 0){
30289 labelCls = ' col-md-' + this.labelmd;
30290 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30293 if(this.labelsm > 0){
30294 labelCls = ' col-sm-' + this.labelsm;
30295 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30298 if(this.labelxs > 0){
30299 labelCls = ' col-xs-' + this.labelxs;
30300 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30304 label.cls += ' ' + labelCls;
30306 cfg.cn.push(label);
30309 Roo.each(['day', 'month', 'year'], function(t){
30312 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30319 inputEl: function ()
30321 return this.el.select('.roo-date-split-field-group-value', true).first();
30324 onRender : function(ct, position)
30328 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30330 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30332 this.dayField = new Roo.bootstrap.ComboBox({
30333 allowBlank : this.dayAllowBlank,
30334 alwaysQuery : true,
30335 displayField : 'value',
30338 forceSelection : true,
30340 placeholder : this.dayPlaceholder,
30341 selectOnFocus : true,
30342 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30343 triggerAction : 'all',
30345 valueField : 'value',
30346 store : new Roo.data.SimpleStore({
30347 data : (function() {
30349 _this.fireEvent('days', _this, days);
30352 fields : [ 'value' ]
30355 select : function (_self, record, index)
30357 _this.setValue(_this.getValue());
30362 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30364 this.monthField = new Roo.bootstrap.MonthField({
30365 after : '<i class=\"fa fa-calendar\"></i>',
30366 allowBlank : this.monthAllowBlank,
30367 placeholder : this.monthPlaceholder,
30370 render : function (_self)
30372 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30373 e.preventDefault();
30377 select : function (_self, oldvalue, newvalue)
30379 _this.setValue(_this.getValue());
30384 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30386 this.yearField = new Roo.bootstrap.ComboBox({
30387 allowBlank : this.yearAllowBlank,
30388 alwaysQuery : true,
30389 displayField : 'value',
30392 forceSelection : true,
30394 placeholder : this.yearPlaceholder,
30395 selectOnFocus : true,
30396 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30397 triggerAction : 'all',
30399 valueField : 'value',
30400 store : new Roo.data.SimpleStore({
30401 data : (function() {
30403 _this.fireEvent('years', _this, years);
30406 fields : [ 'value' ]
30409 select : function (_self, record, index)
30411 _this.setValue(_this.getValue());
30416 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30419 setValue : function(v, format)
30421 this.inputEl.dom.value = v;
30423 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30425 var d = Date.parseDate(v, f);
30432 this.setDay(d.format(this.dayFormat));
30433 this.setMonth(d.format(this.monthFormat));
30434 this.setYear(d.format(this.yearFormat));
30441 setDay : function(v)
30443 this.dayField.setValue(v);
30444 this.inputEl.dom.value = this.getValue();
30449 setMonth : function(v)
30451 this.monthField.setValue(v, true);
30452 this.inputEl.dom.value = this.getValue();
30457 setYear : function(v)
30459 this.yearField.setValue(v);
30460 this.inputEl.dom.value = this.getValue();
30465 getDay : function()
30467 return this.dayField.getValue();
30470 getMonth : function()
30472 return this.monthField.getValue();
30475 getYear : function()
30477 return this.yearField.getValue();
30480 getValue : function()
30482 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30484 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30494 this.inputEl.dom.value = '';
30499 validate : function()
30501 var d = this.dayField.validate();
30502 var m = this.monthField.validate();
30503 var y = this.yearField.validate();
30508 (!this.dayAllowBlank && !d) ||
30509 (!this.monthAllowBlank && !m) ||
30510 (!this.yearAllowBlank && !y)
30515 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30524 this.markInvalid();
30529 markValid : function()
30532 var label = this.el.select('label', true).first();
30533 var icon = this.el.select('i.fa-star', true).first();
30539 this.fireEvent('valid', this);
30543 * Mark this field as invalid
30544 * @param {String} msg The validation message
30546 markInvalid : function(msg)
30549 var label = this.el.select('label', true).first();
30550 var icon = this.el.select('i.fa-star', true).first();
30552 if(label && !icon){
30553 this.el.select('.roo-date-split-field-label', true).createChild({
30555 cls : 'text-danger fa fa-lg fa-star',
30556 tooltip : 'This field is required',
30557 style : 'margin-right:5px;'
30561 this.fireEvent('invalid', this, msg);
30564 clearInvalid : function()
30566 var label = this.el.select('label', true).first();
30567 var icon = this.el.select('i.fa-star', true).first();
30573 this.fireEvent('valid', this);
30576 getName: function()
30586 * http://masonry.desandro.com
30588 * The idea is to render all the bricks based on vertical width...
30590 * The original code extends 'outlayer' - we might need to use that....
30596 * @class Roo.bootstrap.LayoutMasonry
30597 * @extends Roo.bootstrap.Component
30598 * Bootstrap Layout Masonry class
30601 * Create a new Element
30602 * @param {Object} config The config object
30605 Roo.bootstrap.LayoutMasonry = function(config){
30607 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30611 Roo.bootstrap.LayoutMasonry.register(this);
30617 * Fire after layout the items
30618 * @param {Roo.bootstrap.LayoutMasonry} this
30619 * @param {Roo.EventObject} e
30626 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30629 * @cfg {Boolean} isLayoutInstant = no animation?
30631 isLayoutInstant : false, // needed?
30634 * @cfg {Number} boxWidth width of the columns
30639 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30644 * @cfg {Number} padWidth padding below box..
30649 * @cfg {Number} gutter gutter width..
30654 * @cfg {Number} maxCols maximum number of columns
30660 * @cfg {Boolean} isAutoInitial defalut true
30662 isAutoInitial : true,
30667 * @cfg {Boolean} isHorizontal defalut false
30669 isHorizontal : false,
30671 currentSize : null,
30677 bricks: null, //CompositeElement
30681 _isLayoutInited : false,
30683 // isAlternative : false, // only use for vertical layout...
30686 * @cfg {Number} alternativePadWidth padding below box..
30688 alternativePadWidth : 50,
30690 selectedBrick : [],
30692 getAutoCreate : function(){
30694 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30698 cls: 'blog-masonary-wrapper ' + this.cls,
30700 cls : 'mas-boxes masonary'
30707 getChildContainer: function( )
30709 if (this.boxesEl) {
30710 return this.boxesEl;
30713 this.boxesEl = this.el.select('.mas-boxes').first();
30715 return this.boxesEl;
30719 initEvents : function()
30723 if(this.isAutoInitial){
30724 Roo.log('hook children rendered');
30725 this.on('childrenrendered', function() {
30726 Roo.log('children rendered');
30732 initial : function()
30734 this.selectedBrick = [];
30736 this.currentSize = this.el.getBox(true);
30738 Roo.EventManager.onWindowResize(this.resize, this);
30740 if(!this.isAutoInitial){
30748 //this.layout.defer(500,this);
30752 resize : function()
30754 var cs = this.el.getBox(true);
30757 this.currentSize.width == cs.width &&
30758 this.currentSize.x == cs.x &&
30759 this.currentSize.height == cs.height &&
30760 this.currentSize.y == cs.y
30762 Roo.log("no change in with or X or Y");
30766 this.currentSize = cs;
30772 layout : function()
30774 this._resetLayout();
30776 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30778 this.layoutItems( isInstant );
30780 this._isLayoutInited = true;
30782 this.fireEvent('layout', this);
30786 _resetLayout : function()
30788 if(this.isHorizontal){
30789 this.horizontalMeasureColumns();
30793 this.verticalMeasureColumns();
30797 verticalMeasureColumns : function()
30799 this.getContainerWidth();
30801 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30802 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30806 var boxWidth = this.boxWidth + this.padWidth;
30808 if(this.containerWidth < this.boxWidth){
30809 boxWidth = this.containerWidth
30812 var containerWidth = this.containerWidth;
30814 var cols = Math.floor(containerWidth / boxWidth);
30816 this.cols = Math.max( cols, 1 );
30818 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30820 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30822 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30824 this.colWidth = boxWidth + avail - this.padWidth;
30826 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30827 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30830 horizontalMeasureColumns : function()
30832 this.getContainerWidth();
30834 var boxWidth = this.boxWidth;
30836 if(this.containerWidth < boxWidth){
30837 boxWidth = this.containerWidth;
30840 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30842 this.el.setHeight(boxWidth);
30846 getContainerWidth : function()
30848 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30851 layoutItems : function( isInstant )
30853 Roo.log(this.bricks);
30855 var items = Roo.apply([], this.bricks);
30857 if(this.isHorizontal){
30858 this._horizontalLayoutItems( items , isInstant );
30862 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30863 // this._verticalAlternativeLayoutItems( items , isInstant );
30867 this._verticalLayoutItems( items , isInstant );
30871 _verticalLayoutItems : function ( items , isInstant)
30873 if ( !items || !items.length ) {
30878 ['xs', 'xs', 'xs', 'tall'],
30879 ['xs', 'xs', 'tall'],
30880 ['xs', 'xs', 'sm'],
30881 ['xs', 'xs', 'xs'],
30887 ['sm', 'xs', 'xs'],
30891 ['tall', 'xs', 'xs', 'xs'],
30892 ['tall', 'xs', 'xs'],
30904 Roo.each(items, function(item, k){
30906 switch (item.size) {
30907 // these layouts take up a full box,
30918 boxes.push([item]);
30941 var filterPattern = function(box, length)
30949 var pattern = box.slice(0, length);
30953 Roo.each(pattern, function(i){
30954 format.push(i.size);
30957 Roo.each(standard, function(s){
30959 if(String(s) != String(format)){
30968 if(!match && length == 1){
30973 filterPattern(box, length - 1);
30977 queue.push(pattern);
30979 box = box.slice(length, box.length);
30981 filterPattern(box, 4);
30987 Roo.each(boxes, function(box, k){
30993 if(box.length == 1){
30998 filterPattern(box, 4);
31002 this._processVerticalLayoutQueue( queue, isInstant );
31006 // _verticalAlternativeLayoutItems : function( items , isInstant )
31008 // if ( !items || !items.length ) {
31012 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31016 _horizontalLayoutItems : function ( items , isInstant)
31018 if ( !items || !items.length || items.length < 3) {
31024 var eItems = items.slice(0, 3);
31026 items = items.slice(3, items.length);
31029 ['xs', 'xs', 'xs', 'wide'],
31030 ['xs', 'xs', 'wide'],
31031 ['xs', 'xs', 'sm'],
31032 ['xs', 'xs', 'xs'],
31038 ['sm', 'xs', 'xs'],
31042 ['wide', 'xs', 'xs', 'xs'],
31043 ['wide', 'xs', 'xs'],
31056 Roo.each(items, function(item, k){
31058 switch (item.size) {
31069 boxes.push([item]);
31093 var filterPattern = function(box, length)
31101 var pattern = box.slice(0, length);
31105 Roo.each(pattern, function(i){
31106 format.push(i.size);
31109 Roo.each(standard, function(s){
31111 if(String(s) != String(format)){
31120 if(!match && length == 1){
31125 filterPattern(box, length - 1);
31129 queue.push(pattern);
31131 box = box.slice(length, box.length);
31133 filterPattern(box, 4);
31139 Roo.each(boxes, function(box, k){
31145 if(box.length == 1){
31150 filterPattern(box, 4);
31157 var pos = this.el.getBox(true);
31161 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31163 var hit_end = false;
31165 Roo.each(queue, function(box){
31169 Roo.each(box, function(b){
31171 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31181 Roo.each(box, function(b){
31183 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31186 mx = Math.max(mx, b.x);
31190 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31194 Roo.each(box, function(b){
31196 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31210 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31213 /** Sets position of item in DOM
31214 * @param {Element} item
31215 * @param {Number} x - horizontal position
31216 * @param {Number} y - vertical position
31217 * @param {Boolean} isInstant - disables transitions
31219 _processVerticalLayoutQueue : function( queue, isInstant )
31221 var pos = this.el.getBox(true);
31226 for (var i = 0; i < this.cols; i++){
31230 Roo.each(queue, function(box, k){
31232 var col = k % this.cols;
31234 Roo.each(box, function(b,kk){
31236 b.el.position('absolute');
31238 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31239 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31241 if(b.size == 'md-left' || b.size == 'md-right'){
31242 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31243 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31246 b.el.setWidth(width);
31247 b.el.setHeight(height);
31249 b.el.select('iframe',true).setSize(width,height);
31253 for (var i = 0; i < this.cols; i++){
31255 if(maxY[i] < maxY[col]){
31260 col = Math.min(col, i);
31264 x = pos.x + col * (this.colWidth + this.padWidth);
31268 var positions = [];
31270 switch (box.length){
31272 positions = this.getVerticalOneBoxColPositions(x, y, box);
31275 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31278 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31281 positions = this.getVerticalFourBoxColPositions(x, y, box);
31287 Roo.each(box, function(b,kk){
31289 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31291 var sz = b.el.getSize();
31293 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31301 for (var i = 0; i < this.cols; i++){
31302 mY = Math.max(mY, maxY[i]);
31305 this.el.setHeight(mY - pos.y);
31309 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31311 // var pos = this.el.getBox(true);
31314 // var maxX = pos.right;
31316 // var maxHeight = 0;
31318 // Roo.each(items, function(item, k){
31322 // item.el.position('absolute');
31324 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31326 // item.el.setWidth(width);
31328 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31330 // item.el.setHeight(height);
31333 // item.el.setXY([x, y], isInstant ? false : true);
31335 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31338 // y = y + height + this.alternativePadWidth;
31340 // maxHeight = maxHeight + height + this.alternativePadWidth;
31344 // this.el.setHeight(maxHeight);
31348 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31350 var pos = this.el.getBox(true);
31355 var maxX = pos.right;
31357 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31359 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31361 Roo.each(queue, function(box, k){
31363 Roo.each(box, function(b, kk){
31365 b.el.position('absolute');
31367 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31368 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31370 if(b.size == 'md-left' || b.size == 'md-right'){
31371 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31372 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31375 b.el.setWidth(width);
31376 b.el.setHeight(height);
31384 var positions = [];
31386 switch (box.length){
31388 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31391 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31394 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31397 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31403 Roo.each(box, function(b,kk){
31405 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31407 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31415 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31417 Roo.each(eItems, function(b,k){
31419 b.size = (k == 0) ? 'sm' : 'xs';
31420 b.x = (k == 0) ? 2 : 1;
31421 b.y = (k == 0) ? 2 : 1;
31423 b.el.position('absolute');
31425 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31427 b.el.setWidth(width);
31429 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31431 b.el.setHeight(height);
31435 var positions = [];
31438 x : maxX - this.unitWidth * 2 - this.gutter,
31443 x : maxX - this.unitWidth,
31444 y : minY + (this.unitWidth + this.gutter) * 2
31448 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31452 Roo.each(eItems, function(b,k){
31454 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31460 getVerticalOneBoxColPositions : function(x, y, box)
31464 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31466 if(box[0].size == 'md-left'){
31470 if(box[0].size == 'md-right'){
31475 x : x + (this.unitWidth + this.gutter) * rand,
31482 getVerticalTwoBoxColPositions : function(x, y, box)
31486 if(box[0].size == 'xs'){
31490 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31494 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31508 x : x + (this.unitWidth + this.gutter) * 2,
31509 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31516 getVerticalThreeBoxColPositions : function(x, y, box)
31520 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31528 x : x + (this.unitWidth + this.gutter) * 1,
31533 x : x + (this.unitWidth + this.gutter) * 2,
31541 if(box[0].size == 'xs' && box[1].size == 'xs'){
31550 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31554 x : x + (this.unitWidth + this.gutter) * 1,
31568 x : x + (this.unitWidth + this.gutter) * 2,
31573 x : x + (this.unitWidth + this.gutter) * 2,
31574 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31581 getVerticalFourBoxColPositions : function(x, y, box)
31585 if(box[0].size == 'xs'){
31594 y : y + (this.unitHeight + this.gutter) * 1
31599 y : y + (this.unitHeight + this.gutter) * 2
31603 x : x + (this.unitWidth + this.gutter) * 1,
31617 x : x + (this.unitWidth + this.gutter) * 2,
31622 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31623 y : y + (this.unitHeight + this.gutter) * 1
31627 x : x + (this.unitWidth + this.gutter) * 2,
31628 y : y + (this.unitWidth + this.gutter) * 2
31635 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31639 if(box[0].size == 'md-left'){
31641 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31648 if(box[0].size == 'md-right'){
31650 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31651 y : minY + (this.unitWidth + this.gutter) * 1
31657 var rand = Math.floor(Math.random() * (4 - box[0].y));
31660 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31661 y : minY + (this.unitWidth + this.gutter) * rand
31668 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31672 if(box[0].size == 'xs'){
31675 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31680 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31681 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31689 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31694 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31695 y : minY + (this.unitWidth + this.gutter) * 2
31702 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31706 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31709 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31714 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31715 y : minY + (this.unitWidth + this.gutter) * 1
31719 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31720 y : minY + (this.unitWidth + this.gutter) * 2
31727 if(box[0].size == 'xs' && box[1].size == 'xs'){
31730 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31735 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31740 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31741 y : minY + (this.unitWidth + this.gutter) * 1
31749 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31754 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31755 y : minY + (this.unitWidth + this.gutter) * 2
31759 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31760 y : minY + (this.unitWidth + this.gutter) * 2
31767 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31771 if(box[0].size == 'xs'){
31774 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31779 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31784 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),
31789 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31790 y : minY + (this.unitWidth + this.gutter) * 1
31798 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31803 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31804 y : minY + (this.unitWidth + this.gutter) * 2
31808 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31809 y : minY + (this.unitWidth + this.gutter) * 2
31813 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),
31814 y : minY + (this.unitWidth + this.gutter) * 2
31822 * remove a Masonry Brick
31823 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31825 removeBrick : function(brick_id)
31831 for (var i = 0; i<this.bricks.length; i++) {
31832 if (this.bricks[i].id == brick_id) {
31833 this.bricks.splice(i,1);
31834 this.el.dom.removeChild(Roo.get(brick_id).dom);
31841 * adds a Masonry Brick
31842 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31844 addBrick : function(cfg)
31846 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31847 //this.register(cn);
31848 cn.parentId = this.id;
31849 cn.onRender(this.el, null);
31854 * register a Masonry Brick
31855 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31858 register : function(brick)
31860 this.bricks.push(brick);
31861 brick.masonryId = this.id;
31865 * clear all the Masonry Brick
31867 clearAll : function()
31870 //this.getChildContainer().dom.innerHTML = "";
31871 this.el.dom.innerHTML = '';
31874 getSelected : function()
31876 if (!this.selectedBrick) {
31880 return this.selectedBrick;
31884 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31888 * register a Masonry Layout
31889 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31892 register : function(layout)
31894 this.groups[layout.id] = layout;
31897 * fetch a Masonry Layout based on the masonry layout ID
31898 * @param {string} the masonry layout to add
31899 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31902 get: function(layout_id) {
31903 if (typeof(this.groups[layout_id]) == 'undefined') {
31906 return this.groups[layout_id] ;
31918 * http://masonry.desandro.com
31920 * The idea is to render all the bricks based on vertical width...
31922 * The original code extends 'outlayer' - we might need to use that....
31928 * @class Roo.bootstrap.LayoutMasonryAuto
31929 * @extends Roo.bootstrap.Component
31930 * Bootstrap Layout Masonry class
31933 * Create a new Element
31934 * @param {Object} config The config object
31937 Roo.bootstrap.LayoutMasonryAuto = function(config){
31938 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31941 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31944 * @cfg {Boolean} isFitWidth - resize the width..
31946 isFitWidth : false, // options..
31948 * @cfg {Boolean} isOriginLeft = left align?
31950 isOriginLeft : true,
31952 * @cfg {Boolean} isOriginTop = top align?
31954 isOriginTop : false,
31956 * @cfg {Boolean} isLayoutInstant = no animation?
31958 isLayoutInstant : false, // needed?
31960 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31962 isResizingContainer : true,
31964 * @cfg {Number} columnWidth width of the columns
31970 * @cfg {Number} maxCols maximum number of columns
31975 * @cfg {Number} padHeight padding below box..
31981 * @cfg {Boolean} isAutoInitial defalut true
31984 isAutoInitial : true,
31990 initialColumnWidth : 0,
31991 currentSize : null,
31993 colYs : null, // array.
32000 bricks: null, //CompositeElement
32001 cols : 0, // array?
32002 // element : null, // wrapped now this.el
32003 _isLayoutInited : null,
32006 getAutoCreate : function(){
32010 cls: 'blog-masonary-wrapper ' + this.cls,
32012 cls : 'mas-boxes masonary'
32019 getChildContainer: function( )
32021 if (this.boxesEl) {
32022 return this.boxesEl;
32025 this.boxesEl = this.el.select('.mas-boxes').first();
32027 return this.boxesEl;
32031 initEvents : function()
32035 if(this.isAutoInitial){
32036 Roo.log('hook children rendered');
32037 this.on('childrenrendered', function() {
32038 Roo.log('children rendered');
32045 initial : function()
32047 this.reloadItems();
32049 this.currentSize = this.el.getBox(true);
32051 /// was window resize... - let's see if this works..
32052 Roo.EventManager.onWindowResize(this.resize, this);
32054 if(!this.isAutoInitial){
32059 this.layout.defer(500,this);
32062 reloadItems: function()
32064 this.bricks = this.el.select('.masonry-brick', true);
32066 this.bricks.each(function(b) {
32067 //Roo.log(b.getSize());
32068 if (!b.attr('originalwidth')) {
32069 b.attr('originalwidth', b.getSize().width);
32074 Roo.log(this.bricks.elements.length);
32077 resize : function()
32080 var cs = this.el.getBox(true);
32082 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32083 Roo.log("no change in with or X");
32086 this.currentSize = cs;
32090 layout : function()
32093 this._resetLayout();
32094 //this._manageStamps();
32096 // don't animate first layout
32097 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32098 this.layoutItems( isInstant );
32100 // flag for initalized
32101 this._isLayoutInited = true;
32104 layoutItems : function( isInstant )
32106 //var items = this._getItemsForLayout( this.items );
32107 // original code supports filtering layout items.. we just ignore it..
32109 this._layoutItems( this.bricks , isInstant );
32111 this._postLayout();
32113 _layoutItems : function ( items , isInstant)
32115 //this.fireEvent( 'layout', this, items );
32118 if ( !items || !items.elements.length ) {
32119 // no items, emit event with empty array
32124 items.each(function(item) {
32125 Roo.log("layout item");
32127 // get x/y object from method
32128 var position = this._getItemLayoutPosition( item );
32130 position.item = item;
32131 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32132 queue.push( position );
32135 this._processLayoutQueue( queue );
32137 /** Sets position of item in DOM
32138 * @param {Element} item
32139 * @param {Number} x - horizontal position
32140 * @param {Number} y - vertical position
32141 * @param {Boolean} isInstant - disables transitions
32143 _processLayoutQueue : function( queue )
32145 for ( var i=0, len = queue.length; i < len; i++ ) {
32146 var obj = queue[i];
32147 obj.item.position('absolute');
32148 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32154 * Any logic you want to do after each layout,
32155 * i.e. size the container
32157 _postLayout : function()
32159 this.resizeContainer();
32162 resizeContainer : function()
32164 if ( !this.isResizingContainer ) {
32167 var size = this._getContainerSize();
32169 this.el.setSize(size.width,size.height);
32170 this.boxesEl.setSize(size.width,size.height);
32176 _resetLayout : function()
32178 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32179 this.colWidth = this.el.getWidth();
32180 //this.gutter = this.el.getWidth();
32182 this.measureColumns();
32188 this.colYs.push( 0 );
32194 measureColumns : function()
32196 this.getContainerWidth();
32197 // if columnWidth is 0, default to outerWidth of first item
32198 if ( !this.columnWidth ) {
32199 var firstItem = this.bricks.first();
32200 Roo.log(firstItem);
32201 this.columnWidth = this.containerWidth;
32202 if (firstItem && firstItem.attr('originalwidth') ) {
32203 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32205 // columnWidth fall back to item of first element
32206 Roo.log("set column width?");
32207 this.initialColumnWidth = this.columnWidth ;
32209 // if first elem has no width, default to size of container
32214 if (this.initialColumnWidth) {
32215 this.columnWidth = this.initialColumnWidth;
32220 // column width is fixed at the top - however if container width get's smaller we should
32223 // this bit calcs how man columns..
32225 var columnWidth = this.columnWidth += this.gutter;
32227 // calculate columns
32228 var containerWidth = this.containerWidth + this.gutter;
32230 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32231 // fix rounding errors, typically with gutters
32232 var excess = columnWidth - containerWidth % columnWidth;
32235 // if overshoot is less than a pixel, round up, otherwise floor it
32236 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32237 cols = Math[ mathMethod ]( cols );
32238 this.cols = Math.max( cols, 1 );
32239 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32241 // padding positioning..
32242 var totalColWidth = this.cols * this.columnWidth;
32243 var padavail = this.containerWidth - totalColWidth;
32244 // so for 2 columns - we need 3 'pads'
32246 var padNeeded = (1+this.cols) * this.padWidth;
32248 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32250 this.columnWidth += padExtra
32251 //this.padWidth = Math.floor(padavail / ( this.cols));
32253 // adjust colum width so that padding is fixed??
32255 // we have 3 columns ... total = width * 3
32256 // we have X left over... that should be used by
32258 //if (this.expandC) {
32266 getContainerWidth : function()
32268 /* // container is parent if fit width
32269 var container = this.isFitWidth ? this.element.parentNode : this.element;
32270 // check that this.size and size are there
32271 // IE8 triggers resize on body size change, so they might not be
32273 var size = getSize( container ); //FIXME
32274 this.containerWidth = size && size.innerWidth; //FIXME
32277 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32281 _getItemLayoutPosition : function( item ) // what is item?
32283 // we resize the item to our columnWidth..
32285 item.setWidth(this.columnWidth);
32286 item.autoBoxAdjust = false;
32288 var sz = item.getSize();
32290 // how many columns does this brick span
32291 var remainder = this.containerWidth % this.columnWidth;
32293 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32294 // round if off by 1 pixel, otherwise use ceil
32295 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32296 colSpan = Math.min( colSpan, this.cols );
32298 // normally this should be '1' as we dont' currently allow multi width columns..
32300 var colGroup = this._getColGroup( colSpan );
32301 // get the minimum Y value from the columns
32302 var minimumY = Math.min.apply( Math, colGroup );
32303 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32305 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32307 // position the brick
32309 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32310 y: this.currentSize.y + minimumY + this.padHeight
32314 // apply setHeight to necessary columns
32315 var setHeight = minimumY + sz.height + this.padHeight;
32316 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32318 var setSpan = this.cols + 1 - colGroup.length;
32319 for ( var i = 0; i < setSpan; i++ ) {
32320 this.colYs[ shortColIndex + i ] = setHeight ;
32327 * @param {Number} colSpan - number of columns the element spans
32328 * @returns {Array} colGroup
32330 _getColGroup : function( colSpan )
32332 if ( colSpan < 2 ) {
32333 // if brick spans only one column, use all the column Ys
32338 // how many different places could this brick fit horizontally
32339 var groupCount = this.cols + 1 - colSpan;
32340 // for each group potential horizontal position
32341 for ( var i = 0; i < groupCount; i++ ) {
32342 // make an array of colY values for that one group
32343 var groupColYs = this.colYs.slice( i, i + colSpan );
32344 // and get the max value of the array
32345 colGroup[i] = Math.max.apply( Math, groupColYs );
32350 _manageStamp : function( stamp )
32352 var stampSize = stamp.getSize();
32353 var offset = stamp.getBox();
32354 // get the columns that this stamp affects
32355 var firstX = this.isOriginLeft ? offset.x : offset.right;
32356 var lastX = firstX + stampSize.width;
32357 var firstCol = Math.floor( firstX / this.columnWidth );
32358 firstCol = Math.max( 0, firstCol );
32360 var lastCol = Math.floor( lastX / this.columnWidth );
32361 // lastCol should not go over if multiple of columnWidth #425
32362 lastCol -= lastX % this.columnWidth ? 0 : 1;
32363 lastCol = Math.min( this.cols - 1, lastCol );
32365 // set colYs to bottom of the stamp
32366 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32369 for ( var i = firstCol; i <= lastCol; i++ ) {
32370 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32375 _getContainerSize : function()
32377 this.maxY = Math.max.apply( Math, this.colYs );
32382 if ( this.isFitWidth ) {
32383 size.width = this._getContainerFitWidth();
32389 _getContainerFitWidth : function()
32391 var unusedCols = 0;
32392 // count unused columns
32395 if ( this.colYs[i] !== 0 ) {
32400 // fit container to columns that have been used
32401 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32404 needsResizeLayout : function()
32406 var previousWidth = this.containerWidth;
32407 this.getContainerWidth();
32408 return previousWidth !== this.containerWidth;
32423 * @class Roo.bootstrap.MasonryBrick
32424 * @extends Roo.bootstrap.Component
32425 * Bootstrap MasonryBrick class
32428 * Create a new MasonryBrick
32429 * @param {Object} config The config object
32432 Roo.bootstrap.MasonryBrick = function(config){
32434 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32436 Roo.bootstrap.MasonryBrick.register(this);
32442 * When a MasonryBrick is clcik
32443 * @param {Roo.bootstrap.MasonryBrick} this
32444 * @param {Roo.EventObject} e
32450 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32453 * @cfg {String} title
32457 * @cfg {String} html
32461 * @cfg {String} bgimage
32465 * @cfg {String} videourl
32469 * @cfg {String} cls
32473 * @cfg {String} href
32477 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32482 * @cfg {String} placetitle (center|bottom)
32487 * @cfg {Boolean} isFitContainer defalut true
32489 isFitContainer : true,
32492 * @cfg {Boolean} preventDefault defalut false
32494 preventDefault : false,
32497 * @cfg {Boolean} inverse defalut false
32499 maskInverse : false,
32501 getAutoCreate : function()
32503 if(!this.isFitContainer){
32504 return this.getSplitAutoCreate();
32507 var cls = 'masonry-brick masonry-brick-full';
32509 if(this.href.length){
32510 cls += ' masonry-brick-link';
32513 if(this.bgimage.length){
32514 cls += ' masonry-brick-image';
32517 if(this.maskInverse){
32518 cls += ' mask-inverse';
32521 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32522 cls += ' enable-mask';
32526 cls += ' masonry-' + this.size + '-brick';
32529 if(this.placetitle.length){
32531 switch (this.placetitle) {
32533 cls += ' masonry-center-title';
32536 cls += ' masonry-bottom-title';
32543 if(!this.html.length && !this.bgimage.length){
32544 cls += ' masonry-center-title';
32547 if(!this.html.length && this.bgimage.length){
32548 cls += ' masonry-bottom-title';
32553 cls += ' ' + this.cls;
32557 tag: (this.href.length) ? 'a' : 'div',
32562 cls: 'masonry-brick-mask'
32566 cls: 'masonry-brick-paragraph',
32572 if(this.href.length){
32573 cfg.href = this.href;
32576 var cn = cfg.cn[1].cn;
32578 if(this.title.length){
32581 cls: 'masonry-brick-title',
32586 if(this.html.length){
32589 cls: 'masonry-brick-text',
32594 if (!this.title.length && !this.html.length) {
32595 cfg.cn[1].cls += ' hide';
32598 if(this.bgimage.length){
32601 cls: 'masonry-brick-image-view',
32606 if(this.videourl.length){
32607 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32608 // youtube support only?
32611 cls: 'masonry-brick-image-view',
32614 allowfullscreen : true
32622 getSplitAutoCreate : function()
32624 var cls = 'masonry-brick masonry-brick-split';
32626 if(this.href.length){
32627 cls += ' masonry-brick-link';
32630 if(this.bgimage.length){
32631 cls += ' masonry-brick-image';
32635 cls += ' masonry-' + this.size + '-brick';
32638 switch (this.placetitle) {
32640 cls += ' masonry-center-title';
32643 cls += ' masonry-bottom-title';
32646 if(!this.bgimage.length){
32647 cls += ' masonry-center-title';
32650 if(this.bgimage.length){
32651 cls += ' masonry-bottom-title';
32657 cls += ' ' + this.cls;
32661 tag: (this.href.length) ? 'a' : 'div',
32666 cls: 'masonry-brick-split-head',
32670 cls: 'masonry-brick-paragraph',
32677 cls: 'masonry-brick-split-body',
32683 if(this.href.length){
32684 cfg.href = this.href;
32687 if(this.title.length){
32688 cfg.cn[0].cn[0].cn.push({
32690 cls: 'masonry-brick-title',
32695 if(this.html.length){
32696 cfg.cn[1].cn.push({
32698 cls: 'masonry-brick-text',
32703 if(this.bgimage.length){
32704 cfg.cn[0].cn.push({
32706 cls: 'masonry-brick-image-view',
32711 if(this.videourl.length){
32712 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32713 // youtube support only?
32714 cfg.cn[0].cn.cn.push({
32716 cls: 'masonry-brick-image-view',
32719 allowfullscreen : true
32726 initEvents: function()
32728 switch (this.size) {
32761 this.el.on('touchstart', this.onTouchStart, this);
32762 this.el.on('touchmove', this.onTouchMove, this);
32763 this.el.on('touchend', this.onTouchEnd, this);
32764 this.el.on('contextmenu', this.onContextMenu, this);
32766 this.el.on('mouseenter' ,this.enter, this);
32767 this.el.on('mouseleave', this.leave, this);
32768 this.el.on('click', this.onClick, this);
32771 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32772 this.parent().bricks.push(this);
32777 onClick: function(e, el)
32779 var time = this.endTimer - this.startTimer;
32780 // Roo.log(e.preventDefault());
32783 e.preventDefault();
32788 if(!this.preventDefault){
32792 e.preventDefault();
32794 if (this.activeClass != '') {
32795 this.selectBrick();
32798 this.fireEvent('click', this, e);
32801 enter: function(e, el)
32803 e.preventDefault();
32805 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32809 if(this.bgimage.length && this.html.length){
32810 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32814 leave: function(e, el)
32816 e.preventDefault();
32818 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32822 if(this.bgimage.length && this.html.length){
32823 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32827 onTouchStart: function(e, el)
32829 // e.preventDefault();
32831 this.touchmoved = false;
32833 if(!this.isFitContainer){
32837 if(!this.bgimage.length || !this.html.length){
32841 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32843 this.timer = new Date().getTime();
32847 onTouchMove: function(e, el)
32849 this.touchmoved = true;
32852 onContextMenu : function(e,el)
32854 e.preventDefault();
32855 e.stopPropagation();
32859 onTouchEnd: function(e, el)
32861 // e.preventDefault();
32863 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32870 if(!this.bgimage.length || !this.html.length){
32872 if(this.href.length){
32873 window.location.href = this.href;
32879 if(!this.isFitContainer){
32883 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32885 window.location.href = this.href;
32888 //selection on single brick only
32889 selectBrick : function() {
32891 if (!this.parentId) {
32895 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32896 var index = m.selectedBrick.indexOf(this.id);
32899 m.selectedBrick.splice(index,1);
32900 this.el.removeClass(this.activeClass);
32904 for(var i = 0; i < m.selectedBrick.length; i++) {
32905 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32906 b.el.removeClass(b.activeClass);
32909 m.selectedBrick = [];
32911 m.selectedBrick.push(this.id);
32912 this.el.addClass(this.activeClass);
32916 isSelected : function(){
32917 return this.el.hasClass(this.activeClass);
32922 Roo.apply(Roo.bootstrap.MasonryBrick, {
32925 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32927 * register a Masonry Brick
32928 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32931 register : function(brick)
32933 //this.groups[brick.id] = brick;
32934 this.groups.add(brick.id, brick);
32937 * fetch a masonry brick based on the masonry brick ID
32938 * @param {string} the masonry brick to add
32939 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32942 get: function(brick_id)
32944 // if (typeof(this.groups[brick_id]) == 'undefined') {
32947 // return this.groups[brick_id] ;
32949 if(this.groups.key(brick_id)) {
32950 return this.groups.key(brick_id);
32968 * @class Roo.bootstrap.Brick
32969 * @extends Roo.bootstrap.Component
32970 * Bootstrap Brick class
32973 * Create a new Brick
32974 * @param {Object} config The config object
32977 Roo.bootstrap.Brick = function(config){
32978 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32984 * When a Brick is click
32985 * @param {Roo.bootstrap.Brick} this
32986 * @param {Roo.EventObject} e
32992 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32995 * @cfg {String} title
32999 * @cfg {String} html
33003 * @cfg {String} bgimage
33007 * @cfg {String} cls
33011 * @cfg {String} href
33015 * @cfg {String} video
33019 * @cfg {Boolean} square
33023 getAutoCreate : function()
33025 var cls = 'roo-brick';
33027 if(this.href.length){
33028 cls += ' roo-brick-link';
33031 if(this.bgimage.length){
33032 cls += ' roo-brick-image';
33035 if(!this.html.length && !this.bgimage.length){
33036 cls += ' roo-brick-center-title';
33039 if(!this.html.length && this.bgimage.length){
33040 cls += ' roo-brick-bottom-title';
33044 cls += ' ' + this.cls;
33048 tag: (this.href.length) ? 'a' : 'div',
33053 cls: 'roo-brick-paragraph',
33059 if(this.href.length){
33060 cfg.href = this.href;
33063 var cn = cfg.cn[0].cn;
33065 if(this.title.length){
33068 cls: 'roo-brick-title',
33073 if(this.html.length){
33076 cls: 'roo-brick-text',
33083 if(this.bgimage.length){
33086 cls: 'roo-brick-image-view',
33094 initEvents: function()
33096 if(this.title.length || this.html.length){
33097 this.el.on('mouseenter' ,this.enter, this);
33098 this.el.on('mouseleave', this.leave, this);
33101 Roo.EventManager.onWindowResize(this.resize, this);
33103 if(this.bgimage.length){
33104 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33105 this.imageEl.on('load', this.onImageLoad, this);
33112 onImageLoad : function()
33117 resize : function()
33119 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33121 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33123 if(this.bgimage.length){
33124 var image = this.el.select('.roo-brick-image-view', true).first();
33126 image.setWidth(paragraph.getWidth());
33129 image.setHeight(paragraph.getWidth());
33132 this.el.setHeight(image.getHeight());
33133 paragraph.setHeight(image.getHeight());
33139 enter: function(e, el)
33141 e.preventDefault();
33143 if(this.bgimage.length){
33144 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33145 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33149 leave: function(e, el)
33151 e.preventDefault();
33153 if(this.bgimage.length){
33154 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33155 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33170 * @class Roo.bootstrap.NumberField
33171 * @extends Roo.bootstrap.Input
33172 * Bootstrap NumberField class
33178 * Create a new NumberField
33179 * @param {Object} config The config object
33182 Roo.bootstrap.NumberField = function(config){
33183 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33186 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33189 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33191 allowDecimals : true,
33193 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33195 decimalSeparator : ".",
33197 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33199 decimalPrecision : 2,
33201 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33203 allowNegative : true,
33206 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33210 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33212 minValue : Number.NEGATIVE_INFINITY,
33214 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33216 maxValue : Number.MAX_VALUE,
33218 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33220 minText : "The minimum value for this field is {0}",
33222 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33224 maxText : "The maximum value for this field is {0}",
33226 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33227 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33229 nanText : "{0} is not a valid number",
33231 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33233 thousandsDelimiter : false,
33235 * @cfg {String} valueAlign alignment of value
33237 valueAlign : "left",
33239 getAutoCreate : function()
33241 var hiddenInput = {
33245 cls: 'hidden-number-input'
33249 hiddenInput.name = this.name;
33254 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33256 this.name = hiddenInput.name;
33258 if(cfg.cn.length > 0) {
33259 cfg.cn.push(hiddenInput);
33266 initEvents : function()
33268 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33270 var allowed = "0123456789";
33272 if(this.allowDecimals){
33273 allowed += this.decimalSeparator;
33276 if(this.allowNegative){
33280 if(this.thousandsDelimiter) {
33284 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33286 var keyPress = function(e){
33288 var k = e.getKey();
33290 var c = e.getCharCode();
33293 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33294 allowed.indexOf(String.fromCharCode(c)) === -1
33300 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33304 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33309 this.el.on("keypress", keyPress, this);
33312 validateValue : function(value)
33315 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33319 var num = this.parseValue(value);
33322 this.markInvalid(String.format(this.nanText, value));
33326 if(num < this.minValue){
33327 this.markInvalid(String.format(this.minText, this.minValue));
33331 if(num > this.maxValue){
33332 this.markInvalid(String.format(this.maxText, this.maxValue));
33339 getValue : function()
33341 var v = this.hiddenEl().getValue();
33343 return this.fixPrecision(this.parseValue(v));
33346 parseValue : function(value)
33348 if(this.thousandsDelimiter) {
33350 r = new RegExp(",", "g");
33351 value = value.replace(r, "");
33354 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33355 return isNaN(value) ? '' : value;
33358 fixPrecision : function(value)
33360 if(this.thousandsDelimiter) {
33362 r = new RegExp(",", "g");
33363 value = value.replace(r, "");
33366 var nan = isNaN(value);
33368 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33369 return nan ? '' : value;
33371 return parseFloat(value).toFixed(this.decimalPrecision);
33374 setValue : function(v)
33376 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33382 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33384 this.inputEl().dom.value = (v == '') ? '' :
33385 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33387 if(!this.allowZero && v === '0') {
33388 this.hiddenEl().dom.value = '';
33389 this.inputEl().dom.value = '';
33396 decimalPrecisionFcn : function(v)
33398 return Math.floor(v);
33401 beforeBlur : function()
33403 var v = this.parseValue(this.getRawValue());
33405 if(v || v === 0 || v === ''){
33410 hiddenEl : function()
33412 return this.el.select('input.hidden-number-input',true).first();
33424 * @class Roo.bootstrap.DocumentSlider
33425 * @extends Roo.bootstrap.Component
33426 * Bootstrap DocumentSlider class
33429 * Create a new DocumentViewer
33430 * @param {Object} config The config object
33433 Roo.bootstrap.DocumentSlider = function(config){
33434 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33441 * Fire after initEvent
33442 * @param {Roo.bootstrap.DocumentSlider} this
33447 * Fire after update
33448 * @param {Roo.bootstrap.DocumentSlider} this
33454 * @param {Roo.bootstrap.DocumentSlider} this
33460 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33466 getAutoCreate : function()
33470 cls : 'roo-document-slider',
33474 cls : 'roo-document-slider-header',
33478 cls : 'roo-document-slider-header-title'
33484 cls : 'roo-document-slider-body',
33488 cls : 'roo-document-slider-prev',
33492 cls : 'fa fa-chevron-left'
33498 cls : 'roo-document-slider-thumb',
33502 cls : 'roo-document-slider-image'
33508 cls : 'roo-document-slider-next',
33512 cls : 'fa fa-chevron-right'
33524 initEvents : function()
33526 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33527 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33529 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33530 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33532 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33533 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33535 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33536 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33538 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33539 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33541 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33542 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33544 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33545 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33547 this.thumbEl.on('click', this.onClick, this);
33549 this.prevIndicator.on('click', this.prev, this);
33551 this.nextIndicator.on('click', this.next, this);
33555 initial : function()
33557 if(this.files.length){
33558 this.indicator = 1;
33562 this.fireEvent('initial', this);
33565 update : function()
33567 this.imageEl.attr('src', this.files[this.indicator - 1]);
33569 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33571 this.prevIndicator.show();
33573 if(this.indicator == 1){
33574 this.prevIndicator.hide();
33577 this.nextIndicator.show();
33579 if(this.indicator == this.files.length){
33580 this.nextIndicator.hide();
33583 this.thumbEl.scrollTo('top');
33585 this.fireEvent('update', this);
33588 onClick : function(e)
33590 e.preventDefault();
33592 this.fireEvent('click', this);
33597 e.preventDefault();
33599 this.indicator = Math.max(1, this.indicator - 1);
33606 e.preventDefault();
33608 this.indicator = Math.min(this.files.length, this.indicator + 1);
33622 * @class Roo.bootstrap.RadioSet
33623 * @extends Roo.bootstrap.Input
33624 * Bootstrap RadioSet class
33625 * @cfg {String} indicatorpos (left|right) default left
33626 * @cfg {Boolean} inline (true|false) inline the element (default true)
33627 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33629 * Create a new RadioSet
33630 * @param {Object} config The config object
33633 Roo.bootstrap.RadioSet = function(config){
33635 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33639 Roo.bootstrap.RadioSet.register(this);
33644 * Fires when the element is checked or unchecked.
33645 * @param {Roo.bootstrap.RadioSet} this This radio
33646 * @param {Roo.bootstrap.Radio} item The checked item
33651 * Fires when the element is click.
33652 * @param {Roo.bootstrap.RadioSet} this This radio set
33653 * @param {Roo.bootstrap.Radio} item The checked item
33654 * @param {Roo.EventObject} e The event object
33661 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33669 indicatorpos : 'left',
33671 getAutoCreate : function()
33675 cls : 'roo-radio-set-label',
33679 html : this.fieldLabel
33684 if(this.indicatorpos == 'left'){
33687 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33688 tooltip : 'This field is required'
33693 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33694 tooltip : 'This field is required'
33700 cls : 'roo-radio-set-items'
33703 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33705 if (align === 'left' && this.fieldLabel.length) {
33708 cls : "roo-radio-set-right",
33714 if(this.labelWidth > 12){
33715 label.style = "width: " + this.labelWidth + 'px';
33718 if(this.labelWidth < 13 && this.labelmd == 0){
33719 this.labelmd = this.labelWidth;
33722 if(this.labellg > 0){
33723 label.cls += ' col-lg-' + this.labellg;
33724 items.cls += ' col-lg-' + (12 - this.labellg);
33727 if(this.labelmd > 0){
33728 label.cls += ' col-md-' + this.labelmd;
33729 items.cls += ' col-md-' + (12 - this.labelmd);
33732 if(this.labelsm > 0){
33733 label.cls += ' col-sm-' + this.labelsm;
33734 items.cls += ' col-sm-' + (12 - this.labelsm);
33737 if(this.labelxs > 0){
33738 label.cls += ' col-xs-' + this.labelxs;
33739 items.cls += ' col-xs-' + (12 - this.labelxs);
33745 cls : 'roo-radio-set',
33749 cls : 'roo-radio-set-input',
33752 value : this.value ? this.value : ''
33759 if(this.weight.length){
33760 cfg.cls += ' roo-radio-' + this.weight;
33764 cfg.cls += ' roo-radio-set-inline';
33768 ['xs','sm','md','lg'].map(function(size){
33769 if (settings[size]) {
33770 cfg.cls += ' col-' + size + '-' + settings[size];
33778 initEvents : function()
33780 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33781 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33783 if(!this.fieldLabel.length){
33784 this.labelEl.hide();
33787 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33788 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33790 this.indicator = this.indicatorEl();
33792 if(this.indicator){
33793 this.indicator.addClass('invisible');
33796 this.originalValue = this.getValue();
33800 inputEl: function ()
33802 return this.el.select('.roo-radio-set-input', true).first();
33805 getChildContainer : function()
33807 return this.itemsEl;
33810 register : function(item)
33812 this.radioes.push(item);
33816 validate : function()
33818 if(this.getVisibilityEl().hasClass('hidden')){
33824 Roo.each(this.radioes, function(i){
33833 if(this.allowBlank) {
33837 if(this.disabled || valid){
33842 this.markInvalid();
33847 markValid : function()
33849 if(this.labelEl.isVisible(true)){
33850 this.indicatorEl().removeClass('visible');
33851 this.indicatorEl().addClass('invisible');
33854 this.el.removeClass([this.invalidClass, this.validClass]);
33855 this.el.addClass(this.validClass);
33857 this.fireEvent('valid', this);
33860 markInvalid : function(msg)
33862 if(this.allowBlank || this.disabled){
33866 if(this.labelEl.isVisible(true)){
33867 this.indicatorEl().removeClass('invisible');
33868 this.indicatorEl().addClass('visible');
33871 this.el.removeClass([this.invalidClass, this.validClass]);
33872 this.el.addClass(this.invalidClass);
33874 this.fireEvent('invalid', this, msg);
33878 setValue : function(v, suppressEvent)
33880 if(this.value === v){
33887 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33890 Roo.each(this.radioes, function(i){
33892 i.el.removeClass('checked');
33895 Roo.each(this.radioes, function(i){
33897 if(i.value === v || i.value.toString() === v.toString()){
33899 i.el.addClass('checked');
33901 if(suppressEvent !== true){
33902 this.fireEvent('check', this, i);
33913 clearInvalid : function(){
33915 if(!this.el || this.preventMark){
33919 this.el.removeClass([this.invalidClass]);
33921 this.fireEvent('valid', this);
33926 Roo.apply(Roo.bootstrap.RadioSet, {
33930 register : function(set)
33932 this.groups[set.name] = set;
33935 get: function(name)
33937 if (typeof(this.groups[name]) == 'undefined') {
33941 return this.groups[name] ;
33947 * Ext JS Library 1.1.1
33948 * Copyright(c) 2006-2007, Ext JS, LLC.
33950 * Originally Released Under LGPL - original licence link has changed is not relivant.
33953 * <script type="text/javascript">
33958 * @class Roo.bootstrap.SplitBar
33959 * @extends Roo.util.Observable
33960 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33964 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33965 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33966 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33967 split.minSize = 100;
33968 split.maxSize = 600;
33969 split.animate = true;
33970 split.on('moved', splitterMoved);
33973 * Create a new SplitBar
33974 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33975 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33976 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33977 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33978 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33979 position of the SplitBar).
33981 Roo.bootstrap.SplitBar = function(cfg){
33986 // dragElement : elm
33987 // resizingElement: el,
33989 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33990 // placement : Roo.bootstrap.SplitBar.LEFT ,
33991 // existingProxy ???
33994 this.el = Roo.get(cfg.dragElement, true);
33995 this.el.dom.unselectable = "on";
33997 this.resizingEl = Roo.get(cfg.resizingElement, true);
34001 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34002 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34005 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34008 * The minimum size of the resizing element. (Defaults to 0)
34014 * The maximum size of the resizing element. (Defaults to 2000)
34017 this.maxSize = 2000;
34020 * Whether to animate the transition to the new size
34023 this.animate = false;
34026 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34029 this.useShim = false;
34034 if(!cfg.existingProxy){
34036 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34038 this.proxy = Roo.get(cfg.existingProxy).dom;
34041 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34044 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34047 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34050 this.dragSpecs = {};
34053 * @private The adapter to use to positon and resize elements
34055 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34056 this.adapter.init(this);
34058 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34060 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34061 this.el.addClass("roo-splitbar-h");
34064 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34065 this.el.addClass("roo-splitbar-v");
34071 * Fires when the splitter is moved (alias for {@link #event-moved})
34072 * @param {Roo.bootstrap.SplitBar} this
34073 * @param {Number} newSize the new width or height
34078 * Fires when the splitter is moved
34079 * @param {Roo.bootstrap.SplitBar} this
34080 * @param {Number} newSize the new width or height
34084 * @event beforeresize
34085 * Fires before the splitter is dragged
34086 * @param {Roo.bootstrap.SplitBar} this
34088 "beforeresize" : true,
34090 "beforeapply" : true
34093 Roo.util.Observable.call(this);
34096 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34097 onStartProxyDrag : function(x, y){
34098 this.fireEvent("beforeresize", this);
34100 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34102 o.enableDisplayMode("block");
34103 // all splitbars share the same overlay
34104 Roo.bootstrap.SplitBar.prototype.overlay = o;
34106 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34107 this.overlay.show();
34108 Roo.get(this.proxy).setDisplayed("block");
34109 var size = this.adapter.getElementSize(this);
34110 this.activeMinSize = this.getMinimumSize();;
34111 this.activeMaxSize = this.getMaximumSize();;
34112 var c1 = size - this.activeMinSize;
34113 var c2 = Math.max(this.activeMaxSize - size, 0);
34114 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34115 this.dd.resetConstraints();
34116 this.dd.setXConstraint(
34117 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34118 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34120 this.dd.setYConstraint(0, 0);
34122 this.dd.resetConstraints();
34123 this.dd.setXConstraint(0, 0);
34124 this.dd.setYConstraint(
34125 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34126 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34129 this.dragSpecs.startSize = size;
34130 this.dragSpecs.startPoint = [x, y];
34131 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34135 * @private Called after the drag operation by the DDProxy
34137 onEndProxyDrag : function(e){
34138 Roo.get(this.proxy).setDisplayed(false);
34139 var endPoint = Roo.lib.Event.getXY(e);
34141 this.overlay.hide();
34144 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34145 newSize = this.dragSpecs.startSize +
34146 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34147 endPoint[0] - this.dragSpecs.startPoint[0] :
34148 this.dragSpecs.startPoint[0] - endPoint[0]
34151 newSize = this.dragSpecs.startSize +
34152 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34153 endPoint[1] - this.dragSpecs.startPoint[1] :
34154 this.dragSpecs.startPoint[1] - endPoint[1]
34157 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34158 if(newSize != this.dragSpecs.startSize){
34159 if(this.fireEvent('beforeapply', this, newSize) !== false){
34160 this.adapter.setElementSize(this, newSize);
34161 this.fireEvent("moved", this, newSize);
34162 this.fireEvent("resize", this, newSize);
34168 * Get the adapter this SplitBar uses
34169 * @return The adapter object
34171 getAdapter : function(){
34172 return this.adapter;
34176 * Set the adapter this SplitBar uses
34177 * @param {Object} adapter A SplitBar adapter object
34179 setAdapter : function(adapter){
34180 this.adapter = adapter;
34181 this.adapter.init(this);
34185 * Gets the minimum size for the resizing element
34186 * @return {Number} The minimum size
34188 getMinimumSize : function(){
34189 return this.minSize;
34193 * Sets the minimum size for the resizing element
34194 * @param {Number} minSize The minimum size
34196 setMinimumSize : function(minSize){
34197 this.minSize = minSize;
34201 * Gets the maximum size for the resizing element
34202 * @return {Number} The maximum size
34204 getMaximumSize : function(){
34205 return this.maxSize;
34209 * Sets the maximum size for the resizing element
34210 * @param {Number} maxSize The maximum size
34212 setMaximumSize : function(maxSize){
34213 this.maxSize = maxSize;
34217 * Sets the initialize size for the resizing element
34218 * @param {Number} size The initial size
34220 setCurrentSize : function(size){
34221 var oldAnimate = this.animate;
34222 this.animate = false;
34223 this.adapter.setElementSize(this, size);
34224 this.animate = oldAnimate;
34228 * Destroy this splitbar.
34229 * @param {Boolean} removeEl True to remove the element
34231 destroy : function(removeEl){
34233 this.shim.remove();
34236 this.proxy.parentNode.removeChild(this.proxy);
34244 * @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.
34246 Roo.bootstrap.SplitBar.createProxy = function(dir){
34247 var proxy = new Roo.Element(document.createElement("div"));
34248 proxy.unselectable();
34249 var cls = 'roo-splitbar-proxy';
34250 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34251 document.body.appendChild(proxy.dom);
34256 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34257 * Default Adapter. It assumes the splitter and resizing element are not positioned
34258 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34260 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34263 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34264 // do nothing for now
34265 init : function(s){
34269 * Called before drag operations to get the current size of the resizing element.
34270 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34272 getElementSize : function(s){
34273 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34274 return s.resizingEl.getWidth();
34276 return s.resizingEl.getHeight();
34281 * Called after drag operations to set the size of the resizing element.
34282 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34283 * @param {Number} newSize The new size to set
34284 * @param {Function} onComplete A function to be invoked when resizing is complete
34286 setElementSize : function(s, newSize, onComplete){
34287 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34289 s.resizingEl.setWidth(newSize);
34291 onComplete(s, newSize);
34294 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34299 s.resizingEl.setHeight(newSize);
34301 onComplete(s, newSize);
34304 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34311 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34312 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34313 * Adapter that moves the splitter element to align with the resized sizing element.
34314 * Used with an absolute positioned SplitBar.
34315 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34316 * document.body, make sure you assign an id to the body element.
34318 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34319 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34320 this.container = Roo.get(container);
34323 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34324 init : function(s){
34325 this.basic.init(s);
34328 getElementSize : function(s){
34329 return this.basic.getElementSize(s);
34332 setElementSize : function(s, newSize, onComplete){
34333 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34336 moveSplitter : function(s){
34337 var yes = Roo.bootstrap.SplitBar;
34338 switch(s.placement){
34340 s.el.setX(s.resizingEl.getRight());
34343 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34346 s.el.setY(s.resizingEl.getBottom());
34349 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34356 * Orientation constant - Create a vertical SplitBar
34360 Roo.bootstrap.SplitBar.VERTICAL = 1;
34363 * Orientation constant - Create a horizontal SplitBar
34367 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34370 * Placement constant - The resizing element is to the left of the splitter element
34374 Roo.bootstrap.SplitBar.LEFT = 1;
34377 * Placement constant - The resizing element is to the right of the splitter element
34381 Roo.bootstrap.SplitBar.RIGHT = 2;
34384 * Placement constant - The resizing element is positioned above the splitter element
34388 Roo.bootstrap.SplitBar.TOP = 3;
34391 * Placement constant - The resizing element is positioned under splitter element
34395 Roo.bootstrap.SplitBar.BOTTOM = 4;
34396 Roo.namespace("Roo.bootstrap.layout");/*
34398 * Ext JS Library 1.1.1
34399 * Copyright(c) 2006-2007, Ext JS, LLC.
34401 * Originally Released Under LGPL - original licence link has changed is not relivant.
34404 * <script type="text/javascript">
34408 * @class Roo.bootstrap.layout.Manager
34409 * @extends Roo.bootstrap.Component
34410 * Base class for layout managers.
34412 Roo.bootstrap.layout.Manager = function(config)
34414 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34420 /** false to disable window resize monitoring @type Boolean */
34421 this.monitorWindowResize = true;
34426 * Fires when a layout is performed.
34427 * @param {Roo.LayoutManager} this
34431 * @event regionresized
34432 * Fires when the user resizes a region.
34433 * @param {Roo.LayoutRegion} region The resized region
34434 * @param {Number} newSize The new size (width for east/west, height for north/south)
34436 "regionresized" : true,
34438 * @event regioncollapsed
34439 * Fires when a region is collapsed.
34440 * @param {Roo.LayoutRegion} region The collapsed region
34442 "regioncollapsed" : true,
34444 * @event regionexpanded
34445 * Fires when a region is expanded.
34446 * @param {Roo.LayoutRegion} region The expanded region
34448 "regionexpanded" : true
34450 this.updating = false;
34453 this.el = Roo.get(config.el);
34459 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34464 monitorWindowResize : true,
34470 onRender : function(ct, position)
34473 this.el = Roo.get(ct);
34476 //this.fireEvent('render',this);
34480 initEvents: function()
34484 // ie scrollbar fix
34485 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34486 document.body.scroll = "no";
34487 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34488 this.el.position('relative');
34490 this.id = this.el.id;
34491 this.el.addClass("roo-layout-container");
34492 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34493 if(this.el.dom != document.body ) {
34494 this.el.on('resize', this.layout,this);
34495 this.el.on('show', this.layout,this);
34501 * Returns true if this layout is currently being updated
34502 * @return {Boolean}
34504 isUpdating : function(){
34505 return this.updating;
34509 * Suspend the LayoutManager from doing auto-layouts while
34510 * making multiple add or remove calls
34512 beginUpdate : function(){
34513 this.updating = true;
34517 * Restore auto-layouts and optionally disable the manager from performing a layout
34518 * @param {Boolean} noLayout true to disable a layout update
34520 endUpdate : function(noLayout){
34521 this.updating = false;
34527 layout: function(){
34531 onRegionResized : function(region, newSize){
34532 this.fireEvent("regionresized", region, newSize);
34536 onRegionCollapsed : function(region){
34537 this.fireEvent("regioncollapsed", region);
34540 onRegionExpanded : function(region){
34541 this.fireEvent("regionexpanded", region);
34545 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34546 * performs box-model adjustments.
34547 * @return {Object} The size as an object {width: (the width), height: (the height)}
34549 getViewSize : function()
34552 if(this.el.dom != document.body){
34553 size = this.el.getSize();
34555 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34557 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34558 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34563 * Returns the Element this layout is bound to.
34564 * @return {Roo.Element}
34566 getEl : function(){
34571 * Returns the specified region.
34572 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34573 * @return {Roo.LayoutRegion}
34575 getRegion : function(target){
34576 return this.regions[target.toLowerCase()];
34579 onWindowResize : function(){
34580 if(this.monitorWindowResize){
34587 * Ext JS Library 1.1.1
34588 * Copyright(c) 2006-2007, Ext JS, LLC.
34590 * Originally Released Under LGPL - original licence link has changed is not relivant.
34593 * <script type="text/javascript">
34596 * @class Roo.bootstrap.layout.Border
34597 * @extends Roo.bootstrap.layout.Manager
34598 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34599 * please see: examples/bootstrap/nested.html<br><br>
34601 <b>The container the layout is rendered into can be either the body element or any other element.
34602 If it is not the body element, the container needs to either be an absolute positioned element,
34603 or you will need to add "position:relative" to the css of the container. You will also need to specify
34604 the container size if it is not the body element.</b>
34607 * Create a new Border
34608 * @param {Object} config Configuration options
34610 Roo.bootstrap.layout.Border = function(config){
34611 config = config || {};
34612 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34616 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34617 if(config[region]){
34618 config[region].region = region;
34619 this.addRegion(config[region]);
34625 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34627 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34629 * Creates and adds a new region if it doesn't already exist.
34630 * @param {String} target The target region key (north, south, east, west or center).
34631 * @param {Object} config The regions config object
34632 * @return {BorderLayoutRegion} The new region
34634 addRegion : function(config)
34636 if(!this.regions[config.region]){
34637 var r = this.factory(config);
34638 this.bindRegion(r);
34640 return this.regions[config.region];
34644 bindRegion : function(r){
34645 this.regions[r.config.region] = r;
34647 r.on("visibilitychange", this.layout, this);
34648 r.on("paneladded", this.layout, this);
34649 r.on("panelremoved", this.layout, this);
34650 r.on("invalidated", this.layout, this);
34651 r.on("resized", this.onRegionResized, this);
34652 r.on("collapsed", this.onRegionCollapsed, this);
34653 r.on("expanded", this.onRegionExpanded, this);
34657 * Performs a layout update.
34659 layout : function()
34661 if(this.updating) {
34665 // render all the rebions if they have not been done alreayd?
34666 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34667 if(this.regions[region] && !this.regions[region].bodyEl){
34668 this.regions[region].onRender(this.el)
34672 var size = this.getViewSize();
34673 var w = size.width;
34674 var h = size.height;
34679 //var x = 0, y = 0;
34681 var rs = this.regions;
34682 var north = rs["north"];
34683 var south = rs["south"];
34684 var west = rs["west"];
34685 var east = rs["east"];
34686 var center = rs["center"];
34687 //if(this.hideOnLayout){ // not supported anymore
34688 //c.el.setStyle("display", "none");
34690 if(north && north.isVisible()){
34691 var b = north.getBox();
34692 var m = north.getMargins();
34693 b.width = w - (m.left+m.right);
34696 centerY = b.height + b.y + m.bottom;
34697 centerH -= centerY;
34698 north.updateBox(this.safeBox(b));
34700 if(south && south.isVisible()){
34701 var b = south.getBox();
34702 var m = south.getMargins();
34703 b.width = w - (m.left+m.right);
34705 var totalHeight = (b.height + m.top + m.bottom);
34706 b.y = h - totalHeight + m.top;
34707 centerH -= totalHeight;
34708 south.updateBox(this.safeBox(b));
34710 if(west && west.isVisible()){
34711 var b = west.getBox();
34712 var m = west.getMargins();
34713 b.height = centerH - (m.top+m.bottom);
34715 b.y = centerY + m.top;
34716 var totalWidth = (b.width + m.left + m.right);
34717 centerX += totalWidth;
34718 centerW -= totalWidth;
34719 west.updateBox(this.safeBox(b));
34721 if(east && east.isVisible()){
34722 var b = east.getBox();
34723 var m = east.getMargins();
34724 b.height = centerH - (m.top+m.bottom);
34725 var totalWidth = (b.width + m.left + m.right);
34726 b.x = w - totalWidth + m.left;
34727 b.y = centerY + m.top;
34728 centerW -= totalWidth;
34729 east.updateBox(this.safeBox(b));
34732 var m = center.getMargins();
34734 x: centerX + m.left,
34735 y: centerY + m.top,
34736 width: centerW - (m.left+m.right),
34737 height: centerH - (m.top+m.bottom)
34739 //if(this.hideOnLayout){
34740 //center.el.setStyle("display", "block");
34742 center.updateBox(this.safeBox(centerBox));
34745 this.fireEvent("layout", this);
34749 safeBox : function(box){
34750 box.width = Math.max(0, box.width);
34751 box.height = Math.max(0, box.height);
34756 * Adds a ContentPanel (or subclass) to this layout.
34757 * @param {String} target The target region key (north, south, east, west or center).
34758 * @param {Roo.ContentPanel} panel The panel to add
34759 * @return {Roo.ContentPanel} The added panel
34761 add : function(target, panel){
34763 target = target.toLowerCase();
34764 return this.regions[target].add(panel);
34768 * Remove a ContentPanel (or subclass) to this layout.
34769 * @param {String} target The target region key (north, south, east, west or center).
34770 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34771 * @return {Roo.ContentPanel} The removed panel
34773 remove : function(target, panel){
34774 target = target.toLowerCase();
34775 return this.regions[target].remove(panel);
34779 * Searches all regions for a panel with the specified id
34780 * @param {String} panelId
34781 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34783 findPanel : function(panelId){
34784 var rs = this.regions;
34785 for(var target in rs){
34786 if(typeof rs[target] != "function"){
34787 var p = rs[target].getPanel(panelId);
34797 * Searches all regions for a panel with the specified id and activates (shows) it.
34798 * @param {String/ContentPanel} panelId The panels id or the panel itself
34799 * @return {Roo.ContentPanel} The shown panel or null
34801 showPanel : function(panelId) {
34802 var rs = this.regions;
34803 for(var target in rs){
34804 var r = rs[target];
34805 if(typeof r != "function"){
34806 if(r.hasPanel(panelId)){
34807 return r.showPanel(panelId);
34815 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34816 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34819 restoreState : function(provider){
34821 provider = Roo.state.Manager;
34823 var sm = new Roo.LayoutStateManager();
34824 sm.init(this, provider);
34830 * Adds a xtype elements to the layout.
34834 xtype : 'ContentPanel',
34841 xtype : 'NestedLayoutPanel',
34847 items : [ ... list of content panels or nested layout panels.. ]
34851 * @param {Object} cfg Xtype definition of item to add.
34853 addxtype : function(cfg)
34855 // basically accepts a pannel...
34856 // can accept a layout region..!?!?
34857 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34860 // theory? children can only be panels??
34862 //if (!cfg.xtype.match(/Panel$/)) {
34867 if (typeof(cfg.region) == 'undefined') {
34868 Roo.log("Failed to add Panel, region was not set");
34872 var region = cfg.region;
34878 xitems = cfg.items;
34885 case 'Content': // ContentPanel (el, cfg)
34886 case 'Scroll': // ContentPanel (el, cfg)
34888 cfg.autoCreate = true;
34889 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34891 // var el = this.el.createChild();
34892 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34895 this.add(region, ret);
34899 case 'TreePanel': // our new panel!
34900 cfg.el = this.el.createChild();
34901 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34902 this.add(region, ret);
34907 // create a new Layout (which is a Border Layout...
34909 var clayout = cfg.layout;
34910 clayout.el = this.el.createChild();
34911 clayout.items = clayout.items || [];
34915 // replace this exitems with the clayout ones..
34916 xitems = clayout.items;
34918 // force background off if it's in center...
34919 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34920 cfg.background = false;
34922 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34925 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34926 //console.log('adding nested layout panel ' + cfg.toSource());
34927 this.add(region, ret);
34928 nb = {}; /// find first...
34933 // needs grid and region
34935 //var el = this.getRegion(region).el.createChild();
34937 *var el = this.el.createChild();
34938 // create the grid first...
34939 cfg.grid.container = el;
34940 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34943 if (region == 'center' && this.active ) {
34944 cfg.background = false;
34947 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34949 this.add(region, ret);
34951 if (cfg.background) {
34952 // render grid on panel activation (if panel background)
34953 ret.on('activate', function(gp) {
34954 if (!gp.grid.rendered) {
34955 // gp.grid.render(el);
34959 // cfg.grid.render(el);
34965 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34966 // it was the old xcomponent building that caused this before.
34967 // espeically if border is the top element in the tree.
34977 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34979 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34980 this.add(region, ret);
34984 throw "Can not add '" + cfg.xtype + "' to Border";
34990 this.beginUpdate();
34994 Roo.each(xitems, function(i) {
34995 region = nb && i.region ? i.region : false;
34997 var add = ret.addxtype(i);
35000 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35001 if (!i.background) {
35002 abn[region] = nb[region] ;
35009 // make the last non-background panel active..
35010 //if (nb) { Roo.log(abn); }
35013 for(var r in abn) {
35014 region = this.getRegion(r);
35016 // tried using nb[r], but it does not work..
35018 region.showPanel(abn[r]);
35029 factory : function(cfg)
35032 var validRegions = Roo.bootstrap.layout.Border.regions;
35034 var target = cfg.region;
35037 var r = Roo.bootstrap.layout;
35041 return new r.North(cfg);
35043 return new r.South(cfg);
35045 return new r.East(cfg);
35047 return new r.West(cfg);
35049 return new r.Center(cfg);
35051 throw 'Layout region "'+target+'" not supported.';
35058 * Ext JS Library 1.1.1
35059 * Copyright(c) 2006-2007, Ext JS, LLC.
35061 * Originally Released Under LGPL - original licence link has changed is not relivant.
35064 * <script type="text/javascript">
35068 * @class Roo.bootstrap.layout.Basic
35069 * @extends Roo.util.Observable
35070 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35071 * and does not have a titlebar, tabs or any other features. All it does is size and position
35072 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35073 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35074 * @cfg {string} region the region that it inhabits..
35075 * @cfg {bool} skipConfig skip config?
35079 Roo.bootstrap.layout.Basic = function(config){
35081 this.mgr = config.mgr;
35083 this.position = config.region;
35085 var skipConfig = config.skipConfig;
35089 * @scope Roo.BasicLayoutRegion
35093 * @event beforeremove
35094 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35095 * @param {Roo.LayoutRegion} this
35096 * @param {Roo.ContentPanel} panel The panel
35097 * @param {Object} e The cancel event object
35099 "beforeremove" : true,
35101 * @event invalidated
35102 * Fires when the layout for this region is changed.
35103 * @param {Roo.LayoutRegion} this
35105 "invalidated" : true,
35107 * @event visibilitychange
35108 * Fires when this region is shown or hidden
35109 * @param {Roo.LayoutRegion} this
35110 * @param {Boolean} visibility true or false
35112 "visibilitychange" : true,
35114 * @event paneladded
35115 * Fires when a panel is added.
35116 * @param {Roo.LayoutRegion} this
35117 * @param {Roo.ContentPanel} panel The panel
35119 "paneladded" : true,
35121 * @event panelremoved
35122 * Fires when a panel is removed.
35123 * @param {Roo.LayoutRegion} this
35124 * @param {Roo.ContentPanel} panel The panel
35126 "panelremoved" : true,
35128 * @event beforecollapse
35129 * Fires when this region before collapse.
35130 * @param {Roo.LayoutRegion} this
35132 "beforecollapse" : true,
35135 * Fires when this region is collapsed.
35136 * @param {Roo.LayoutRegion} this
35138 "collapsed" : true,
35141 * Fires when this region is expanded.
35142 * @param {Roo.LayoutRegion} this
35147 * Fires when this region is slid into view.
35148 * @param {Roo.LayoutRegion} this
35150 "slideshow" : true,
35153 * Fires when this region slides out of view.
35154 * @param {Roo.LayoutRegion} this
35156 "slidehide" : true,
35158 * @event panelactivated
35159 * Fires when a panel is activated.
35160 * @param {Roo.LayoutRegion} this
35161 * @param {Roo.ContentPanel} panel The activated panel
35163 "panelactivated" : true,
35166 * Fires when the user resizes this region.
35167 * @param {Roo.LayoutRegion} this
35168 * @param {Number} newSize The new size (width for east/west, height for north/south)
35172 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35173 this.panels = new Roo.util.MixedCollection();
35174 this.panels.getKey = this.getPanelId.createDelegate(this);
35176 this.activePanel = null;
35177 // ensure listeners are added...
35179 if (config.listeners || config.events) {
35180 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35181 listeners : config.listeners || {},
35182 events : config.events || {}
35186 if(skipConfig !== true){
35187 this.applyConfig(config);
35191 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35193 getPanelId : function(p){
35197 applyConfig : function(config){
35198 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35199 this.config = config;
35204 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35205 * the width, for horizontal (north, south) the height.
35206 * @param {Number} newSize The new width or height
35208 resizeTo : function(newSize){
35209 var el = this.el ? this.el :
35210 (this.activePanel ? this.activePanel.getEl() : null);
35212 switch(this.position){
35215 el.setWidth(newSize);
35216 this.fireEvent("resized", this, newSize);
35220 el.setHeight(newSize);
35221 this.fireEvent("resized", this, newSize);
35227 getBox : function(){
35228 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35231 getMargins : function(){
35232 return this.margins;
35235 updateBox : function(box){
35237 var el = this.activePanel.getEl();
35238 el.dom.style.left = box.x + "px";
35239 el.dom.style.top = box.y + "px";
35240 this.activePanel.setSize(box.width, box.height);
35244 * Returns the container element for this region.
35245 * @return {Roo.Element}
35247 getEl : function(){
35248 return this.activePanel;
35252 * Returns true if this region is currently visible.
35253 * @return {Boolean}
35255 isVisible : function(){
35256 return this.activePanel ? true : false;
35259 setActivePanel : function(panel){
35260 panel = this.getPanel(panel);
35261 if(this.activePanel && this.activePanel != panel){
35262 this.activePanel.setActiveState(false);
35263 this.activePanel.getEl().setLeftTop(-10000,-10000);
35265 this.activePanel = panel;
35266 panel.setActiveState(true);
35268 panel.setSize(this.box.width, this.box.height);
35270 this.fireEvent("panelactivated", this, panel);
35271 this.fireEvent("invalidated");
35275 * Show the specified panel.
35276 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35277 * @return {Roo.ContentPanel} The shown panel or null
35279 showPanel : function(panel){
35280 panel = this.getPanel(panel);
35282 this.setActivePanel(panel);
35288 * Get the active panel for this region.
35289 * @return {Roo.ContentPanel} The active panel or null
35291 getActivePanel : function(){
35292 return this.activePanel;
35296 * Add the passed ContentPanel(s)
35297 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35298 * @return {Roo.ContentPanel} The panel added (if only one was added)
35300 add : function(panel){
35301 if(arguments.length > 1){
35302 for(var i = 0, len = arguments.length; i < len; i++) {
35303 this.add(arguments[i]);
35307 if(this.hasPanel(panel)){
35308 this.showPanel(panel);
35311 var el = panel.getEl();
35312 if(el.dom.parentNode != this.mgr.el.dom){
35313 this.mgr.el.dom.appendChild(el.dom);
35315 if(panel.setRegion){
35316 panel.setRegion(this);
35318 this.panels.add(panel);
35319 el.setStyle("position", "absolute");
35320 if(!panel.background){
35321 this.setActivePanel(panel);
35322 if(this.config.initialSize && this.panels.getCount()==1){
35323 this.resizeTo(this.config.initialSize);
35326 this.fireEvent("paneladded", this, panel);
35331 * Returns true if the panel is in this region.
35332 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35333 * @return {Boolean}
35335 hasPanel : function(panel){
35336 if(typeof panel == "object"){ // must be panel obj
35337 panel = panel.getId();
35339 return this.getPanel(panel) ? true : false;
35343 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35344 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35345 * @param {Boolean} preservePanel Overrides the config preservePanel option
35346 * @return {Roo.ContentPanel} The panel that was removed
35348 remove : function(panel, preservePanel){
35349 panel = this.getPanel(panel);
35354 this.fireEvent("beforeremove", this, panel, e);
35355 if(e.cancel === true){
35358 var panelId = panel.getId();
35359 this.panels.removeKey(panelId);
35364 * Returns the panel specified or null if it's not in this region.
35365 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35366 * @return {Roo.ContentPanel}
35368 getPanel : function(id){
35369 if(typeof id == "object"){ // must be panel obj
35372 return this.panels.get(id);
35376 * Returns this regions position (north/south/east/west/center).
35379 getPosition: function(){
35380 return this.position;
35384 * Ext JS Library 1.1.1
35385 * Copyright(c) 2006-2007, Ext JS, LLC.
35387 * Originally Released Under LGPL - original licence link has changed is not relivant.
35390 * <script type="text/javascript">
35394 * @class Roo.bootstrap.layout.Region
35395 * @extends Roo.bootstrap.layout.Basic
35396 * This class represents a region in a layout manager.
35398 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35399 * @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})
35400 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35401 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35402 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35403 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35404 * @cfg {String} title The title for the region (overrides panel titles)
35405 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35406 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35407 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35408 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35409 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35410 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35411 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35412 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35413 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35414 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35416 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35417 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35418 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35419 * @cfg {Number} width For East/West panels
35420 * @cfg {Number} height For North/South panels
35421 * @cfg {Boolean} split To show the splitter
35422 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35424 * @cfg {string} cls Extra CSS classes to add to region
35426 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35427 * @cfg {string} region the region that it inhabits..
35430 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35431 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35433 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35434 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35435 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35437 Roo.bootstrap.layout.Region = function(config)
35439 this.applyConfig(config);
35441 var mgr = config.mgr;
35442 var pos = config.region;
35443 config.skipConfig = true;
35444 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35447 this.onRender(mgr.el);
35450 this.visible = true;
35451 this.collapsed = false;
35452 this.unrendered_panels = [];
35455 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35457 position: '', // set by wrapper (eg. north/south etc..)
35458 unrendered_panels : null, // unrendered panels.
35459 createBody : function(){
35460 /** This region's body element
35461 * @type Roo.Element */
35462 this.bodyEl = this.el.createChild({
35464 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35468 onRender: function(ctr, pos)
35470 var dh = Roo.DomHelper;
35471 /** This region's container element
35472 * @type Roo.Element */
35473 this.el = dh.append(ctr.dom, {
35475 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35477 /** This region's title element
35478 * @type Roo.Element */
35480 this.titleEl = dh.append(this.el.dom,
35483 unselectable: "on",
35484 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35486 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35487 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35490 this.titleEl.enableDisplayMode();
35491 /** This region's title text element
35492 * @type HTMLElement */
35493 this.titleTextEl = this.titleEl.dom.firstChild;
35494 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35496 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35497 this.closeBtn.enableDisplayMode();
35498 this.closeBtn.on("click", this.closeClicked, this);
35499 this.closeBtn.hide();
35501 this.createBody(this.config);
35502 if(this.config.hideWhenEmpty){
35504 this.on("paneladded", this.validateVisibility, this);
35505 this.on("panelremoved", this.validateVisibility, this);
35507 if(this.autoScroll){
35508 this.bodyEl.setStyle("overflow", "auto");
35510 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35512 //if(c.titlebar !== false){
35513 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35514 this.titleEl.hide();
35516 this.titleEl.show();
35517 if(this.config.title){
35518 this.titleTextEl.innerHTML = this.config.title;
35522 if(this.config.collapsed){
35523 this.collapse(true);
35525 if(this.config.hidden){
35529 if (this.unrendered_panels && this.unrendered_panels.length) {
35530 for (var i =0;i< this.unrendered_panels.length; i++) {
35531 this.add(this.unrendered_panels[i]);
35533 this.unrendered_panels = null;
35539 applyConfig : function(c)
35542 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35543 var dh = Roo.DomHelper;
35544 if(c.titlebar !== false){
35545 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35546 this.collapseBtn.on("click", this.collapse, this);
35547 this.collapseBtn.enableDisplayMode();
35549 if(c.showPin === true || this.showPin){
35550 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35551 this.stickBtn.enableDisplayMode();
35552 this.stickBtn.on("click", this.expand, this);
35553 this.stickBtn.hide();
35558 /** This region's collapsed element
35559 * @type Roo.Element */
35562 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35563 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35566 if(c.floatable !== false){
35567 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35568 this.collapsedEl.on("click", this.collapseClick, this);
35571 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35572 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35573 id: "message", unselectable: "on", style:{"float":"left"}});
35574 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35576 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35577 this.expandBtn.on("click", this.expand, this);
35581 if(this.collapseBtn){
35582 this.collapseBtn.setVisible(c.collapsible == true);
35585 this.cmargins = c.cmargins || this.cmargins ||
35586 (this.position == "west" || this.position == "east" ?
35587 {top: 0, left: 2, right:2, bottom: 0} :
35588 {top: 2, left: 0, right:0, bottom: 2});
35590 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35593 this.bottomTabs = c.tabPosition != "top";
35595 this.autoScroll = c.autoScroll || false;
35600 this.duration = c.duration || .30;
35601 this.slideDuration = c.slideDuration || .45;
35606 * Returns true if this region is currently visible.
35607 * @return {Boolean}
35609 isVisible : function(){
35610 return this.visible;
35614 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35615 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35617 //setCollapsedTitle : function(title){
35618 // title = title || " ";
35619 // if(this.collapsedTitleTextEl){
35620 // this.collapsedTitleTextEl.innerHTML = title;
35624 getBox : function(){
35626 // if(!this.collapsed){
35627 b = this.el.getBox(false, true);
35629 // b = this.collapsedEl.getBox(false, true);
35634 getMargins : function(){
35635 return this.margins;
35636 //return this.collapsed ? this.cmargins : this.margins;
35639 highlight : function(){
35640 this.el.addClass("x-layout-panel-dragover");
35643 unhighlight : function(){
35644 this.el.removeClass("x-layout-panel-dragover");
35647 updateBox : function(box)
35649 if (!this.bodyEl) {
35650 return; // not rendered yet..
35654 if(!this.collapsed){
35655 this.el.dom.style.left = box.x + "px";
35656 this.el.dom.style.top = box.y + "px";
35657 this.updateBody(box.width, box.height);
35659 this.collapsedEl.dom.style.left = box.x + "px";
35660 this.collapsedEl.dom.style.top = box.y + "px";
35661 this.collapsedEl.setSize(box.width, box.height);
35664 this.tabs.autoSizeTabs();
35668 updateBody : function(w, h)
35671 this.el.setWidth(w);
35672 w -= this.el.getBorderWidth("rl");
35673 if(this.config.adjustments){
35674 w += this.config.adjustments[0];
35677 if(h !== null && h > 0){
35678 this.el.setHeight(h);
35679 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35680 h -= this.el.getBorderWidth("tb");
35681 if(this.config.adjustments){
35682 h += this.config.adjustments[1];
35684 this.bodyEl.setHeight(h);
35686 h = this.tabs.syncHeight(h);
35689 if(this.panelSize){
35690 w = w !== null ? w : this.panelSize.width;
35691 h = h !== null ? h : this.panelSize.height;
35693 if(this.activePanel){
35694 var el = this.activePanel.getEl();
35695 w = w !== null ? w : el.getWidth();
35696 h = h !== null ? h : el.getHeight();
35697 this.panelSize = {width: w, height: h};
35698 this.activePanel.setSize(w, h);
35700 if(Roo.isIE && this.tabs){
35701 this.tabs.el.repaint();
35706 * Returns the container element for this region.
35707 * @return {Roo.Element}
35709 getEl : function(){
35714 * Hides this region.
35717 //if(!this.collapsed){
35718 this.el.dom.style.left = "-2000px";
35721 // this.collapsedEl.dom.style.left = "-2000px";
35722 // this.collapsedEl.hide();
35724 this.visible = false;
35725 this.fireEvent("visibilitychange", this, false);
35729 * Shows this region if it was previously hidden.
35732 //if(!this.collapsed){
35735 // this.collapsedEl.show();
35737 this.visible = true;
35738 this.fireEvent("visibilitychange", this, true);
35741 closeClicked : function(){
35742 if(this.activePanel){
35743 this.remove(this.activePanel);
35747 collapseClick : function(e){
35749 e.stopPropagation();
35752 e.stopPropagation();
35758 * Collapses this region.
35759 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35762 collapse : function(skipAnim, skipCheck = false){
35763 if(this.collapsed) {
35767 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35769 this.collapsed = true;
35771 this.split.el.hide();
35773 if(this.config.animate && skipAnim !== true){
35774 this.fireEvent("invalidated", this);
35775 this.animateCollapse();
35777 this.el.setLocation(-20000,-20000);
35779 this.collapsedEl.show();
35780 this.fireEvent("collapsed", this);
35781 this.fireEvent("invalidated", this);
35787 animateCollapse : function(){
35792 * Expands this region if it was previously collapsed.
35793 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35794 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35797 expand : function(e, skipAnim){
35799 e.stopPropagation();
35801 if(!this.collapsed || this.el.hasActiveFx()) {
35805 this.afterSlideIn();
35808 this.collapsed = false;
35809 if(this.config.animate && skipAnim !== true){
35810 this.animateExpand();
35814 this.split.el.show();
35816 this.collapsedEl.setLocation(-2000,-2000);
35817 this.collapsedEl.hide();
35818 this.fireEvent("invalidated", this);
35819 this.fireEvent("expanded", this);
35823 animateExpand : function(){
35827 initTabs : function()
35829 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35831 var ts = new Roo.bootstrap.panel.Tabs({
35832 el: this.bodyEl.dom,
35833 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35834 disableTooltips: this.config.disableTabTips,
35835 toolbar : this.config.toolbar
35838 if(this.config.hideTabs){
35839 ts.stripWrap.setDisplayed(false);
35842 ts.resizeTabs = this.config.resizeTabs === true;
35843 ts.minTabWidth = this.config.minTabWidth || 40;
35844 ts.maxTabWidth = this.config.maxTabWidth || 250;
35845 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35846 ts.monitorResize = false;
35847 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35848 ts.bodyEl.addClass('roo-layout-tabs-body');
35849 this.panels.each(this.initPanelAsTab, this);
35852 initPanelAsTab : function(panel){
35853 var ti = this.tabs.addTab(
35857 this.config.closeOnTab && panel.isClosable(),
35860 if(panel.tabTip !== undefined){
35861 ti.setTooltip(panel.tabTip);
35863 ti.on("activate", function(){
35864 this.setActivePanel(panel);
35867 if(this.config.closeOnTab){
35868 ti.on("beforeclose", function(t, e){
35870 this.remove(panel);
35874 panel.tabItem = ti;
35879 updatePanelTitle : function(panel, title)
35881 if(this.activePanel == panel){
35882 this.updateTitle(title);
35885 var ti = this.tabs.getTab(panel.getEl().id);
35887 if(panel.tabTip !== undefined){
35888 ti.setTooltip(panel.tabTip);
35893 updateTitle : function(title){
35894 if(this.titleTextEl && !this.config.title){
35895 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35899 setActivePanel : function(panel)
35901 panel = this.getPanel(panel);
35902 if(this.activePanel && this.activePanel != panel){
35903 if(this.activePanel.setActiveState(false) === false){
35907 this.activePanel = panel;
35908 panel.setActiveState(true);
35909 if(this.panelSize){
35910 panel.setSize(this.panelSize.width, this.panelSize.height);
35913 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35915 this.updateTitle(panel.getTitle());
35917 this.fireEvent("invalidated", this);
35919 this.fireEvent("panelactivated", this, panel);
35923 * Shows the specified panel.
35924 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35925 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35927 showPanel : function(panel)
35929 panel = this.getPanel(panel);
35932 var tab = this.tabs.getTab(panel.getEl().id);
35933 if(tab.isHidden()){
35934 this.tabs.unhideTab(tab.id);
35938 this.setActivePanel(panel);
35945 * Get the active panel for this region.
35946 * @return {Roo.ContentPanel} The active panel or null
35948 getActivePanel : function(){
35949 return this.activePanel;
35952 validateVisibility : function(){
35953 if(this.panels.getCount() < 1){
35954 this.updateTitle(" ");
35955 this.closeBtn.hide();
35958 if(!this.isVisible()){
35965 * Adds the passed ContentPanel(s) to this region.
35966 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35967 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35969 add : function(panel)
35971 if(arguments.length > 1){
35972 for(var i = 0, len = arguments.length; i < len; i++) {
35973 this.add(arguments[i]);
35978 // if we have not been rendered yet, then we can not really do much of this..
35979 if (!this.bodyEl) {
35980 this.unrendered_panels.push(panel);
35987 if(this.hasPanel(panel)){
35988 this.showPanel(panel);
35991 panel.setRegion(this);
35992 this.panels.add(panel);
35993 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35994 // sinle panel - no tab...?? would it not be better to render it with the tabs,
35995 // and hide them... ???
35996 this.bodyEl.dom.appendChild(panel.getEl().dom);
35997 if(panel.background !== true){
35998 this.setActivePanel(panel);
36000 this.fireEvent("paneladded", this, panel);
36007 this.initPanelAsTab(panel);
36011 if(panel.background !== true){
36012 this.tabs.activate(panel.getEl().id);
36014 this.fireEvent("paneladded", this, panel);
36019 * Hides the tab for the specified panel.
36020 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36022 hidePanel : function(panel){
36023 if(this.tabs && (panel = this.getPanel(panel))){
36024 this.tabs.hideTab(panel.getEl().id);
36029 * Unhides the tab for a previously hidden panel.
36030 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36032 unhidePanel : function(panel){
36033 if(this.tabs && (panel = this.getPanel(panel))){
36034 this.tabs.unhideTab(panel.getEl().id);
36038 clearPanels : function(){
36039 while(this.panels.getCount() > 0){
36040 this.remove(this.panels.first());
36045 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36046 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36047 * @param {Boolean} preservePanel Overrides the config preservePanel option
36048 * @return {Roo.ContentPanel} The panel that was removed
36050 remove : function(panel, preservePanel)
36052 panel = this.getPanel(panel);
36057 this.fireEvent("beforeremove", this, panel, e);
36058 if(e.cancel === true){
36061 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36062 var panelId = panel.getId();
36063 this.panels.removeKey(panelId);
36065 document.body.appendChild(panel.getEl().dom);
36068 this.tabs.removeTab(panel.getEl().id);
36069 }else if (!preservePanel){
36070 this.bodyEl.dom.removeChild(panel.getEl().dom);
36072 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36073 var p = this.panels.first();
36074 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36075 tempEl.appendChild(p.getEl().dom);
36076 this.bodyEl.update("");
36077 this.bodyEl.dom.appendChild(p.getEl().dom);
36079 this.updateTitle(p.getTitle());
36081 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36082 this.setActivePanel(p);
36084 panel.setRegion(null);
36085 if(this.activePanel == panel){
36086 this.activePanel = null;
36088 if(this.config.autoDestroy !== false && preservePanel !== true){
36089 try{panel.destroy();}catch(e){}
36091 this.fireEvent("panelremoved", this, panel);
36096 * Returns the TabPanel component used by this region
36097 * @return {Roo.TabPanel}
36099 getTabs : function(){
36103 createTool : function(parentEl, className){
36104 var btn = Roo.DomHelper.append(parentEl, {
36106 cls: "x-layout-tools-button",
36109 cls: "roo-layout-tools-button-inner " + className,
36113 btn.addClassOnOver("roo-layout-tools-button-over");
36118 * Ext JS Library 1.1.1
36119 * Copyright(c) 2006-2007, Ext JS, LLC.
36121 * Originally Released Under LGPL - original licence link has changed is not relivant.
36124 * <script type="text/javascript">
36130 * @class Roo.SplitLayoutRegion
36131 * @extends Roo.LayoutRegion
36132 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36134 Roo.bootstrap.layout.Split = function(config){
36135 this.cursor = config.cursor;
36136 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36139 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36141 splitTip : "Drag to resize.",
36142 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36143 useSplitTips : false,
36145 applyConfig : function(config){
36146 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36149 onRender : function(ctr,pos) {
36151 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36152 if(!this.config.split){
36157 var splitEl = Roo.DomHelper.append(ctr.dom, {
36159 id: this.el.id + "-split",
36160 cls: "roo-layout-split roo-layout-split-"+this.position,
36163 /** The SplitBar for this region
36164 * @type Roo.SplitBar */
36165 // does not exist yet...
36166 Roo.log([this.position, this.orientation]);
36168 this.split = new Roo.bootstrap.SplitBar({
36169 dragElement : splitEl,
36170 resizingElement: this.el,
36171 orientation : this.orientation
36174 this.split.on("moved", this.onSplitMove, this);
36175 this.split.useShim = this.config.useShim === true;
36176 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36177 if(this.useSplitTips){
36178 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36180 //if(config.collapsible){
36181 // this.split.el.on("dblclick", this.collapse, this);
36184 if(typeof this.config.minSize != "undefined"){
36185 this.split.minSize = this.config.minSize;
36187 if(typeof this.config.maxSize != "undefined"){
36188 this.split.maxSize = this.config.maxSize;
36190 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36191 this.hideSplitter();
36196 getHMaxSize : function(){
36197 var cmax = this.config.maxSize || 10000;
36198 var center = this.mgr.getRegion("center");
36199 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36202 getVMaxSize : function(){
36203 var cmax = this.config.maxSize || 10000;
36204 var center = this.mgr.getRegion("center");
36205 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36208 onSplitMove : function(split, newSize){
36209 this.fireEvent("resized", this, newSize);
36213 * Returns the {@link Roo.SplitBar} for this region.
36214 * @return {Roo.SplitBar}
36216 getSplitBar : function(){
36221 this.hideSplitter();
36222 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36225 hideSplitter : function(){
36227 this.split.el.setLocation(-2000,-2000);
36228 this.split.el.hide();
36234 this.split.el.show();
36236 Roo.bootstrap.layout.Split.superclass.show.call(this);
36239 beforeSlide: function(){
36240 if(Roo.isGecko){// firefox overflow auto bug workaround
36241 this.bodyEl.clip();
36243 this.tabs.bodyEl.clip();
36245 if(this.activePanel){
36246 this.activePanel.getEl().clip();
36248 if(this.activePanel.beforeSlide){
36249 this.activePanel.beforeSlide();
36255 afterSlide : function(){
36256 if(Roo.isGecko){// firefox overflow auto bug workaround
36257 this.bodyEl.unclip();
36259 this.tabs.bodyEl.unclip();
36261 if(this.activePanel){
36262 this.activePanel.getEl().unclip();
36263 if(this.activePanel.afterSlide){
36264 this.activePanel.afterSlide();
36270 initAutoHide : function(){
36271 if(this.autoHide !== false){
36272 if(!this.autoHideHd){
36273 var st = new Roo.util.DelayedTask(this.slideIn, this);
36274 this.autoHideHd = {
36275 "mouseout": function(e){
36276 if(!e.within(this.el, true)){
36280 "mouseover" : function(e){
36286 this.el.on(this.autoHideHd);
36290 clearAutoHide : function(){
36291 if(this.autoHide !== false){
36292 this.el.un("mouseout", this.autoHideHd.mouseout);
36293 this.el.un("mouseover", this.autoHideHd.mouseover);
36297 clearMonitor : function(){
36298 Roo.get(document).un("click", this.slideInIf, this);
36301 // these names are backwards but not changed for compat
36302 slideOut : function(){
36303 if(this.isSlid || this.el.hasActiveFx()){
36306 this.isSlid = true;
36307 if(this.collapseBtn){
36308 this.collapseBtn.hide();
36310 this.closeBtnState = this.closeBtn.getStyle('display');
36311 this.closeBtn.hide();
36313 this.stickBtn.show();
36316 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36317 this.beforeSlide();
36318 this.el.setStyle("z-index", 10001);
36319 this.el.slideIn(this.getSlideAnchor(), {
36320 callback: function(){
36322 this.initAutoHide();
36323 Roo.get(document).on("click", this.slideInIf, this);
36324 this.fireEvent("slideshow", this);
36331 afterSlideIn : function(){
36332 this.clearAutoHide();
36333 this.isSlid = false;
36334 this.clearMonitor();
36335 this.el.setStyle("z-index", "");
36336 if(this.collapseBtn){
36337 this.collapseBtn.show();
36339 this.closeBtn.setStyle('display', this.closeBtnState);
36341 this.stickBtn.hide();
36343 this.fireEvent("slidehide", this);
36346 slideIn : function(cb){
36347 if(!this.isSlid || this.el.hasActiveFx()){
36351 this.isSlid = false;
36352 this.beforeSlide();
36353 this.el.slideOut(this.getSlideAnchor(), {
36354 callback: function(){
36355 this.el.setLeftTop(-10000, -10000);
36357 this.afterSlideIn();
36365 slideInIf : function(e){
36366 if(!e.within(this.el)){
36371 animateCollapse : function(){
36372 this.beforeSlide();
36373 this.el.setStyle("z-index", 20000);
36374 var anchor = this.getSlideAnchor();
36375 this.el.slideOut(anchor, {
36376 callback : function(){
36377 this.el.setStyle("z-index", "");
36378 this.collapsedEl.slideIn(anchor, {duration:.3});
36380 this.el.setLocation(-10000,-10000);
36382 this.fireEvent("collapsed", this);
36389 animateExpand : function(){
36390 this.beforeSlide();
36391 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36392 this.el.setStyle("z-index", 20000);
36393 this.collapsedEl.hide({
36396 this.el.slideIn(this.getSlideAnchor(), {
36397 callback : function(){
36398 this.el.setStyle("z-index", "");
36401 this.split.el.show();
36403 this.fireEvent("invalidated", this);
36404 this.fireEvent("expanded", this);
36432 getAnchor : function(){
36433 return this.anchors[this.position];
36436 getCollapseAnchor : function(){
36437 return this.canchors[this.position];
36440 getSlideAnchor : function(){
36441 return this.sanchors[this.position];
36444 getAlignAdj : function(){
36445 var cm = this.cmargins;
36446 switch(this.position){
36462 getExpandAdj : function(){
36463 var c = this.collapsedEl, cm = this.cmargins;
36464 switch(this.position){
36466 return [-(cm.right+c.getWidth()+cm.left), 0];
36469 return [cm.right+c.getWidth()+cm.left, 0];
36472 return [0, -(cm.top+cm.bottom+c.getHeight())];
36475 return [0, cm.top+cm.bottom+c.getHeight()];
36481 * Ext JS Library 1.1.1
36482 * Copyright(c) 2006-2007, Ext JS, LLC.
36484 * Originally Released Under LGPL - original licence link has changed is not relivant.
36487 * <script type="text/javascript">
36490 * These classes are private internal classes
36492 Roo.bootstrap.layout.Center = function(config){
36493 config.region = "center";
36494 Roo.bootstrap.layout.Region.call(this, config);
36495 this.visible = true;
36496 this.minWidth = config.minWidth || 20;
36497 this.minHeight = config.minHeight || 20;
36500 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36502 // center panel can't be hidden
36506 // center panel can't be hidden
36509 getMinWidth: function(){
36510 return this.minWidth;
36513 getMinHeight: function(){
36514 return this.minHeight;
36527 Roo.bootstrap.layout.North = function(config)
36529 config.region = 'north';
36530 config.cursor = 'n-resize';
36532 Roo.bootstrap.layout.Split.call(this, config);
36536 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36537 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36538 this.split.el.addClass("roo-layout-split-v");
36540 var size = config.initialSize || config.height;
36541 if(typeof size != "undefined"){
36542 this.el.setHeight(size);
36545 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36547 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36551 getBox : function(){
36552 if(this.collapsed){
36553 return this.collapsedEl.getBox();
36555 var box = this.el.getBox();
36557 box.height += this.split.el.getHeight();
36562 updateBox : function(box){
36563 if(this.split && !this.collapsed){
36564 box.height -= this.split.el.getHeight();
36565 this.split.el.setLeft(box.x);
36566 this.split.el.setTop(box.y+box.height);
36567 this.split.el.setWidth(box.width);
36569 if(this.collapsed){
36570 this.updateBody(box.width, null);
36572 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36580 Roo.bootstrap.layout.South = function(config){
36581 config.region = 'south';
36582 config.cursor = 's-resize';
36583 Roo.bootstrap.layout.Split.call(this, config);
36585 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36586 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36587 this.split.el.addClass("roo-layout-split-v");
36589 var size = config.initialSize || config.height;
36590 if(typeof size != "undefined"){
36591 this.el.setHeight(size);
36595 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36596 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36597 getBox : function(){
36598 if(this.collapsed){
36599 return this.collapsedEl.getBox();
36601 var box = this.el.getBox();
36603 var sh = this.split.el.getHeight();
36610 updateBox : function(box){
36611 if(this.split && !this.collapsed){
36612 var sh = this.split.el.getHeight();
36615 this.split.el.setLeft(box.x);
36616 this.split.el.setTop(box.y-sh);
36617 this.split.el.setWidth(box.width);
36619 if(this.collapsed){
36620 this.updateBody(box.width, null);
36622 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36626 Roo.bootstrap.layout.East = function(config){
36627 config.region = "east";
36628 config.cursor = "e-resize";
36629 Roo.bootstrap.layout.Split.call(this, config);
36631 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36632 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36633 this.split.el.addClass("roo-layout-split-h");
36635 var size = config.initialSize || config.width;
36636 if(typeof size != "undefined"){
36637 this.el.setWidth(size);
36640 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36641 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36642 getBox : function(){
36643 if(this.collapsed){
36644 return this.collapsedEl.getBox();
36646 var box = this.el.getBox();
36648 var sw = this.split.el.getWidth();
36655 updateBox : function(box){
36656 if(this.split && !this.collapsed){
36657 var sw = this.split.el.getWidth();
36659 this.split.el.setLeft(box.x);
36660 this.split.el.setTop(box.y);
36661 this.split.el.setHeight(box.height);
36664 if(this.collapsed){
36665 this.updateBody(null, box.height);
36667 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36671 Roo.bootstrap.layout.West = function(config){
36672 config.region = "west";
36673 config.cursor = "w-resize";
36675 Roo.bootstrap.layout.Split.call(this, config);
36677 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36678 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36679 this.split.el.addClass("roo-layout-split-h");
36683 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36684 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36686 onRender: function(ctr, pos)
36688 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36689 var size = this.config.initialSize || this.config.width;
36690 if(typeof size != "undefined"){
36691 this.el.setWidth(size);
36695 getBox : function(){
36696 if(this.collapsed){
36697 return this.collapsedEl.getBox();
36699 var box = this.el.getBox();
36701 box.width += this.split.el.getWidth();
36706 updateBox : function(box){
36707 if(this.split && !this.collapsed){
36708 var sw = this.split.el.getWidth();
36710 this.split.el.setLeft(box.x+box.width);
36711 this.split.el.setTop(box.y);
36712 this.split.el.setHeight(box.height);
36714 if(this.collapsed){
36715 this.updateBody(null, box.height);
36717 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36720 Roo.namespace("Roo.bootstrap.panel");/*
36722 * Ext JS Library 1.1.1
36723 * Copyright(c) 2006-2007, Ext JS, LLC.
36725 * Originally Released Under LGPL - original licence link has changed is not relivant.
36728 * <script type="text/javascript">
36731 * @class Roo.ContentPanel
36732 * @extends Roo.util.Observable
36733 * A basic ContentPanel element.
36734 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36735 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36736 * @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
36737 * @cfg {Boolean} closable True if the panel can be closed/removed
36738 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36739 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36740 * @cfg {Toolbar} toolbar A toolbar for this panel
36741 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36742 * @cfg {String} title The title for this panel
36743 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36744 * @cfg {String} url Calls {@link #setUrl} with this value
36745 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36746 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36747 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36748 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36749 * @cfg {Boolean} badges render the badges
36752 * Create a new ContentPanel.
36753 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36754 * @param {String/Object} config A string to set only the title or a config object
36755 * @param {String} content (optional) Set the HTML content for this panel
36756 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36758 Roo.bootstrap.panel.Content = function( config){
36760 this.tpl = config.tpl || false;
36762 var el = config.el;
36763 var content = config.content;
36765 if(config.autoCreate){ // xtype is available if this is called from factory
36768 this.el = Roo.get(el);
36769 if(!this.el && config && config.autoCreate){
36770 if(typeof config.autoCreate == "object"){
36771 if(!config.autoCreate.id){
36772 config.autoCreate.id = config.id||el;
36774 this.el = Roo.DomHelper.append(document.body,
36775 config.autoCreate, true);
36777 var elcfg = { tag: "div",
36778 cls: "roo-layout-inactive-content",
36782 elcfg.html = config.html;
36786 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36789 this.closable = false;
36790 this.loaded = false;
36791 this.active = false;
36794 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36796 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36798 this.wrapEl = this.el; //this.el.wrap();
36800 if (config.toolbar.items) {
36801 ti = config.toolbar.items ;
36802 delete config.toolbar.items ;
36806 this.toolbar.render(this.wrapEl, 'before');
36807 for(var i =0;i < ti.length;i++) {
36808 // Roo.log(['add child', items[i]]);
36809 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36811 this.toolbar.items = nitems;
36812 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36813 delete config.toolbar;
36817 // xtype created footer. - not sure if will work as we normally have to render first..
36818 if (this.footer && !this.footer.el && this.footer.xtype) {
36819 if (!this.wrapEl) {
36820 this.wrapEl = this.el.wrap();
36823 this.footer.container = this.wrapEl.createChild();
36825 this.footer = Roo.factory(this.footer, Roo);
36830 if(typeof config == "string"){
36831 this.title = config;
36833 Roo.apply(this, config);
36837 this.resizeEl = Roo.get(this.resizeEl, true);
36839 this.resizeEl = this.el;
36841 // handle view.xtype
36849 * Fires when this panel is activated.
36850 * @param {Roo.ContentPanel} this
36854 * @event deactivate
36855 * Fires when this panel is activated.
36856 * @param {Roo.ContentPanel} this
36858 "deactivate" : true,
36862 * Fires when this panel is resized if fitToFrame is true.
36863 * @param {Roo.ContentPanel} this
36864 * @param {Number} width The width after any component adjustments
36865 * @param {Number} height The height after any component adjustments
36871 * Fires when this tab is created
36872 * @param {Roo.ContentPanel} this
36883 if(this.autoScroll){
36884 this.resizeEl.setStyle("overflow", "auto");
36886 // fix randome scrolling
36887 //this.el.on('scroll', function() {
36888 // Roo.log('fix random scolling');
36889 // this.scrollTo('top',0);
36892 content = content || this.content;
36894 this.setContent(content);
36896 if(config && config.url){
36897 this.setUrl(this.url, this.params, this.loadOnce);
36902 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36904 if (this.view && typeof(this.view.xtype) != 'undefined') {
36905 this.view.el = this.el.appendChild(document.createElement("div"));
36906 this.view = Roo.factory(this.view);
36907 this.view.render && this.view.render(false, '');
36911 this.fireEvent('render', this);
36914 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36918 setRegion : function(region){
36919 this.region = region;
36920 this.setActiveClass(region && !this.background);
36924 setActiveClass: function(state)
36927 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36928 this.el.setStyle('position','relative');
36930 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36931 this.el.setStyle('position', 'absolute');
36936 * Returns the toolbar for this Panel if one was configured.
36937 * @return {Roo.Toolbar}
36939 getToolbar : function(){
36940 return this.toolbar;
36943 setActiveState : function(active)
36945 this.active = active;
36946 this.setActiveClass(active);
36948 if(this.fireEvent("deactivate", this) === false){
36953 this.fireEvent("activate", this);
36957 * Updates this panel's element
36958 * @param {String} content The new content
36959 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36961 setContent : function(content, loadScripts){
36962 this.el.update(content, loadScripts);
36965 ignoreResize : function(w, h){
36966 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36969 this.lastSize = {width: w, height: h};
36974 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36975 * @return {Roo.UpdateManager} The UpdateManager
36977 getUpdateManager : function(){
36978 return this.el.getUpdateManager();
36981 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36982 * @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:
36985 url: "your-url.php",
36986 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36987 callback: yourFunction,
36988 scope: yourObject, //(optional scope)
36991 text: "Loading...",
36996 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36997 * 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.
36998 * @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}
36999 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37000 * @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.
37001 * @return {Roo.ContentPanel} this
37004 var um = this.el.getUpdateManager();
37005 um.update.apply(um, arguments);
37011 * 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.
37012 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37013 * @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)
37014 * @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)
37015 * @return {Roo.UpdateManager} The UpdateManager
37017 setUrl : function(url, params, loadOnce){
37018 if(this.refreshDelegate){
37019 this.removeListener("activate", this.refreshDelegate);
37021 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37022 this.on("activate", this.refreshDelegate);
37023 return this.el.getUpdateManager();
37026 _handleRefresh : function(url, params, loadOnce){
37027 if(!loadOnce || !this.loaded){
37028 var updater = this.el.getUpdateManager();
37029 updater.update(url, params, this._setLoaded.createDelegate(this));
37033 _setLoaded : function(){
37034 this.loaded = true;
37038 * Returns this panel's id
37041 getId : function(){
37046 * Returns this panel's element - used by regiosn to add.
37047 * @return {Roo.Element}
37049 getEl : function(){
37050 return this.wrapEl || this.el;
37055 adjustForComponents : function(width, height)
37057 //Roo.log('adjustForComponents ');
37058 if(this.resizeEl != this.el){
37059 width -= this.el.getFrameWidth('lr');
37060 height -= this.el.getFrameWidth('tb');
37063 var te = this.toolbar.getEl();
37064 te.setWidth(width);
37065 height -= te.getHeight();
37068 var te = this.footer.getEl();
37069 te.setWidth(width);
37070 height -= te.getHeight();
37074 if(this.adjustments){
37075 width += this.adjustments[0];
37076 height += this.adjustments[1];
37078 return {"width": width, "height": height};
37081 setSize : function(width, height){
37082 if(this.fitToFrame && !this.ignoreResize(width, height)){
37083 if(this.fitContainer && this.resizeEl != this.el){
37084 this.el.setSize(width, height);
37086 var size = this.adjustForComponents(width, height);
37087 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37088 this.fireEvent('resize', this, size.width, size.height);
37093 * Returns this panel's title
37096 getTitle : function(){
37098 if (typeof(this.title) != 'object') {
37103 for (var k in this.title) {
37104 if (!this.title.hasOwnProperty(k)) {
37108 if (k.indexOf('-') >= 0) {
37109 var s = k.split('-');
37110 for (var i = 0; i<s.length; i++) {
37111 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37114 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37121 * Set this panel's title
37122 * @param {String} title
37124 setTitle : function(title){
37125 this.title = title;
37127 this.region.updatePanelTitle(this, title);
37132 * Returns true is this panel was configured to be closable
37133 * @return {Boolean}
37135 isClosable : function(){
37136 return this.closable;
37139 beforeSlide : function(){
37141 this.resizeEl.clip();
37144 afterSlide : function(){
37146 this.resizeEl.unclip();
37150 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37151 * Will fail silently if the {@link #setUrl} method has not been called.
37152 * This does not activate the panel, just updates its content.
37154 refresh : function(){
37155 if(this.refreshDelegate){
37156 this.loaded = false;
37157 this.refreshDelegate();
37162 * Destroys this panel
37164 destroy : function(){
37165 this.el.removeAllListeners();
37166 var tempEl = document.createElement("span");
37167 tempEl.appendChild(this.el.dom);
37168 tempEl.innerHTML = "";
37174 * form - if the content panel contains a form - this is a reference to it.
37175 * @type {Roo.form.Form}
37179 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37180 * This contains a reference to it.
37186 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37196 * @param {Object} cfg Xtype definition of item to add.
37200 getChildContainer: function () {
37201 return this.getEl();
37206 var ret = new Roo.factory(cfg);
37211 if (cfg.xtype.match(/^Form$/)) {
37214 //if (this.footer) {
37215 // el = this.footer.container.insertSibling(false, 'before');
37217 el = this.el.createChild();
37220 this.form = new Roo.form.Form(cfg);
37223 if ( this.form.allItems.length) {
37224 this.form.render(el.dom);
37228 // should only have one of theses..
37229 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37230 // views.. should not be just added - used named prop 'view''
37232 cfg.el = this.el.appendChild(document.createElement("div"));
37235 var ret = new Roo.factory(cfg);
37237 ret.render && ret.render(false, ''); // render blank..
37247 * @class Roo.bootstrap.panel.Grid
37248 * @extends Roo.bootstrap.panel.Content
37250 * Create a new GridPanel.
37251 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37252 * @param {Object} config A the config object
37258 Roo.bootstrap.panel.Grid = function(config)
37262 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37263 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37265 config.el = this.wrapper;
37266 //this.el = this.wrapper;
37268 if (config.container) {
37269 // ctor'ed from a Border/panel.grid
37272 this.wrapper.setStyle("overflow", "hidden");
37273 this.wrapper.addClass('roo-grid-container');
37278 if(config.toolbar){
37279 var tool_el = this.wrapper.createChild();
37280 this.toolbar = Roo.factory(config.toolbar);
37282 if (config.toolbar.items) {
37283 ti = config.toolbar.items ;
37284 delete config.toolbar.items ;
37288 this.toolbar.render(tool_el);
37289 for(var i =0;i < ti.length;i++) {
37290 // Roo.log(['add child', items[i]]);
37291 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37293 this.toolbar.items = nitems;
37295 delete config.toolbar;
37298 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37299 config.grid.scrollBody = true;;
37300 config.grid.monitorWindowResize = false; // turn off autosizing
37301 config.grid.autoHeight = false;
37302 config.grid.autoWidth = false;
37304 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37306 if (config.background) {
37307 // render grid on panel activation (if panel background)
37308 this.on('activate', function(gp) {
37309 if (!gp.grid.rendered) {
37310 gp.grid.render(this.wrapper);
37311 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37316 this.grid.render(this.wrapper);
37317 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37320 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37321 // ??? needed ??? config.el = this.wrapper;
37326 // xtype created footer. - not sure if will work as we normally have to render first..
37327 if (this.footer && !this.footer.el && this.footer.xtype) {
37329 var ctr = this.grid.getView().getFooterPanel(true);
37330 this.footer.dataSource = this.grid.dataSource;
37331 this.footer = Roo.factory(this.footer, Roo);
37332 this.footer.render(ctr);
37342 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37343 getId : function(){
37344 return this.grid.id;
37348 * Returns the grid for this panel
37349 * @return {Roo.bootstrap.Table}
37351 getGrid : function(){
37355 setSize : function(width, height){
37356 if(!this.ignoreResize(width, height)){
37357 var grid = this.grid;
37358 var size = this.adjustForComponents(width, height);
37359 var gridel = grid.getGridEl();
37360 gridel.setSize(size.width, size.height);
37362 var thd = grid.getGridEl().select('thead',true).first();
37363 var tbd = grid.getGridEl().select('tbody', true).first();
37365 tbd.setSize(width, height - thd.getHeight());
37374 beforeSlide : function(){
37375 this.grid.getView().scroller.clip();
37378 afterSlide : function(){
37379 this.grid.getView().scroller.unclip();
37382 destroy : function(){
37383 this.grid.destroy();
37385 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37390 * @class Roo.bootstrap.panel.Nest
37391 * @extends Roo.bootstrap.panel.Content
37393 * Create a new Panel, that can contain a layout.Border.
37396 * @param {Roo.BorderLayout} layout The layout for this panel
37397 * @param {String/Object} config A string to set only the title or a config object
37399 Roo.bootstrap.panel.Nest = function(config)
37401 // construct with only one argument..
37402 /* FIXME - implement nicer consturctors
37403 if (layout.layout) {
37405 layout = config.layout;
37406 delete config.layout;
37408 if (layout.xtype && !layout.getEl) {
37409 // then layout needs constructing..
37410 layout = Roo.factory(layout, Roo);
37414 config.el = config.layout.getEl();
37416 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37418 config.layout.monitorWindowResize = false; // turn off autosizing
37419 this.layout = config.layout;
37420 this.layout.getEl().addClass("roo-layout-nested-layout");
37427 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37429 setSize : function(width, height){
37430 if(!this.ignoreResize(width, height)){
37431 var size = this.adjustForComponents(width, height);
37432 var el = this.layout.getEl();
37433 if (size.height < 1) {
37434 el.setWidth(size.width);
37436 el.setSize(size.width, size.height);
37438 var touch = el.dom.offsetWidth;
37439 this.layout.layout();
37440 // ie requires a double layout on the first pass
37441 if(Roo.isIE && !this.initialized){
37442 this.initialized = true;
37443 this.layout.layout();
37448 // activate all subpanels if not currently active..
37450 setActiveState : function(active){
37451 this.active = active;
37452 this.setActiveClass(active);
37455 this.fireEvent("deactivate", this);
37459 this.fireEvent("activate", this);
37460 // not sure if this should happen before or after..
37461 if (!this.layout) {
37462 return; // should not happen..
37465 for (var r in this.layout.regions) {
37466 reg = this.layout.getRegion(r);
37467 if (reg.getActivePanel()) {
37468 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37469 reg.setActivePanel(reg.getActivePanel());
37472 if (!reg.panels.length) {
37475 reg.showPanel(reg.getPanel(0));
37484 * Returns the nested BorderLayout for this panel
37485 * @return {Roo.BorderLayout}
37487 getLayout : function(){
37488 return this.layout;
37492 * Adds a xtype elements to the layout of the nested panel
37496 xtype : 'ContentPanel',
37503 xtype : 'NestedLayoutPanel',
37509 items : [ ... list of content panels or nested layout panels.. ]
37513 * @param {Object} cfg Xtype definition of item to add.
37515 addxtype : function(cfg) {
37516 return this.layout.addxtype(cfg);
37521 * Ext JS Library 1.1.1
37522 * Copyright(c) 2006-2007, Ext JS, LLC.
37524 * Originally Released Under LGPL - original licence link has changed is not relivant.
37527 * <script type="text/javascript">
37530 * @class Roo.TabPanel
37531 * @extends Roo.util.Observable
37532 * A lightweight tab container.
37536 // basic tabs 1, built from existing content
37537 var tabs = new Roo.TabPanel("tabs1");
37538 tabs.addTab("script", "View Script");
37539 tabs.addTab("markup", "View Markup");
37540 tabs.activate("script");
37542 // more advanced tabs, built from javascript
37543 var jtabs = new Roo.TabPanel("jtabs");
37544 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37546 // set up the UpdateManager
37547 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37548 var updater = tab2.getUpdateManager();
37549 updater.setDefaultUrl("ajax1.htm");
37550 tab2.on('activate', updater.refresh, updater, true);
37552 // Use setUrl for Ajax loading
37553 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37554 tab3.setUrl("ajax2.htm", null, true);
37557 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37560 jtabs.activate("jtabs-1");
37563 * Create a new TabPanel.
37564 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37565 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37567 Roo.bootstrap.panel.Tabs = function(config){
37569 * The container element for this TabPanel.
37570 * @type Roo.Element
37572 this.el = Roo.get(config.el);
37575 if(typeof config == "boolean"){
37576 this.tabPosition = config ? "bottom" : "top";
37578 Roo.apply(this, config);
37582 if(this.tabPosition == "bottom"){
37583 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37584 this.el.addClass("roo-tabs-bottom");
37586 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37587 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37588 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37590 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37592 if(this.tabPosition != "bottom"){
37593 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37594 * @type Roo.Element
37596 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37597 this.el.addClass("roo-tabs-top");
37601 this.bodyEl.setStyle("position", "relative");
37603 this.active = null;
37604 this.activateDelegate = this.activate.createDelegate(this);
37609 * Fires when the active tab changes
37610 * @param {Roo.TabPanel} this
37611 * @param {Roo.TabPanelItem} activePanel The new active tab
37615 * @event beforetabchange
37616 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37617 * @param {Roo.TabPanel} this
37618 * @param {Object} e Set cancel to true on this object to cancel the tab change
37619 * @param {Roo.TabPanelItem} tab The tab being changed to
37621 "beforetabchange" : true
37624 Roo.EventManager.onWindowResize(this.onResize, this);
37625 this.cpad = this.el.getPadding("lr");
37626 this.hiddenCount = 0;
37629 // toolbar on the tabbar support...
37630 if (this.toolbar) {
37631 alert("no toolbar support yet");
37632 this.toolbar = false;
37634 var tcfg = this.toolbar;
37635 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37636 this.toolbar = new Roo.Toolbar(tcfg);
37637 if (Roo.isSafari) {
37638 var tbl = tcfg.container.child('table', true);
37639 tbl.setAttribute('width', '100%');
37647 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37650 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37652 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37654 tabPosition : "top",
37656 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37658 currentTabWidth : 0,
37660 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37664 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37668 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37670 preferredTabWidth : 175,
37672 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37674 resizeTabs : false,
37676 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37678 monitorResize : true,
37680 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37685 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37686 * @param {String} id The id of the div to use <b>or create</b>
37687 * @param {String} text The text for the tab
37688 * @param {String} content (optional) Content to put in the TabPanelItem body
37689 * @param {Boolean} closable (optional) True to create a close icon on the tab
37690 * @return {Roo.TabPanelItem} The created TabPanelItem
37692 addTab : function(id, text, content, closable, tpl)
37694 var item = new Roo.bootstrap.panel.TabItem({
37698 closable : closable,
37701 this.addTabItem(item);
37703 item.setContent(content);
37709 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37710 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37711 * @return {Roo.TabPanelItem}
37713 getTab : function(id){
37714 return this.items[id];
37718 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37719 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37721 hideTab : function(id){
37722 var t = this.items[id];
37725 this.hiddenCount++;
37726 this.autoSizeTabs();
37731 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37732 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37734 unhideTab : function(id){
37735 var t = this.items[id];
37737 t.setHidden(false);
37738 this.hiddenCount--;
37739 this.autoSizeTabs();
37744 * Adds an existing {@link Roo.TabPanelItem}.
37745 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37747 addTabItem : function(item){
37748 this.items[item.id] = item;
37749 this.items.push(item);
37750 // if(this.resizeTabs){
37751 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37752 // this.autoSizeTabs();
37754 // item.autoSize();
37759 * Removes a {@link Roo.TabPanelItem}.
37760 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37762 removeTab : function(id){
37763 var items = this.items;
37764 var tab = items[id];
37765 if(!tab) { return; }
37766 var index = items.indexOf(tab);
37767 if(this.active == tab && items.length > 1){
37768 var newTab = this.getNextAvailable(index);
37773 this.stripEl.dom.removeChild(tab.pnode.dom);
37774 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37775 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37777 items.splice(index, 1);
37778 delete this.items[tab.id];
37779 tab.fireEvent("close", tab);
37780 tab.purgeListeners();
37781 this.autoSizeTabs();
37784 getNextAvailable : function(start){
37785 var items = this.items;
37787 // look for a next tab that will slide over to
37788 // replace the one being removed
37789 while(index < items.length){
37790 var item = items[++index];
37791 if(item && !item.isHidden()){
37795 // if one isn't found select the previous tab (on the left)
37798 var item = items[--index];
37799 if(item && !item.isHidden()){
37807 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37808 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37810 disableTab : function(id){
37811 var tab = this.items[id];
37812 if(tab && this.active != tab){
37818 * Enables a {@link Roo.TabPanelItem} that is disabled.
37819 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37821 enableTab : function(id){
37822 var tab = this.items[id];
37827 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37828 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37829 * @return {Roo.TabPanelItem} The TabPanelItem.
37831 activate : function(id){
37832 var tab = this.items[id];
37836 if(tab == this.active || tab.disabled){
37840 this.fireEvent("beforetabchange", this, e, tab);
37841 if(e.cancel !== true && !tab.disabled){
37843 this.active.hide();
37845 this.active = this.items[id];
37846 this.active.show();
37847 this.fireEvent("tabchange", this, this.active);
37853 * Gets the active {@link Roo.TabPanelItem}.
37854 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37856 getActiveTab : function(){
37857 return this.active;
37861 * Updates the tab body element to fit the height of the container element
37862 * for overflow scrolling
37863 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37865 syncHeight : function(targetHeight){
37866 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37867 var bm = this.bodyEl.getMargins();
37868 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37869 this.bodyEl.setHeight(newHeight);
37873 onResize : function(){
37874 if(this.monitorResize){
37875 this.autoSizeTabs();
37880 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37882 beginUpdate : function(){
37883 this.updating = true;
37887 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37889 endUpdate : function(){
37890 this.updating = false;
37891 this.autoSizeTabs();
37895 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37897 autoSizeTabs : function(){
37898 var count = this.items.length;
37899 var vcount = count - this.hiddenCount;
37900 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37903 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37904 var availWidth = Math.floor(w / vcount);
37905 var b = this.stripBody;
37906 if(b.getWidth() > w){
37907 var tabs = this.items;
37908 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37909 if(availWidth < this.minTabWidth){
37910 /*if(!this.sleft){ // incomplete scrolling code
37911 this.createScrollButtons();
37914 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37917 if(this.currentTabWidth < this.preferredTabWidth){
37918 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37924 * Returns the number of tabs in this TabPanel.
37927 getCount : function(){
37928 return this.items.length;
37932 * Resizes all the tabs to the passed width
37933 * @param {Number} The new width
37935 setTabWidth : function(width){
37936 this.currentTabWidth = width;
37937 for(var i = 0, len = this.items.length; i < len; i++) {
37938 if(!this.items[i].isHidden()) {
37939 this.items[i].setWidth(width);
37945 * Destroys this TabPanel
37946 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37948 destroy : function(removeEl){
37949 Roo.EventManager.removeResizeListener(this.onResize, this);
37950 for(var i = 0, len = this.items.length; i < len; i++){
37951 this.items[i].purgeListeners();
37953 if(removeEl === true){
37954 this.el.update("");
37959 createStrip : function(container)
37961 var strip = document.createElement("nav");
37962 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37963 container.appendChild(strip);
37967 createStripList : function(strip)
37969 // div wrapper for retard IE
37970 // returns the "tr" element.
37971 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37972 //'<div class="x-tabs-strip-wrap">'+
37973 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37974 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37975 return strip.firstChild; //.firstChild.firstChild.firstChild;
37977 createBody : function(container)
37979 var body = document.createElement("div");
37980 Roo.id(body, "tab-body");
37981 //Roo.fly(body).addClass("x-tabs-body");
37982 Roo.fly(body).addClass("tab-content");
37983 container.appendChild(body);
37986 createItemBody :function(bodyEl, id){
37987 var body = Roo.getDom(id);
37989 body = document.createElement("div");
37992 //Roo.fly(body).addClass("x-tabs-item-body");
37993 Roo.fly(body).addClass("tab-pane");
37994 bodyEl.insertBefore(body, bodyEl.firstChild);
37998 createStripElements : function(stripEl, text, closable, tpl)
38000 var td = document.createElement("li"); // was td..
38003 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38006 stripEl.appendChild(td);
38008 td.className = "x-tabs-closable";
38009 if(!this.closeTpl){
38010 this.closeTpl = new Roo.Template(
38011 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38012 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38013 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38016 var el = this.closeTpl.overwrite(td, {"text": text});
38017 var close = el.getElementsByTagName("div")[0];
38018 var inner = el.getElementsByTagName("em")[0];
38019 return {"el": el, "close": close, "inner": inner};
38022 // not sure what this is..
38023 // if(!this.tabTpl){
38024 //this.tabTpl = new Roo.Template(
38025 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38026 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38028 // this.tabTpl = new Roo.Template(
38029 // '<a href="#">' +
38030 // '<span unselectable="on"' +
38031 // (this.disableTooltips ? '' : ' title="{text}"') +
38032 // ' >{text}</span></a>'
38038 var template = tpl || this.tabTpl || false;
38042 template = new Roo.Template(
38044 '<span unselectable="on"' +
38045 (this.disableTooltips ? '' : ' title="{text}"') +
38046 ' >{text}</span></a>'
38050 switch (typeof(template)) {
38054 template = new Roo.Template(template);
38060 var el = template.overwrite(td, {"text": text});
38062 var inner = el.getElementsByTagName("span")[0];
38064 return {"el": el, "inner": inner};
38072 * @class Roo.TabPanelItem
38073 * @extends Roo.util.Observable
38074 * Represents an individual item (tab plus body) in a TabPanel.
38075 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38076 * @param {String} id The id of this TabPanelItem
38077 * @param {String} text The text for the tab of this TabPanelItem
38078 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38080 Roo.bootstrap.panel.TabItem = function(config){
38082 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38083 * @type Roo.TabPanel
38085 this.tabPanel = config.panel;
38087 * The id for this TabPanelItem
38090 this.id = config.id;
38092 this.disabled = false;
38094 this.text = config.text;
38096 this.loaded = false;
38097 this.closable = config.closable;
38100 * The body element for this TabPanelItem.
38101 * @type Roo.Element
38103 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38104 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38105 this.bodyEl.setStyle("display", "block");
38106 this.bodyEl.setStyle("zoom", "1");
38107 //this.hideAction();
38109 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38111 this.el = Roo.get(els.el);
38112 this.inner = Roo.get(els.inner, true);
38113 this.textEl = Roo.get(this.el.dom.firstChild, true);
38114 this.pnode = Roo.get(els.el.parentNode, true);
38115 // this.el.on("mousedown", this.onTabMouseDown, this);
38116 this.el.on("click", this.onTabClick, this);
38118 if(config.closable){
38119 var c = Roo.get(els.close, true);
38120 c.dom.title = this.closeText;
38121 c.addClassOnOver("close-over");
38122 c.on("click", this.closeClick, this);
38128 * Fires when this tab becomes the active tab.
38129 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38130 * @param {Roo.TabPanelItem} this
38134 * @event beforeclose
38135 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38136 * @param {Roo.TabPanelItem} this
38137 * @param {Object} e Set cancel to true on this object to cancel the close.
38139 "beforeclose": true,
38142 * Fires when this tab is closed.
38143 * @param {Roo.TabPanelItem} this
38147 * @event deactivate
38148 * Fires when this tab is no longer the active tab.
38149 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38150 * @param {Roo.TabPanelItem} this
38152 "deactivate" : true
38154 this.hidden = false;
38156 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38159 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38161 purgeListeners : function(){
38162 Roo.util.Observable.prototype.purgeListeners.call(this);
38163 this.el.removeAllListeners();
38166 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38169 this.pnode.addClass("active");
38172 this.tabPanel.stripWrap.repaint();
38174 this.fireEvent("activate", this.tabPanel, this);
38178 * Returns true if this tab is the active tab.
38179 * @return {Boolean}
38181 isActive : function(){
38182 return this.tabPanel.getActiveTab() == this;
38186 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38189 this.pnode.removeClass("active");
38191 this.fireEvent("deactivate", this.tabPanel, this);
38194 hideAction : function(){
38195 this.bodyEl.hide();
38196 this.bodyEl.setStyle("position", "absolute");
38197 this.bodyEl.setLeft("-20000px");
38198 this.bodyEl.setTop("-20000px");
38201 showAction : function(){
38202 this.bodyEl.setStyle("position", "relative");
38203 this.bodyEl.setTop("");
38204 this.bodyEl.setLeft("");
38205 this.bodyEl.show();
38209 * Set the tooltip for the tab.
38210 * @param {String} tooltip The tab's tooltip
38212 setTooltip : function(text){
38213 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38214 this.textEl.dom.qtip = text;
38215 this.textEl.dom.removeAttribute('title');
38217 this.textEl.dom.title = text;
38221 onTabClick : function(e){
38222 e.preventDefault();
38223 this.tabPanel.activate(this.id);
38226 onTabMouseDown : function(e){
38227 e.preventDefault();
38228 this.tabPanel.activate(this.id);
38231 getWidth : function(){
38232 return this.inner.getWidth();
38235 setWidth : function(width){
38236 var iwidth = width - this.pnode.getPadding("lr");
38237 this.inner.setWidth(iwidth);
38238 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38239 this.pnode.setWidth(width);
38243 * Show or hide the tab
38244 * @param {Boolean} hidden True to hide or false to show.
38246 setHidden : function(hidden){
38247 this.hidden = hidden;
38248 this.pnode.setStyle("display", hidden ? "none" : "");
38252 * Returns true if this tab is "hidden"
38253 * @return {Boolean}
38255 isHidden : function(){
38256 return this.hidden;
38260 * Returns the text for this tab
38263 getText : function(){
38267 autoSize : function(){
38268 //this.el.beginMeasure();
38269 this.textEl.setWidth(1);
38271 * #2804 [new] Tabs in Roojs
38272 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38274 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38275 //this.el.endMeasure();
38279 * Sets the text for the tab (Note: this also sets the tooltip text)
38280 * @param {String} text The tab's text and tooltip
38282 setText : function(text){
38284 this.textEl.update(text);
38285 this.setTooltip(text);
38286 //if(!this.tabPanel.resizeTabs){
38287 // this.autoSize();
38291 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38293 activate : function(){
38294 this.tabPanel.activate(this.id);
38298 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38300 disable : function(){
38301 if(this.tabPanel.active != this){
38302 this.disabled = true;
38303 this.pnode.addClass("disabled");
38308 * Enables this TabPanelItem if it was previously disabled.
38310 enable : function(){
38311 this.disabled = false;
38312 this.pnode.removeClass("disabled");
38316 * Sets the content for this TabPanelItem.
38317 * @param {String} content The content
38318 * @param {Boolean} loadScripts true to look for and load scripts
38320 setContent : function(content, loadScripts){
38321 this.bodyEl.update(content, loadScripts);
38325 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38326 * @return {Roo.UpdateManager} The UpdateManager
38328 getUpdateManager : function(){
38329 return this.bodyEl.getUpdateManager();
38333 * Set a URL to be used to load the content for this TabPanelItem.
38334 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38335 * @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)
38336 * @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)
38337 * @return {Roo.UpdateManager} The UpdateManager
38339 setUrl : function(url, params, loadOnce){
38340 if(this.refreshDelegate){
38341 this.un('activate', this.refreshDelegate);
38343 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38344 this.on("activate", this.refreshDelegate);
38345 return this.bodyEl.getUpdateManager();
38349 _handleRefresh : function(url, params, loadOnce){
38350 if(!loadOnce || !this.loaded){
38351 var updater = this.bodyEl.getUpdateManager();
38352 updater.update(url, params, this._setLoaded.createDelegate(this));
38357 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38358 * Will fail silently if the setUrl method has not been called.
38359 * This does not activate the panel, just updates its content.
38361 refresh : function(){
38362 if(this.refreshDelegate){
38363 this.loaded = false;
38364 this.refreshDelegate();
38369 _setLoaded : function(){
38370 this.loaded = true;
38374 closeClick : function(e){
38377 this.fireEvent("beforeclose", this, o);
38378 if(o.cancel !== true){
38379 this.tabPanel.removeTab(this.id);
38383 * The text displayed in the tooltip for the close icon.
38386 closeText : "Close this tab"
38389 * This script refer to:
38390 * Title: International Telephone Input
38391 * Author: Jack O'Connor
38392 * Code version: v12.1.12
38393 * Availability: https://github.com/jackocnr/intl-tel-input.git
38396 Roo.bootstrap.PhoneInputData = function() {
38399 "Afghanistan (افغانستان)",
38404 "Albania (Shqipëri)",
38409 "Algeria (الجزائر)",
38434 "Antigua and Barbuda",
38444 "Armenia (Հայաստան)",
38460 "Austria (Österreich)",
38465 "Azerbaijan (Azərbaycan)",
38475 "Bahrain (البحرين)",
38480 "Bangladesh (বাংলাদেশ)",
38490 "Belarus (Беларусь)",
38495 "Belgium (België)",
38525 "Bosnia and Herzegovina (Босна и Херцеговина)",
38540 "British Indian Ocean Territory",
38545 "British Virgin Islands",
38555 "Bulgaria (България)",
38565 "Burundi (Uburundi)",
38570 "Cambodia (កម្ពុជា)",
38575 "Cameroon (Cameroun)",
38584 ["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"]
38587 "Cape Verde (Kabu Verdi)",
38592 "Caribbean Netherlands",
38603 "Central African Republic (République centrafricaine)",
38623 "Christmas Island",
38629 "Cocos (Keeling) Islands",
38640 "Comoros (جزر القمر)",
38645 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38650 "Congo (Republic) (Congo-Brazzaville)",
38670 "Croatia (Hrvatska)",
38691 "Czech Republic (Česká republika)",
38696 "Denmark (Danmark)",
38711 "Dominican Republic (República Dominicana)",
38715 ["809", "829", "849"]
38733 "Equatorial Guinea (Guinea Ecuatorial)",
38753 "Falkland Islands (Islas Malvinas)",
38758 "Faroe Islands (Føroyar)",
38779 "French Guiana (Guyane française)",
38784 "French Polynesia (Polynésie française)",
38799 "Georgia (საქართველო)",
38804 "Germany (Deutschland)",
38824 "Greenland (Kalaallit Nunaat)",
38861 "Guinea-Bissau (Guiné Bissau)",
38886 "Hungary (Magyarország)",
38891 "Iceland (Ísland)",
38911 "Iraq (العراق)",
38927 "Israel (ישראל)",
38954 "Jordan (الأردن)",
38959 "Kazakhstan (Казахстан)",
38980 "Kuwait (الكويت)",
38985 "Kyrgyzstan (Кыргызстан)",
38995 "Latvia (Latvija)",
39000 "Lebanon (لبنان)",
39015 "Libya (ليبيا)",
39025 "Lithuania (Lietuva)",
39040 "Macedonia (FYROM) (Македонија)",
39045 "Madagascar (Madagasikara)",
39075 "Marshall Islands",
39085 "Mauritania (موريتانيا)",
39090 "Mauritius (Moris)",
39111 "Moldova (Republica Moldova)",
39121 "Mongolia (Монгол)",
39126 "Montenegro (Crna Gora)",
39136 "Morocco (المغرب)",
39142 "Mozambique (Moçambique)",
39147 "Myanmar (Burma) (မြန်မာ)",
39152 "Namibia (Namibië)",
39167 "Netherlands (Nederland)",
39172 "New Caledonia (Nouvelle-Calédonie)",
39207 "North Korea (조선 민주주의 인민 공화국)",
39212 "Northern Mariana Islands",
39228 "Pakistan (پاکستان)",
39238 "Palestine (فلسطين)",
39248 "Papua New Guinea",
39290 "Réunion (La Réunion)",
39296 "Romania (România)",
39312 "Saint Barthélemy",
39323 "Saint Kitts and Nevis",
39333 "Saint Martin (Saint-Martin (partie française))",
39339 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39344 "Saint Vincent and the Grenadines",
39359 "São Tomé and Príncipe (São Tomé e Príncipe)",
39364 "Saudi Arabia (المملكة العربية السعودية)",
39369 "Senegal (Sénégal)",
39399 "Slovakia (Slovensko)",
39404 "Slovenia (Slovenija)",
39414 "Somalia (Soomaaliya)",
39424 "South Korea (대한민국)",
39429 "South Sudan (جنوب السودان)",
39439 "Sri Lanka (ශ්රී ලංකාව)",
39444 "Sudan (السودان)",
39454 "Svalbard and Jan Mayen",
39465 "Sweden (Sverige)",
39470 "Switzerland (Schweiz)",
39475 "Syria (سوريا)",
39520 "Trinidad and Tobago",
39525 "Tunisia (تونس)",
39530 "Turkey (Türkiye)",
39540 "Turks and Caicos Islands",
39550 "U.S. Virgin Islands",
39560 "Ukraine (Україна)",
39565 "United Arab Emirates (الإمارات العربية المتحدة)",
39587 "Uzbekistan (Oʻzbekiston)",
39597 "Vatican City (Città del Vaticano)",
39608 "Vietnam (Việt Nam)",
39613 "Wallis and Futuna (Wallis-et-Futuna)",
39618 "Western Sahara (الصحراء الغربية)",
39624 "Yemen (اليمن)",
39648 * This script refer to:
39649 * Title: International Telephone Input
39650 * Author: Jack O'Connor
39651 * Code version: v12.1.12
39652 * Availability: https://github.com/jackocnr/intl-tel-input.git
39656 * @class Roo.bootstrap.PhoneInput
39657 * @extends Roo.bootstrap.TriggerField
39658 * An input with International dial-code selection
39660 * @cfg {String} defaultDialCode default '+852'
39661 * @cfg {Array} preferedCountries default []
39664 * Create a new PhoneInput.
39665 * @param {Object} config Configuration options
39668 Roo.bootstrap.PhoneInput = function(config) {
39669 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39672 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39674 listWidth: undefined,
39676 selectedClass: 'active',
39678 invalidClass : "has-warning",
39680 validClass: 'has-success',
39682 allowed: '0123456789',
39685 * @cfg {String} defaultDialCode The default dial code when initializing the input
39687 defaultDialCode: '+852',
39690 * @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
39692 preferedCountries: false,
39694 getAutoCreate : function()
39696 var data = Roo.bootstrap.PhoneInputData();
39697 var align = this.labelAlign || this.parentLabelAlign();
39700 this.allCountries = [];
39701 this.dialCodeMapping = [];
39703 for (var i = 0; i < data.length; i++) {
39705 this.allCountries[i] = {
39709 priority: c[3] || 0,
39710 areaCodes: c[4] || null
39712 this.dialCodeMapping[c[2]] = {
39715 priority: c[3] || 0,
39716 areaCodes: c[4] || null
39728 cls : 'form-control tel-input',
39729 autocomplete: 'new-password'
39732 var hiddenInput = {
39735 cls: 'hidden-tel-input'
39739 hiddenInput.name = this.name;
39742 if (this.disabled) {
39743 input.disabled = true;
39746 var flag_container = {
39763 cls: this.hasFeedback ? 'has-feedback' : '',
39769 cls: 'dial-code-holder',
39776 cls: 'roo-select2-container input-group',
39783 if (this.fieldLabel.length) {
39786 tooltip: 'This field is required'
39792 cls: 'control-label',
39798 html: this.fieldLabel
39801 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39807 if(this.indicatorpos == 'right') {
39808 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39815 if(align == 'left') {
39823 if(this.labelWidth > 12){
39824 label.style = "width: " + this.labelWidth + 'px';
39826 if(this.labelWidth < 13 && this.labelmd == 0){
39827 this.labelmd = this.labelWidth;
39829 if(this.labellg > 0){
39830 label.cls += ' col-lg-' + this.labellg;
39831 input.cls += ' col-lg-' + (12 - this.labellg);
39833 if(this.labelmd > 0){
39834 label.cls += ' col-md-' + this.labelmd;
39835 container.cls += ' col-md-' + (12 - this.labelmd);
39837 if(this.labelsm > 0){
39838 label.cls += ' col-sm-' + this.labelsm;
39839 container.cls += ' col-sm-' + (12 - this.labelsm);
39841 if(this.labelxs > 0){
39842 label.cls += ' col-xs-' + this.labelxs;
39843 container.cls += ' col-xs-' + (12 - this.labelxs);
39853 var settings = this;
39855 ['xs','sm','md','lg'].map(function(size){
39856 if (settings[size]) {
39857 cfg.cls += ' col-' + size + '-' + settings[size];
39861 this.store = new Roo.data.Store({
39862 proxy : new Roo.data.MemoryProxy({}),
39863 reader : new Roo.data.JsonReader({
39874 'name' : 'dialCode',
39878 'name' : 'priority',
39882 'name' : 'areaCodes',
39889 if(!this.preferedCountries) {
39890 this.preferedCountries = [
39897 var p = this.preferedCountries.reverse();
39900 for (var i = 0; i < p.length; i++) {
39901 for (var j = 0; j < this.allCountries.length; j++) {
39902 if(this.allCountries[j].iso2 == p[i]) {
39903 var t = this.allCountries[j];
39904 this.allCountries.splice(j,1);
39905 this.allCountries.unshift(t);
39911 this.store.proxy.data = {
39913 data: this.allCountries
39919 initEvents : function()
39922 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39924 this.indicator = this.indicatorEl();
39925 this.flag = this.flagEl();
39926 this.dialCodeHolder = this.dialCodeHolderEl();
39928 this.trigger = this.el.select('div.flag-box',true).first();
39929 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39934 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39935 _this.list.setWidth(lw);
39938 this.list.on('mouseover', this.onViewOver, this);
39939 this.list.on('mousemove', this.onViewMove, this);
39940 this.inputEl().on("keyup", this.onKeyUp, this);
39942 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39944 this.view = new Roo.View(this.list, this.tpl, {
39945 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39948 this.view.on('click', this.onViewClick, this);
39949 this.setValue(this.defaultDialCode);
39952 onTriggerClick : function(e)
39954 Roo.log('trigger click');
39959 if(this.isExpanded()){
39961 this.hasFocus = false;
39963 this.store.load({});
39964 this.hasFocus = true;
39969 isExpanded : function()
39971 return this.list.isVisible();
39974 collapse : function()
39976 if(!this.isExpanded()){
39980 Roo.get(document).un('mousedown', this.collapseIf, this);
39981 Roo.get(document).un('mousewheel', this.collapseIf, this);
39982 this.fireEvent('collapse', this);
39986 expand : function()
39990 if(this.isExpanded() || !this.hasFocus){
39994 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39995 this.list.setWidth(lw);
39998 this.restrictHeight();
40000 Roo.get(document).on('mousedown', this.collapseIf, this);
40001 Roo.get(document).on('mousewheel', this.collapseIf, this);
40003 this.fireEvent('expand', this);
40006 restrictHeight : function()
40008 this.list.alignTo(this.inputEl(), this.listAlign);
40009 this.list.alignTo(this.inputEl(), this.listAlign);
40012 onViewOver : function(e, t)
40014 if(this.inKeyMode){
40017 var item = this.view.findItemFromChild(t);
40020 var index = this.view.indexOf(item);
40021 this.select(index, false);
40026 onViewClick : function(view, doFocus, el, e)
40028 var index = this.view.getSelectedIndexes()[0];
40030 var r = this.store.getAt(index);
40033 this.onSelect(r, index);
40035 if(doFocus !== false && !this.blockFocus){
40036 this.inputEl().focus();
40040 onViewMove : function(e, t)
40042 this.inKeyMode = false;
40045 select : function(index, scrollIntoView)
40047 this.selectedIndex = index;
40048 this.view.select(index);
40049 if(scrollIntoView !== false){
40050 var el = this.view.getNode(index);
40052 this.list.scrollChildIntoView(el, false);
40057 createList : function()
40059 this.list = Roo.get(document.body).createChild({
40061 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40062 style: 'display:none'
40065 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40068 collapseIf : function(e)
40070 var in_combo = e.within(this.el);
40071 var in_list = e.within(this.list);
40072 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40074 if (in_combo || in_list || is_list) {
40080 onSelect : function(record, index)
40082 if(this.fireEvent('beforeselect', this, record, index) !== false){
40084 this.setFlagClass(record.data.iso2);
40085 this.setDialCode(record.data.dialCode);
40086 this.hasFocus = false;
40088 this.fireEvent('select', this, record, index);
40092 flagEl : function()
40094 var flag = this.el.select('div.flag',true).first();
40101 dialCodeHolderEl : function()
40103 var d = this.el.select('input.dial-code-holder',true).first();
40110 setDialCode : function(v)
40112 this.dialCodeHolder.dom.value = '+'+v;
40115 setFlagClass : function(n)
40117 this.flag.dom.className = 'flag '+n;
40120 getValue : function()
40122 var v = this.inputEl().getValue();
40123 if(this.dialCodeHolder) {
40124 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40129 setValue : function(v)
40131 var d = this.getDialCode(v);
40133 //invalid dial code
40134 if(v.length == 0 || !d || d.length == 0) {
40136 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40137 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40143 this.setFlagClass(this.dialCodeMapping[d].iso2);
40144 this.setDialCode(d);
40145 this.inputEl().dom.value = v.replace('+'+d,'');
40146 this.hiddenEl().dom.value = this.getValue();
40151 getDialCode : function(v)
40155 if (v.length == 0) {
40156 return this.dialCodeHolder.dom.value;
40160 if (v.charAt(0) != "+") {
40163 var numericChars = "";
40164 for (var i = 1; i < v.length; i++) {
40165 var c = v.charAt(i);
40168 if (this.dialCodeMapping[numericChars]) {
40169 dialCode = v.substr(1, i);
40171 if (numericChars.length == 4) {
40181 this.setValue(this.defaultDialCode);
40185 hiddenEl : function()
40187 return this.el.select('input.hidden-tel-input',true).first();
40190 onKeyUp : function(e){
40192 var k = e.getKey();
40193 var c = e.getCharCode();
40196 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40197 this.allowed.indexOf(String.fromCharCode(c)) === -1
40202 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40205 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40209 this.setValue(this.getValue());
40214 * @class Roo.bootstrap.MoneyField
40215 * @extends Roo.bootstrap.ComboBox
40216 * Bootstrap MoneyField class
40219 * Create a new MoneyField.
40220 * @param {Object} config Configuration options
40223 Roo.bootstrap.MoneyField = function(config) {
40225 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40229 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40232 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40234 allowDecimals : true,
40236 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40238 decimalSeparator : ".",
40240 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40242 decimalPrecision : 0,
40244 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40246 allowNegative : true,
40248 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40252 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40254 minValue : Number.NEGATIVE_INFINITY,
40256 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40258 maxValue : Number.MAX_VALUE,
40260 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40262 minText : "The minimum value for this field is {0}",
40264 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40266 maxText : "The maximum value for this field is {0}",
40268 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40269 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40271 nanText : "{0} is not a valid number",
40273 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40277 * @cfg {String} defaults currency of the MoneyField
40278 * value should be in lkey
40280 defaultCurrency : false,
40282 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40284 thousandsDelimiter : false,
40294 getAutoCreate : function()
40296 var align = this.labelAlign || this.parentLabelAlign();
40308 cls : 'form-control roo-money-amount-input',
40309 autocomplete: 'new-password'
40312 var hiddenInput = {
40316 cls: 'hidden-number-input'
40320 hiddenInput.name = this.name;
40323 if (this.disabled) {
40324 input.disabled = true;
40327 var clg = 12 - this.inputlg;
40328 var cmd = 12 - this.inputmd;
40329 var csm = 12 - this.inputsm;
40330 var cxs = 12 - this.inputxs;
40334 cls : 'row roo-money-field',
40338 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40342 cls: 'roo-select2-container input-group',
40346 cls : 'form-control roo-money-currency-input',
40347 autocomplete: 'new-password',
40349 name : this.currencyName
40353 cls : 'input-group-addon',
40367 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40371 cls: this.hasFeedback ? 'has-feedback' : '',
40382 if (this.fieldLabel.length) {
40385 tooltip: 'This field is required'
40391 cls: 'control-label',
40397 html: this.fieldLabel
40400 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40406 if(this.indicatorpos == 'right') {
40407 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40414 if(align == 'left') {
40422 if(this.labelWidth > 12){
40423 label.style = "width: " + this.labelWidth + 'px';
40425 if(this.labelWidth < 13 && this.labelmd == 0){
40426 this.labelmd = this.labelWidth;
40428 if(this.labellg > 0){
40429 label.cls += ' col-lg-' + this.labellg;
40430 input.cls += ' col-lg-' + (12 - this.labellg);
40432 if(this.labelmd > 0){
40433 label.cls += ' col-md-' + this.labelmd;
40434 container.cls += ' col-md-' + (12 - this.labelmd);
40436 if(this.labelsm > 0){
40437 label.cls += ' col-sm-' + this.labelsm;
40438 container.cls += ' col-sm-' + (12 - this.labelsm);
40440 if(this.labelxs > 0){
40441 label.cls += ' col-xs-' + this.labelxs;
40442 container.cls += ' col-xs-' + (12 - this.labelxs);
40453 var settings = this;
40455 ['xs','sm','md','lg'].map(function(size){
40456 if (settings[size]) {
40457 cfg.cls += ' col-' + size + '-' + settings[size];
40464 initEvents : function()
40466 this.indicator = this.indicatorEl();
40468 this.initCurrencyEvent();
40470 this.initNumberEvent();
40473 initCurrencyEvent : function()
40476 throw "can not find store for combo";
40479 this.store = Roo.factory(this.store, Roo.data);
40480 this.store.parent = this;
40484 this.triggerEl = this.el.select('.input-group-addon', true).first();
40486 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40491 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40492 _this.list.setWidth(lw);
40495 this.list.on('mouseover', this.onViewOver, this);
40496 this.list.on('mousemove', this.onViewMove, this);
40497 this.list.on('scroll', this.onViewScroll, this);
40500 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40503 this.view = new Roo.View(this.list, this.tpl, {
40504 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40507 this.view.on('click', this.onViewClick, this);
40509 this.store.on('beforeload', this.onBeforeLoad, this);
40510 this.store.on('load', this.onLoad, this);
40511 this.store.on('loadexception', this.onLoadException, this);
40513 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40514 "up" : function(e){
40515 this.inKeyMode = true;
40519 "down" : function(e){
40520 if(!this.isExpanded()){
40521 this.onTriggerClick();
40523 this.inKeyMode = true;
40528 "enter" : function(e){
40531 if(this.fireEvent("specialkey", this, e)){
40532 this.onViewClick(false);
40538 "esc" : function(e){
40542 "tab" : function(e){
40545 if(this.fireEvent("specialkey", this, e)){
40546 this.onViewClick(false);
40554 doRelay : function(foo, bar, hname){
40555 if(hname == 'down' || this.scope.isExpanded()){
40556 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40564 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40568 initNumberEvent : function(e)
40570 this.inputEl().on("keydown" , this.fireKey, this);
40571 this.inputEl().on("focus", this.onFocus, this);
40572 this.inputEl().on("blur", this.onBlur, this);
40574 this.inputEl().relayEvent('keyup', this);
40576 if(this.indicator){
40577 this.indicator.addClass('invisible');
40580 this.originalValue = this.getValue();
40582 if(this.validationEvent == 'keyup'){
40583 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40584 this.inputEl().on('keyup', this.filterValidation, this);
40586 else if(this.validationEvent !== false){
40587 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40590 if(this.selectOnFocus){
40591 this.on("focus", this.preFocus, this);
40594 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40595 this.inputEl().on("keypress", this.filterKeys, this);
40597 this.inputEl().relayEvent('keypress', this);
40600 var allowed = "0123456789";
40602 if(this.allowDecimals){
40603 allowed += this.decimalSeparator;
40606 if(this.allowNegative){
40610 if(this.thousandsDelimiter) {
40614 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40616 var keyPress = function(e){
40618 var k = e.getKey();
40620 var c = e.getCharCode();
40623 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40624 allowed.indexOf(String.fromCharCode(c)) === -1
40630 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40634 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40639 this.inputEl().on("keypress", keyPress, this);
40643 onTriggerClick : function(e)
40650 this.loadNext = false;
40652 if(this.isExpanded()){
40657 this.hasFocus = true;
40659 if(this.triggerAction == 'all') {
40660 this.doQuery(this.allQuery, true);
40664 this.doQuery(this.getRawValue());
40667 getCurrency : function()
40669 var v = this.currencyEl().getValue();
40674 restrictHeight : function()
40676 this.list.alignTo(this.currencyEl(), this.listAlign);
40677 this.list.alignTo(this.currencyEl(), this.listAlign);
40680 onViewClick : function(view, doFocus, el, e)
40682 var index = this.view.getSelectedIndexes()[0];
40684 var r = this.store.getAt(index);
40687 this.onSelect(r, index);
40691 onSelect : function(record, index){
40693 if(this.fireEvent('beforeselect', this, record, index) !== false){
40695 this.setFromCurrencyData(index > -1 ? record.data : false);
40699 this.fireEvent('select', this, record, index);
40703 setFromCurrencyData : function(o)
40707 this.lastCurrency = o;
40709 if (this.currencyField) {
40710 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40712 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40715 this.lastSelectionText = currency;
40717 //setting default currency
40718 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40719 this.setCurrency(this.defaultCurrency);
40723 this.setCurrency(currency);
40726 setFromData : function(o)
40730 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40732 this.setFromCurrencyData(c);
40737 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40739 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40742 this.setValue(value);
40746 setCurrency : function(v)
40748 this.currencyValue = v;
40751 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40756 setValue : function(v)
40758 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40764 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40766 this.inputEl().dom.value = (v == '') ? '' :
40767 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40769 if(!this.allowZero && v === '0') {
40770 this.hiddenEl().dom.value = '';
40771 this.inputEl().dom.value = '';
40778 getRawValue : function()
40780 var v = this.inputEl().getValue();
40785 getValue : function()
40787 return this.fixPrecision(this.parseValue(this.getRawValue()));
40790 parseValue : function(value)
40792 if(this.thousandsDelimiter) {
40794 r = new RegExp(",", "g");
40795 value = value.replace(r, "");
40798 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40799 return isNaN(value) ? '' : value;
40803 fixPrecision : function(value)
40805 if(this.thousandsDelimiter) {
40807 r = new RegExp(",", "g");
40808 value = value.replace(r, "");
40811 var nan = isNaN(value);
40813 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40814 return nan ? '' : value;
40816 return parseFloat(value).toFixed(this.decimalPrecision);
40819 decimalPrecisionFcn : function(v)
40821 return Math.floor(v);
40824 validateValue : function(value)
40826 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40830 var num = this.parseValue(value);
40833 this.markInvalid(String.format(this.nanText, value));
40837 if(num < this.minValue){
40838 this.markInvalid(String.format(this.minText, this.minValue));
40842 if(num > this.maxValue){
40843 this.markInvalid(String.format(this.maxText, this.maxValue));
40850 validate : function()
40852 if(this.disabled || this.allowBlank){
40857 var currency = this.getCurrency();
40859 if(this.validateValue(this.getRawValue()) && currency.length){
40864 this.markInvalid();
40868 getName: function()
40873 beforeBlur : function()
40879 var v = this.parseValue(this.getRawValue());
40886 onBlur : function()
40890 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40891 //this.el.removeClass(this.focusClass);
40894 this.hasFocus = false;
40896 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40900 var v = this.getValue();
40902 if(String(v) !== String(this.startValue)){
40903 this.fireEvent('change', this, v, this.startValue);
40906 this.fireEvent("blur", this);
40909 inputEl : function()
40911 return this.el.select('.roo-money-amount-input', true).first();
40914 currencyEl : function()
40916 return this.el.select('.roo-money-currency-input', true).first();
40919 hiddenEl : function()
40921 return this.el.select('input.hidden-number-input',true).first();