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);
2228 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2231 * Displays this menu at a specific xy position
2232 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2233 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2235 showAt : function(xy, parentMenu, /* private: */_e){
2236 this.parentMenu = parentMenu;
2241 this.fireEvent("beforeshow", this);
2242 // xy = this.el.adjustForConstraints(xy);
2246 this.hideMenuItems();
2247 this.hidden = false;
2248 this.triggerEl.addClass('open');
2250 xy = this.el.getAlignToXY(this.triggerEl, '?');
2252 Roo.log(this.el.getWidth());
2254 Roo.log(Roo.lib.Dom.getViewWidth());
2256 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2257 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2260 // xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2262 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2267 this.fireEvent("show", this);
2273 this.doFocus.defer(50, this);
2277 doFocus : function(){
2279 this.focusEl.focus();
2284 * Hides this menu and optionally all parent menus
2285 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2287 hide : function(deep)
2290 this.hideMenuItems();
2291 if(this.el && this.isVisible()){
2292 this.fireEvent("beforehide", this);
2293 if(this.activeItem){
2294 this.activeItem.deactivate();
2295 this.activeItem = null;
2297 this.triggerEl.removeClass('open');;
2299 this.fireEvent("hide", this);
2301 if(deep === true && this.parentMenu){
2302 this.parentMenu.hide(true);
2306 onTriggerClick : function(e)
2308 Roo.log('trigger click');
2310 var target = e.getTarget();
2312 Roo.log(target.nodeName.toLowerCase());
2314 if(target.nodeName.toLowerCase() === 'i'){
2320 onTriggerPress : function(e)
2322 Roo.log('trigger press');
2323 //Roo.log(e.getTarget());
2324 // Roo.log(this.triggerEl.dom);
2326 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2327 var pel = Roo.get(e.getTarget());
2328 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2329 Roo.log('is treeview or dropdown?');
2333 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2337 if (this.isVisible()) {
2342 this.show(this.triggerEl, false, false);
2345 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2352 hideMenuItems : function()
2354 Roo.log("hide Menu Items");
2358 //$(backdrop).remove()
2359 this.el.select('.open',true).each(function(aa) {
2361 aa.removeClass('open');
2362 //var parent = getParent($(this))
2363 //var relatedTarget = { relatedTarget: this }
2365 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2366 //if (e.isDefaultPrevented()) return
2367 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2370 addxtypeChild : function (tree, cntr) {
2371 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2373 this.menuitems.add(comp);
2385 this.getEl().dom.innerHTML = '';
2386 this.menuitems.clear();
2400 * @class Roo.bootstrap.MenuItem
2401 * @extends Roo.bootstrap.Component
2402 * Bootstrap MenuItem class
2403 * @cfg {String} html the menu label
2404 * @cfg {String} href the link
2405 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2406 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2407 * @cfg {Boolean} active used on sidebars to highlight active itesm
2408 * @cfg {String} fa favicon to show on left of menu item.
2409 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2413 * Create a new MenuItem
2414 * @param {Object} config The config object
2418 Roo.bootstrap.MenuItem = function(config){
2419 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2424 * The raw click event for the entire grid.
2425 * @param {Roo.bootstrap.MenuItem} this
2426 * @param {Roo.EventObject} e
2432 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2436 preventDefault: false,
2437 isContainer : false,
2441 getAutoCreate : function(){
2443 if(this.isContainer){
2446 cls: 'dropdown-menu-item'
2460 if (this.fa !== false) {
2463 cls : 'fa fa-' + this.fa
2472 cls: 'dropdown-menu-item',
2475 if (this.parent().type == 'treeview') {
2476 cfg.cls = 'treeview-menu';
2479 cfg.cls += ' active';
2484 anc.href = this.href || cfg.cn[0].href ;
2485 ctag.html = this.html || cfg.cn[0].html ;
2489 initEvents: function()
2491 if (this.parent().type == 'treeview') {
2492 this.el.select('a').on('click', this.onClick, this);
2496 this.menu.parentType = this.xtype;
2497 this.menu.triggerEl = this.el;
2498 this.menu = this.addxtype(Roo.apply({}, this.menu));
2502 onClick : function(e)
2504 Roo.log('item on click ');
2506 if(this.preventDefault){
2509 //this.parent().hideMenuItems();
2511 this.fireEvent('click', this, e);
2530 * @class Roo.bootstrap.MenuSeparator
2531 * @extends Roo.bootstrap.Component
2532 * Bootstrap MenuSeparator class
2535 * Create a new MenuItem
2536 * @param {Object} config The config object
2540 Roo.bootstrap.MenuSeparator = function(config){
2541 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2544 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2546 getAutoCreate : function(){
2565 * @class Roo.bootstrap.Modal
2566 * @extends Roo.bootstrap.Component
2567 * Bootstrap Modal class
2568 * @cfg {String} title Title of dialog
2569 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2570 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2571 * @cfg {Boolean} specificTitle default false
2572 * @cfg {Array} buttons Array of buttons or standard button set..
2573 * @cfg {String} buttonPosition (left|right|center) default right
2574 * @cfg {Boolean} animate default true
2575 * @cfg {Boolean} allow_close default true
2576 * @cfg {Boolean} fitwindow default false
2577 * @cfg {String} size (sm|lg) default empty
2581 * Create a new Modal Dialog
2582 * @param {Object} config The config object
2585 Roo.bootstrap.Modal = function(config){
2586 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2591 * The raw btnclick event for the button
2592 * @param {Roo.EventObject} e
2597 * Fire when dialog resize
2598 * @param {Roo.bootstrap.Modal} this
2599 * @param {Roo.EventObject} e
2603 this.buttons = this.buttons || [];
2606 this.tmpl = Roo.factory(this.tmpl);
2611 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2613 title : 'test dialog',
2623 specificTitle: false,
2625 buttonPosition: 'right',
2644 onRender : function(ct, position)
2646 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2649 var cfg = Roo.apply({}, this.getAutoCreate());
2652 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2654 //if (!cfg.name.length) {
2658 cfg.cls += ' ' + this.cls;
2661 cfg.style = this.style;
2663 this.el = Roo.get(document.body).createChild(cfg, position);
2665 //var type = this.el.dom.type;
2668 if(this.tabIndex !== undefined){
2669 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2672 this.dialogEl = this.el.select('.modal-dialog',true).first();
2673 this.bodyEl = this.el.select('.modal-body',true).first();
2674 this.closeEl = this.el.select('.modal-header .close', true).first();
2675 this.headerEl = this.el.select('.modal-header',true).first();
2676 this.titleEl = this.el.select('.modal-title',true).first();
2677 this.footerEl = this.el.select('.modal-footer',true).first();
2679 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2681 //this.el.addClass("x-dlg-modal");
2683 if (this.buttons.length) {
2684 Roo.each(this.buttons, function(bb) {
2685 var b = Roo.apply({}, bb);
2686 b.xns = b.xns || Roo.bootstrap;
2687 b.xtype = b.xtype || 'Button';
2688 if (typeof(b.listeners) == 'undefined') {
2689 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2692 var btn = Roo.factory(b);
2694 btn.render(this.el.select('.modal-footer div').first());
2698 // render the children.
2701 if(typeof(this.items) != 'undefined'){
2702 var items = this.items;
2705 for(var i =0;i < items.length;i++) {
2706 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2710 this.items = nitems;
2712 // where are these used - they used to be body/close/footer
2716 //this.el.addClass([this.fieldClass, this.cls]);
2720 getAutoCreate : function(){
2725 html : this.html || ''
2730 cls : 'modal-title',
2734 if(this.specificTitle){
2740 if (this.allow_close) {
2752 if(this.size.length){
2753 size = 'modal-' + this.size;
2760 cls: "modal-dialog " + size,
2763 cls : "modal-content",
2766 cls : 'modal-header',
2771 cls : 'modal-footer',
2775 cls: 'btn-' + this.buttonPosition
2792 modal.cls += ' fade';
2798 getChildContainer : function() {
2803 getButtonContainer : function() {
2804 return this.el.select('.modal-footer div',true).first();
2807 initEvents : function()
2809 if (this.allow_close) {
2810 this.closeEl.on('click', this.hide, this);
2812 Roo.EventManager.onWindowResize(this.resize, this, true);
2819 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2820 if (this.fitwindow) {
2821 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2822 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2827 setSize : function(w,h)
2837 if (!this.rendered) {
2841 //this.el.setStyle('display', 'block');
2842 this.el.removeClass('hideing');
2843 this.el.addClass('show');
2845 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2848 this.el.addClass('in');
2851 this.el.addClass('in');
2855 // not sure how we can show data in here..
2857 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2860 Roo.get(document.body).addClass("x-body-masked");
2862 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2863 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2864 this.maskEl.addClass('show');
2868 this.fireEvent('show', this);
2870 // set zindex here - otherwise it appears to be ignored...
2871 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2874 this.items.forEach( function(e) {
2875 e.layout ? e.layout() : false;
2883 if(this.fireEvent("beforehide", this) !== false){
2884 this.maskEl.removeClass('show');
2885 Roo.get(document.body).removeClass("x-body-masked");
2886 this.el.removeClass('in');
2887 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2889 if(this.animate){ // why
2890 this.el.addClass('hideing');
2892 if (!this.el.hasClass('hideing')) {
2893 return; // it's been shown again...
2895 this.el.removeClass('show');
2896 this.el.removeClass('hideing');
2900 this.el.removeClass('show');
2902 this.fireEvent('hide', this);
2905 isVisible : function()
2908 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2912 addButton : function(str, cb)
2916 var b = Roo.apply({}, { html : str } );
2917 b.xns = b.xns || Roo.bootstrap;
2918 b.xtype = b.xtype || 'Button';
2919 if (typeof(b.listeners) == 'undefined') {
2920 b.listeners = { click : cb.createDelegate(this) };
2923 var btn = Roo.factory(b);
2925 btn.render(this.el.select('.modal-footer div').first());
2931 setDefaultButton : function(btn)
2933 //this.el.select('.modal-footer').()
2937 resizeTo: function(w,h)
2941 this.dialogEl.setWidth(w);
2942 if (this.diff === false) {
2943 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2946 this.bodyEl.setHeight(h-this.diff);
2948 this.fireEvent('resize', this);
2951 setContentSize : function(w, h)
2955 onButtonClick: function(btn,e)
2958 this.fireEvent('btnclick', btn.name, e);
2961 * Set the title of the Dialog
2962 * @param {String} str new Title
2964 setTitle: function(str) {
2965 this.titleEl.dom.innerHTML = str;
2968 * Set the body of the Dialog
2969 * @param {String} str new Title
2971 setBody: function(str) {
2972 this.bodyEl.dom.innerHTML = str;
2975 * Set the body of the Dialog using the template
2976 * @param {Obj} data - apply this data to the template and replace the body contents.
2978 applyBody: function(obj)
2981 Roo.log("Error - using apply Body without a template");
2984 this.tmpl.overwrite(this.bodyEl, obj);
2990 Roo.apply(Roo.bootstrap.Modal, {
2992 * Button config that displays a single OK button
3001 * Button config that displays Yes and No buttons
3017 * Button config that displays OK and Cancel buttons
3032 * Button config that displays Yes, No and Cancel buttons
3056 * messagebox - can be used as a replace
3060 * @class Roo.MessageBox
3061 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3065 Roo.Msg.alert('Status', 'Changes saved successfully.');
3067 // Prompt for user data:
3068 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3070 // process text value...
3074 // Show a dialog using config options:
3076 title:'Save Changes?',
3077 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3078 buttons: Roo.Msg.YESNOCANCEL,
3085 Roo.bootstrap.MessageBox = function(){
3086 var dlg, opt, mask, waitTimer;
3087 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3088 var buttons, activeTextEl, bwidth;
3092 var handleButton = function(button){
3094 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3098 var handleHide = function(){
3100 dlg.el.removeClass(opt.cls);
3103 // Roo.TaskMgr.stop(waitTimer);
3104 // waitTimer = null;
3109 var updateButtons = function(b){
3112 buttons["ok"].hide();
3113 buttons["cancel"].hide();
3114 buttons["yes"].hide();
3115 buttons["no"].hide();
3116 //dlg.footer.dom.style.display = 'none';
3119 dlg.footerEl.dom.style.display = '';
3120 for(var k in buttons){
3121 if(typeof buttons[k] != "function"){
3124 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3125 width += buttons[k].el.getWidth()+15;
3135 var handleEsc = function(d, k, e){
3136 if(opt && opt.closable !== false){
3146 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3147 * @return {Roo.BasicDialog} The BasicDialog element
3149 getDialog : function(){
3151 dlg = new Roo.bootstrap.Modal( {
3154 //constraintoviewport:false,
3156 //collapsible : false,
3161 //buttonAlign:"center",
3162 closeClick : function(){
3163 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3166 handleButton("cancel");
3171 dlg.on("hide", handleHide);
3173 //dlg.addKeyListener(27, handleEsc);
3175 this.buttons = buttons;
3176 var bt = this.buttonText;
3177 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3178 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3179 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3180 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3182 bodyEl = dlg.bodyEl.createChild({
3184 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3185 '<textarea class="roo-mb-textarea"></textarea>' +
3186 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3188 msgEl = bodyEl.dom.firstChild;
3189 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3190 textboxEl.enableDisplayMode();
3191 textboxEl.addKeyListener([10,13], function(){
3192 if(dlg.isVisible() && opt && opt.buttons){
3195 }else if(opt.buttons.yes){
3196 handleButton("yes");
3200 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3201 textareaEl.enableDisplayMode();
3202 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3203 progressEl.enableDisplayMode();
3205 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3206 var pf = progressEl.dom.firstChild;
3208 pp = Roo.get(pf.firstChild);
3209 pp.setHeight(pf.offsetHeight);
3217 * Updates the message box body text
3218 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3219 * the XHTML-compliant non-breaking space character '&#160;')
3220 * @return {Roo.MessageBox} This message box
3222 updateText : function(text)
3224 if(!dlg.isVisible() && !opt.width){
3225 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3226 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3228 msgEl.innerHTML = text || ' ';
3230 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3231 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3233 Math.min(opt.width || cw , this.maxWidth),
3234 Math.max(opt.minWidth || this.minWidth, bwidth)
3237 activeTextEl.setWidth(w);
3239 if(dlg.isVisible()){
3240 dlg.fixedcenter = false;
3242 // to big, make it scroll. = But as usual stupid IE does not support
3245 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3246 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3247 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3249 bodyEl.dom.style.height = '';
3250 bodyEl.dom.style.overflowY = '';
3253 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3255 bodyEl.dom.style.overflowX = '';
3258 dlg.setContentSize(w, bodyEl.getHeight());
3259 if(dlg.isVisible()){
3260 dlg.fixedcenter = true;
3266 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3267 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3268 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3269 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3270 * @return {Roo.MessageBox} This message box
3272 updateProgress : function(value, text){
3274 this.updateText(text);
3277 if (pp) { // weird bug on my firefox - for some reason this is not defined
3278 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3279 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3285 * Returns true if the message box is currently displayed
3286 * @return {Boolean} True if the message box is visible, else false
3288 isVisible : function(){
3289 return dlg && dlg.isVisible();
3293 * Hides the message box if it is displayed
3296 if(this.isVisible()){
3302 * Displays a new message box, or reinitializes an existing message box, based on the config options
3303 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3304 * The following config object properties are supported:
3306 Property Type Description
3307 ---------- --------------- ------------------------------------------------------------------------------------
3308 animEl String/Element An id or Element from which the message box should animate as it opens and
3309 closes (defaults to undefined)
3310 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3311 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3312 closable Boolean False to hide the top-right close button (defaults to true). Note that
3313 progress and wait dialogs will ignore this property and always hide the
3314 close button as they can only be closed programmatically.
3315 cls String A custom CSS class to apply to the message box element
3316 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3317 displayed (defaults to 75)
3318 fn Function A callback function to execute after closing the dialog. The arguments to the
3319 function will be btn (the name of the button that was clicked, if applicable,
3320 e.g. "ok"), and text (the value of the active text field, if applicable).
3321 Progress and wait dialogs will ignore this option since they do not respond to
3322 user actions and can only be closed programmatically, so any required function
3323 should be called by the same code after it closes the dialog.
3324 icon String A CSS class that provides a background image to be used as an icon for
3325 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3326 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3327 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3328 modal Boolean False to allow user interaction with the page while the message box is
3329 displayed (defaults to true)
3330 msg String A string that will replace the existing message box body text (defaults
3331 to the XHTML-compliant non-breaking space character ' ')
3332 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3333 progress Boolean True to display a progress bar (defaults to false)
3334 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3335 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3336 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3337 title String The title text
3338 value String The string value to set into the active textbox element if displayed
3339 wait Boolean True to display a progress bar (defaults to false)
3340 width Number The width of the dialog in pixels
3347 msg: 'Please enter your address:',
3349 buttons: Roo.MessageBox.OKCANCEL,
3352 animEl: 'addAddressBtn'
3355 * @param {Object} config Configuration options
3356 * @return {Roo.MessageBox} This message box
3358 show : function(options)
3361 // this causes nightmares if you show one dialog after another
3362 // especially on callbacks..
3364 if(this.isVisible()){
3367 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3368 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3369 Roo.log("New Dialog Message:" + options.msg )
3370 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3371 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3374 var d = this.getDialog();
3376 d.setTitle(opt.title || " ");
3377 d.closeEl.setDisplayed(opt.closable !== false);
3378 activeTextEl = textboxEl;
3379 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3384 textareaEl.setHeight(typeof opt.multiline == "number" ?
3385 opt.multiline : this.defaultTextHeight);
3386 activeTextEl = textareaEl;
3395 progressEl.setDisplayed(opt.progress === true);
3396 this.updateProgress(0);
3397 activeTextEl.dom.value = opt.value || "";
3399 dlg.setDefaultButton(activeTextEl);
3401 var bs = opt.buttons;
3405 }else if(bs && bs.yes){
3406 db = buttons["yes"];
3408 dlg.setDefaultButton(db);
3410 bwidth = updateButtons(opt.buttons);
3411 this.updateText(opt.msg);
3413 d.el.addClass(opt.cls);
3415 d.proxyDrag = opt.proxyDrag === true;
3416 d.modal = opt.modal !== false;
3417 d.mask = opt.modal !== false ? mask : false;
3419 // force it to the end of the z-index stack so it gets a cursor in FF
3420 document.body.appendChild(dlg.el.dom);
3421 d.animateTarget = null;
3422 d.show(options.animEl);
3428 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3429 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3430 * and closing the message box when the process is complete.
3431 * @param {String} title The title bar text
3432 * @param {String} msg The message box body text
3433 * @return {Roo.MessageBox} This message box
3435 progress : function(title, msg){
3442 minWidth: this.minProgressWidth,
3449 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3450 * If a callback function is passed it will be called after the user clicks the button, and the
3451 * id of the button that was clicked will be passed as the only parameter to the callback
3452 * (could also be the top-right close button).
3453 * @param {String} title The title bar text
3454 * @param {String} msg The message box body text
3455 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3456 * @param {Object} scope (optional) The scope of the callback function
3457 * @return {Roo.MessageBox} This message box
3459 alert : function(title, msg, fn, scope)
3474 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3475 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3476 * You are responsible for closing the message box when the process is complete.
3477 * @param {String} msg The message box body text
3478 * @param {String} title (optional) The title bar text
3479 * @return {Roo.MessageBox} This message box
3481 wait : function(msg, title){
3492 waitTimer = Roo.TaskMgr.start({
3494 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3502 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3503 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3504 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3505 * @param {String} title The title bar text
3506 * @param {String} msg The message box body text
3507 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3508 * @param {Object} scope (optional) The scope of the callback function
3509 * @return {Roo.MessageBox} This message box
3511 confirm : function(title, msg, fn, scope){
3515 buttons: this.YESNO,
3524 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3525 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3526 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3527 * (could also be the top-right close button) and the text that was entered will be passed as the two
3528 * parameters to the callback.
3529 * @param {String} title The title bar text
3530 * @param {String} msg The message box body text
3531 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3532 * @param {Object} scope (optional) The scope of the callback function
3533 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3534 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3535 * @return {Roo.MessageBox} This message box
3537 prompt : function(title, msg, fn, scope, multiline){
3541 buttons: this.OKCANCEL,
3546 multiline: multiline,
3553 * Button config that displays a single OK button
3558 * Button config that displays Yes and No buttons
3561 YESNO : {yes:true, no:true},
3563 * Button config that displays OK and Cancel buttons
3566 OKCANCEL : {ok:true, cancel:true},
3568 * Button config that displays Yes, No and Cancel buttons
3571 YESNOCANCEL : {yes:true, no:true, cancel:true},
3574 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3577 defaultTextHeight : 75,
3579 * The maximum width in pixels of the message box (defaults to 600)
3584 * The minimum width in pixels of the message box (defaults to 100)
3589 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3590 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3593 minProgressWidth : 250,
3595 * An object containing the default button text strings that can be overriden for localized language support.
3596 * Supported properties are: ok, cancel, yes and no.
3597 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3610 * Shorthand for {@link Roo.MessageBox}
3612 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3613 Roo.Msg = Roo.Msg || Roo.MessageBox;
3622 * @class Roo.bootstrap.Navbar
3623 * @extends Roo.bootstrap.Component
3624 * Bootstrap Navbar class
3627 * Create a new Navbar
3628 * @param {Object} config The config object
3632 Roo.bootstrap.Navbar = function(config){
3633 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3637 * @event beforetoggle
3638 * Fire before toggle the menu
3639 * @param {Roo.EventObject} e
3641 "beforetoggle" : true
3645 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3654 getAutoCreate : function(){
3657 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3661 initEvents :function ()
3663 //Roo.log(this.el.select('.navbar-toggle',true));
3664 this.el.select('.navbar-toggle',true).on('click', function() {
3665 if(this.fireEvent('beforetoggle', this) !== false){
3666 this.el.select('.navbar-collapse',true).toggleClass('in');
3676 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3678 var size = this.el.getSize();
3679 this.maskEl.setSize(size.width, size.height);
3680 this.maskEl.enableDisplayMode("block");
3689 getChildContainer : function()
3691 if (this.el.select('.collapse').getCount()) {
3692 return this.el.select('.collapse',true).first();
3725 * @class Roo.bootstrap.NavSimplebar
3726 * @extends Roo.bootstrap.Navbar
3727 * Bootstrap Sidebar class
3729 * @cfg {Boolean} inverse is inverted color
3731 * @cfg {String} type (nav | pills | tabs)
3732 * @cfg {Boolean} arrangement stacked | justified
3733 * @cfg {String} align (left | right) alignment
3735 * @cfg {Boolean} main (true|false) main nav bar? default false
3736 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3738 * @cfg {String} tag (header|footer|nav|div) default is nav
3744 * Create a new Sidebar
3745 * @param {Object} config The config object
3749 Roo.bootstrap.NavSimplebar = function(config){
3750 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3753 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3769 getAutoCreate : function(){
3773 tag : this.tag || 'div',
3786 this.type = this.type || 'nav';
3787 if (['tabs','pills'].indexOf(this.type)!==-1) {
3788 cfg.cn[0].cls += ' nav-' + this.type
3792 if (this.type!=='nav') {
3793 Roo.log('nav type must be nav/tabs/pills')
3795 cfg.cn[0].cls += ' navbar-nav'
3801 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3802 cfg.cn[0].cls += ' nav-' + this.arrangement;
3806 if (this.align === 'right') {
3807 cfg.cn[0].cls += ' navbar-right';
3811 cfg.cls += ' navbar-inverse';
3838 * @class Roo.bootstrap.NavHeaderbar
3839 * @extends Roo.bootstrap.NavSimplebar
3840 * Bootstrap Sidebar class
3842 * @cfg {String} brand what is brand
3843 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3844 * @cfg {String} brand_href href of the brand
3845 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3846 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3847 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3848 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3851 * Create a new Sidebar
3852 * @param {Object} config The config object
3856 Roo.bootstrap.NavHeaderbar = function(config){
3857 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3861 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3868 desktopCenter : false,
3871 getAutoCreate : function(){
3874 tag: this.nav || 'nav',
3881 if (this.desktopCenter) {
3882 cn.push({cls : 'container', cn : []});
3889 cls: 'navbar-header',
3894 cls: 'navbar-toggle',
3895 'data-toggle': 'collapse',
3900 html: 'Toggle navigation'
3922 cls: 'collapse navbar-collapse',
3926 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3928 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3929 cfg.cls += ' navbar-' + this.position;
3931 // tag can override this..
3933 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3936 if (this.brand !== '') {
3939 href: this.brand_href ? this.brand_href : '#',
3940 cls: 'navbar-brand',
3948 cfg.cls += ' main-nav';
3956 getHeaderChildContainer : function()
3958 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3959 return this.el.select('.navbar-header',true).first();
3962 return this.getChildContainer();
3966 initEvents : function()
3968 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3970 if (this.autohide) {
3975 Roo.get(document).on('scroll',function(e) {
3976 var ns = Roo.get(document).getScroll().top;
3977 var os = prevScroll;
3981 ft.removeClass('slideDown');
3982 ft.addClass('slideUp');
3985 ft.removeClass('slideUp');
3986 ft.addClass('slideDown');
4007 * @class Roo.bootstrap.NavSidebar
4008 * @extends Roo.bootstrap.Navbar
4009 * Bootstrap Sidebar class
4012 * Create a new Sidebar
4013 * @param {Object} config The config object
4017 Roo.bootstrap.NavSidebar = function(config){
4018 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4021 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4023 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4025 getAutoCreate : function(){
4030 cls: 'sidebar sidebar-nav'
4052 * @class Roo.bootstrap.NavGroup
4053 * @extends Roo.bootstrap.Component
4054 * Bootstrap NavGroup class
4055 * @cfg {String} align (left|right)
4056 * @cfg {Boolean} inverse
4057 * @cfg {String} type (nav|pills|tab) default nav
4058 * @cfg {String} navId - reference Id for navbar.
4062 * Create a new nav group
4063 * @param {Object} config The config object
4066 Roo.bootstrap.NavGroup = function(config){
4067 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4070 Roo.bootstrap.NavGroup.register(this);
4074 * Fires when the active item changes
4075 * @param {Roo.bootstrap.NavGroup} this
4076 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4077 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4084 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4095 getAutoCreate : function()
4097 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4104 if (['tabs','pills'].indexOf(this.type)!==-1) {
4105 cfg.cls += ' nav-' + this.type
4107 if (this.type!=='nav') {
4108 Roo.log('nav type must be nav/tabs/pills')
4110 cfg.cls += ' navbar-nav'
4113 if (this.parent() && this.parent().sidebar) {
4116 cls: 'dashboard-menu sidebar-menu'
4122 if (this.form === true) {
4128 if (this.align === 'right') {
4129 cfg.cls += ' navbar-right';
4131 cfg.cls += ' navbar-left';
4135 if (this.align === 'right') {
4136 cfg.cls += ' navbar-right';
4140 cfg.cls += ' navbar-inverse';
4148 * sets the active Navigation item
4149 * @param {Roo.bootstrap.NavItem} the new current navitem
4151 setActiveItem : function(item)
4154 Roo.each(this.navItems, function(v){
4159 v.setActive(false, true);
4166 item.setActive(true, true);
4167 this.fireEvent('changed', this, item, prev);
4172 * gets the active Navigation item
4173 * @return {Roo.bootstrap.NavItem} the current navitem
4175 getActive : function()
4179 Roo.each(this.navItems, function(v){
4190 indexOfNav : function()
4194 Roo.each(this.navItems, function(v,i){
4205 * adds a Navigation item
4206 * @param {Roo.bootstrap.NavItem} the navitem to add
4208 addItem : function(cfg)
4210 var cn = new Roo.bootstrap.NavItem(cfg);
4212 cn.parentId = this.id;
4213 cn.onRender(this.el, null);
4217 * register a Navigation item
4218 * @param {Roo.bootstrap.NavItem} the navitem to add
4220 register : function(item)
4222 this.navItems.push( item);
4223 item.navId = this.navId;
4228 * clear all the Navigation item
4231 clearAll : function()
4234 this.el.dom.innerHTML = '';
4237 getNavItem: function(tabId)
4240 Roo.each(this.navItems, function(e) {
4241 if (e.tabId == tabId) {
4251 setActiveNext : function()
4253 var i = this.indexOfNav(this.getActive());
4254 if (i > this.navItems.length) {
4257 this.setActiveItem(this.navItems[i+1]);
4259 setActivePrev : function()
4261 var i = this.indexOfNav(this.getActive());
4265 this.setActiveItem(this.navItems[i-1]);
4267 clearWasActive : function(except) {
4268 Roo.each(this.navItems, function(e) {
4269 if (e.tabId != except.tabId && e.was_active) {
4270 e.was_active = false;
4277 getWasActive : function ()
4280 Roo.each(this.navItems, function(e) {
4295 Roo.apply(Roo.bootstrap.NavGroup, {
4299 * register a Navigation Group
4300 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4302 register : function(navgrp)
4304 this.groups[navgrp.navId] = navgrp;
4308 * fetch a Navigation Group based on the navigation ID
4309 * @param {string} the navgroup to add
4310 * @returns {Roo.bootstrap.NavGroup} the navgroup
4312 get: function(navId) {
4313 if (typeof(this.groups[navId]) == 'undefined') {
4315 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4317 return this.groups[navId] ;
4332 * @class Roo.bootstrap.NavItem
4333 * @extends Roo.bootstrap.Component
4334 * Bootstrap Navbar.NavItem class
4335 * @cfg {String} href link to
4336 * @cfg {String} html content of button
4337 * @cfg {String} badge text inside badge
4338 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4339 * @cfg {String} glyphicon name of glyphicon
4340 * @cfg {String} icon name of font awesome icon
4341 * @cfg {Boolean} active Is item active
4342 * @cfg {Boolean} disabled Is item disabled
4344 * @cfg {Boolean} preventDefault (true | false) default false
4345 * @cfg {String} tabId the tab that this item activates.
4346 * @cfg {String} tagtype (a|span) render as a href or span?
4347 * @cfg {Boolean} animateRef (true|false) link to element default false
4350 * Create a new Navbar Item
4351 * @param {Object} config The config object
4353 Roo.bootstrap.NavItem = function(config){
4354 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4359 * The raw click event for the entire grid.
4360 * @param {Roo.EventObject} e
4365 * Fires when the active item active state changes
4366 * @param {Roo.bootstrap.NavItem} this
4367 * @param {boolean} state the new state
4373 * Fires when scroll to element
4374 * @param {Roo.bootstrap.NavItem} this
4375 * @param {Object} options
4376 * @param {Roo.EventObject} e
4384 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4392 preventDefault : false,
4399 getAutoCreate : function(){
4408 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4410 if (this.disabled) {
4411 cfg.cls += ' disabled';
4414 if (this.href || this.html || this.glyphicon || this.icon) {
4418 href : this.href || "#",
4419 html: this.html || ''
4424 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4427 if(this.glyphicon) {
4428 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4433 cfg.cn[0].html += " <span class='caret'></span>";
4437 if (this.badge !== '') {
4439 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4447 initEvents: function()
4449 if (typeof (this.menu) != 'undefined') {
4450 this.menu.parentType = this.xtype;
4451 this.menu.triggerEl = this.el;
4452 this.menu = this.addxtype(Roo.apply({}, this.menu));
4455 this.el.select('a',true).on('click', this.onClick, this);
4457 if(this.tagtype == 'span'){
4458 this.el.select('span',true).on('click', this.onClick, this);
4461 // at this point parent should be available..
4462 this.parent().register(this);
4465 onClick : function(e)
4467 if (e.getTarget('.dropdown-menu-item')) {
4468 // did you click on a menu itemm.... - then don't trigger onclick..
4473 this.preventDefault ||
4476 Roo.log("NavItem - prevent Default?");
4480 if (this.disabled) {
4484 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4485 if (tg && tg.transition) {
4486 Roo.log("waiting for the transitionend");
4492 //Roo.log("fire event clicked");
4493 if(this.fireEvent('click', this, e) === false){
4497 if(this.tagtype == 'span'){
4501 //Roo.log(this.href);
4502 var ael = this.el.select('a',true).first();
4505 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4506 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4507 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4508 return; // ignore... - it's a 'hash' to another page.
4510 Roo.log("NavItem - prevent Default?");
4512 this.scrollToElement(e);
4516 var p = this.parent();
4518 if (['tabs','pills'].indexOf(p.type)!==-1) {
4519 if (typeof(p.setActiveItem) !== 'undefined') {
4520 p.setActiveItem(this);
4524 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4525 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4526 // remove the collapsed menu expand...
4527 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4531 isActive: function () {
4534 setActive : function(state, fire, is_was_active)
4536 if (this.active && !state && this.navId) {
4537 this.was_active = true;
4538 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4540 nv.clearWasActive(this);
4544 this.active = state;
4547 this.el.removeClass('active');
4548 } else if (!this.el.hasClass('active')) {
4549 this.el.addClass('active');
4552 this.fireEvent('changed', this, state);
4555 // show a panel if it's registered and related..
4557 if (!this.navId || !this.tabId || !state || is_was_active) {
4561 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4565 var pan = tg.getPanelByName(this.tabId);
4569 // if we can not flip to new panel - go back to old nav highlight..
4570 if (false == tg.showPanel(pan)) {
4571 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4573 var onav = nv.getWasActive();
4575 onav.setActive(true, false, true);
4584 // this should not be here...
4585 setDisabled : function(state)
4587 this.disabled = state;
4589 this.el.removeClass('disabled');
4590 } else if (!this.el.hasClass('disabled')) {
4591 this.el.addClass('disabled');
4597 * Fetch the element to display the tooltip on.
4598 * @return {Roo.Element} defaults to this.el
4600 tooltipEl : function()
4602 return this.el.select('' + this.tagtype + '', true).first();
4605 scrollToElement : function(e)
4607 var c = document.body;
4610 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4612 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4613 c = document.documentElement;
4616 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4622 var o = target.calcOffsetsTo(c);
4629 this.fireEvent('scrollto', this, options, e);
4631 Roo.get(c).scrollTo('top', options.value, true);
4644 * <span> icon </span>
4645 * <span> text </span>
4646 * <span>badge </span>
4650 * @class Roo.bootstrap.NavSidebarItem
4651 * @extends Roo.bootstrap.NavItem
4652 * Bootstrap Navbar.NavSidebarItem class
4653 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4654 * {Boolean} open is the menu open
4655 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4656 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4657 * {String} buttonSize (sm|md|lg)the extra classes for the button
4658 * {Boolean} showArrow show arrow next to the text (default true)
4660 * Create a new Navbar Button
4661 * @param {Object} config The config object
4663 Roo.bootstrap.NavSidebarItem = function(config){
4664 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4669 * The raw click event for the entire grid.
4670 * @param {Roo.EventObject} e
4675 * Fires when the active item active state changes
4676 * @param {Roo.bootstrap.NavSidebarItem} this
4677 * @param {boolean} state the new state
4685 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4687 badgeWeight : 'default',
4693 buttonWeight : 'default',
4699 getAutoCreate : function(){
4704 href : this.href || '#',
4710 if(this.buttonView){
4713 href : this.href || '#',
4714 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4727 cfg.cls += ' active';
4730 if (this.disabled) {
4731 cfg.cls += ' disabled';
4734 cfg.cls += ' open x-open';
4737 if (this.glyphicon || this.icon) {
4738 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4739 a.cn.push({ tag : 'i', cls : c }) ;
4742 if(!this.buttonView){
4745 html : this.html || ''
4752 if (this.badge !== '') {
4753 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4759 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4762 a.cls += ' dropdown-toggle treeview' ;
4768 initEvents : function()
4770 if (typeof (this.menu) != 'undefined') {
4771 this.menu.parentType = this.xtype;
4772 this.menu.triggerEl = this.el;
4773 this.menu = this.addxtype(Roo.apply({}, this.menu));
4776 this.el.on('click', this.onClick, this);
4778 if(this.badge !== ''){
4779 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4784 onClick : function(e)
4791 if(this.preventDefault){
4795 this.fireEvent('click', this);
4798 disable : function()
4800 this.setDisabled(true);
4805 this.setDisabled(false);
4808 setDisabled : function(state)
4810 if(this.disabled == state){
4814 this.disabled = state;
4817 this.el.addClass('disabled');
4821 this.el.removeClass('disabled');
4826 setActive : function(state)
4828 if(this.active == state){
4832 this.active = state;
4835 this.el.addClass('active');
4839 this.el.removeClass('active');
4844 isActive: function ()
4849 setBadge : function(str)
4855 this.badgeEl.dom.innerHTML = str;
4872 * @class Roo.bootstrap.Row
4873 * @extends Roo.bootstrap.Component
4874 * Bootstrap Row class (contains columns...)
4878 * @param {Object} config The config object
4881 Roo.bootstrap.Row = function(config){
4882 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4885 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4887 getAutoCreate : function(){
4906 * @class Roo.bootstrap.Element
4907 * @extends Roo.bootstrap.Component
4908 * Bootstrap Element class
4909 * @cfg {String} html contents of the element
4910 * @cfg {String} tag tag of the element
4911 * @cfg {String} cls class of the element
4912 * @cfg {Boolean} preventDefault (true|false) default false
4913 * @cfg {Boolean} clickable (true|false) default false
4916 * Create a new Element
4917 * @param {Object} config The config object
4920 Roo.bootstrap.Element = function(config){
4921 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4927 * When a element is chick
4928 * @param {Roo.bootstrap.Element} this
4929 * @param {Roo.EventObject} e
4935 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4940 preventDefault: false,
4943 getAutoCreate : function(){
4947 // cls: this.cls, double assign in parent class Component.js :: onRender
4954 initEvents: function()
4956 Roo.bootstrap.Element.superclass.initEvents.call(this);
4959 this.el.on('click', this.onClick, this);
4964 onClick : function(e)
4966 if(this.preventDefault){
4970 this.fireEvent('click', this, e);
4973 getValue : function()
4975 return this.el.dom.innerHTML;
4978 setValue : function(value)
4980 this.el.dom.innerHTML = value;
4995 * @class Roo.bootstrap.Pagination
4996 * @extends Roo.bootstrap.Component
4997 * Bootstrap Pagination class
4998 * @cfg {String} size xs | sm | md | lg
4999 * @cfg {Boolean} inverse false | true
5002 * Create a new Pagination
5003 * @param {Object} config The config object
5006 Roo.bootstrap.Pagination = function(config){
5007 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5010 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5016 getAutoCreate : function(){
5022 cfg.cls += ' inverse';
5028 cfg.cls += " " + this.cls;
5046 * @class Roo.bootstrap.PaginationItem
5047 * @extends Roo.bootstrap.Component
5048 * Bootstrap PaginationItem class
5049 * @cfg {String} html text
5050 * @cfg {String} href the link
5051 * @cfg {Boolean} preventDefault (true | false) default true
5052 * @cfg {Boolean} active (true | false) default false
5053 * @cfg {Boolean} disabled default false
5057 * Create a new PaginationItem
5058 * @param {Object} config The config object
5062 Roo.bootstrap.PaginationItem = function(config){
5063 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5068 * The raw click event for the entire grid.
5069 * @param {Roo.EventObject} e
5075 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5079 preventDefault: true,
5084 getAutoCreate : function(){
5090 href : this.href ? this.href : '#',
5091 html : this.html ? this.html : ''
5101 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5105 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5111 initEvents: function() {
5113 this.el.on('click', this.onClick, this);
5116 onClick : function(e)
5118 Roo.log('PaginationItem on click ');
5119 if(this.preventDefault){
5127 this.fireEvent('click', this, e);
5143 * @class Roo.bootstrap.Slider
5144 * @extends Roo.bootstrap.Component
5145 * Bootstrap Slider class
5148 * Create a new Slider
5149 * @param {Object} config The config object
5152 Roo.bootstrap.Slider = function(config){
5153 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5156 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5158 getAutoCreate : function(){
5162 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5166 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5178 * Ext JS Library 1.1.1
5179 * Copyright(c) 2006-2007, Ext JS, LLC.
5181 * Originally Released Under LGPL - original licence link has changed is not relivant.
5184 * <script type="text/javascript">
5189 * @class Roo.grid.ColumnModel
5190 * @extends Roo.util.Observable
5191 * This is the default implementation of a ColumnModel used by the Grid. It defines
5192 * the columns in the grid.
5195 var colModel = new Roo.grid.ColumnModel([
5196 {header: "Ticker", width: 60, sortable: true, locked: true},
5197 {header: "Company Name", width: 150, sortable: true},
5198 {header: "Market Cap.", width: 100, sortable: true},
5199 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5200 {header: "Employees", width: 100, sortable: true, resizable: false}
5205 * The config options listed for this class are options which may appear in each
5206 * individual column definition.
5207 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5209 * @param {Object} config An Array of column config objects. See this class's
5210 * config objects for details.
5212 Roo.grid.ColumnModel = function(config){
5214 * The config passed into the constructor
5216 this.config = config;
5219 // if no id, create one
5220 // if the column does not have a dataIndex mapping,
5221 // map it to the order it is in the config
5222 for(var i = 0, len = config.length; i < len; i++){
5224 if(typeof c.dataIndex == "undefined"){
5227 if(typeof c.renderer == "string"){
5228 c.renderer = Roo.util.Format[c.renderer];
5230 if(typeof c.id == "undefined"){
5233 if(c.editor && c.editor.xtype){
5234 c.editor = Roo.factory(c.editor, Roo.grid);
5236 if(c.editor && c.editor.isFormField){
5237 c.editor = new Roo.grid.GridEditor(c.editor);
5239 this.lookup[c.id] = c;
5243 * The width of columns which have no width specified (defaults to 100)
5246 this.defaultWidth = 100;
5249 * Default sortable of columns which have no sortable specified (defaults to false)
5252 this.defaultSortable = false;
5256 * @event widthchange
5257 * Fires when the width of a column changes.
5258 * @param {ColumnModel} this
5259 * @param {Number} columnIndex The column index
5260 * @param {Number} newWidth The new width
5262 "widthchange": true,
5264 * @event headerchange
5265 * Fires when the text of a header changes.
5266 * @param {ColumnModel} this
5267 * @param {Number} columnIndex The column index
5268 * @param {Number} newText The new header text
5270 "headerchange": true,
5272 * @event hiddenchange
5273 * Fires when a column is hidden or "unhidden".
5274 * @param {ColumnModel} this
5275 * @param {Number} columnIndex The column index
5276 * @param {Boolean} hidden true if hidden, false otherwise
5278 "hiddenchange": true,
5280 * @event columnmoved
5281 * Fires when a column is moved.
5282 * @param {ColumnModel} this
5283 * @param {Number} oldIndex
5284 * @param {Number} newIndex
5286 "columnmoved" : true,
5288 * @event columlockchange
5289 * Fires when a column's locked state is changed
5290 * @param {ColumnModel} this
5291 * @param {Number} colIndex
5292 * @param {Boolean} locked true if locked
5294 "columnlockchange" : true
5296 Roo.grid.ColumnModel.superclass.constructor.call(this);
5298 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5300 * @cfg {String} header The header text to display in the Grid view.
5303 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5304 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5305 * specified, the column's index is used as an index into the Record's data Array.
5308 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5309 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5312 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5313 * Defaults to the value of the {@link #defaultSortable} property.
5314 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5317 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5320 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5323 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5326 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5329 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5330 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5331 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5332 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5335 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5338 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5341 * @cfg {String} cursor (Optional)
5344 * @cfg {String} tooltip (Optional)
5347 * @cfg {Number} xs (Optional)
5350 * @cfg {Number} sm (Optional)
5353 * @cfg {Number} md (Optional)
5356 * @cfg {Number} lg (Optional)
5359 * Returns the id of the column at the specified index.
5360 * @param {Number} index The column index
5361 * @return {String} the id
5363 getColumnId : function(index){
5364 return this.config[index].id;
5368 * Returns the column for a specified id.
5369 * @param {String} id The column id
5370 * @return {Object} the column
5372 getColumnById : function(id){
5373 return this.lookup[id];
5378 * Returns the column for a specified dataIndex.
5379 * @param {String} dataIndex The column dataIndex
5380 * @return {Object|Boolean} the column or false if not found
5382 getColumnByDataIndex: function(dataIndex){
5383 var index = this.findColumnIndex(dataIndex);
5384 return index > -1 ? this.config[index] : false;
5388 * Returns the index for a specified column id.
5389 * @param {String} id The column id
5390 * @return {Number} the index, or -1 if not found
5392 getIndexById : function(id){
5393 for(var i = 0, len = this.config.length; i < len; i++){
5394 if(this.config[i].id == id){
5402 * Returns the index for a specified column dataIndex.
5403 * @param {String} dataIndex The column dataIndex
5404 * @return {Number} the index, or -1 if not found
5407 findColumnIndex : function(dataIndex){
5408 for(var i = 0, len = this.config.length; i < len; i++){
5409 if(this.config[i].dataIndex == dataIndex){
5417 moveColumn : function(oldIndex, newIndex){
5418 var c = this.config[oldIndex];
5419 this.config.splice(oldIndex, 1);
5420 this.config.splice(newIndex, 0, c);
5421 this.dataMap = null;
5422 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5425 isLocked : function(colIndex){
5426 return this.config[colIndex].locked === true;
5429 setLocked : function(colIndex, value, suppressEvent){
5430 if(this.isLocked(colIndex) == value){
5433 this.config[colIndex].locked = value;
5435 this.fireEvent("columnlockchange", this, colIndex, value);
5439 getTotalLockedWidth : function(){
5441 for(var i = 0; i < this.config.length; i++){
5442 if(this.isLocked(i) && !this.isHidden(i)){
5443 this.totalWidth += this.getColumnWidth(i);
5449 getLockedCount : function(){
5450 for(var i = 0, len = this.config.length; i < len; i++){
5451 if(!this.isLocked(i)){
5456 return this.config.length;
5460 * Returns the number of columns.
5463 getColumnCount : function(visibleOnly){
5464 if(visibleOnly === true){
5466 for(var i = 0, len = this.config.length; i < len; i++){
5467 if(!this.isHidden(i)){
5473 return this.config.length;
5477 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5478 * @param {Function} fn
5479 * @param {Object} scope (optional)
5480 * @return {Array} result
5482 getColumnsBy : function(fn, scope){
5484 for(var i = 0, len = this.config.length; i < len; i++){
5485 var c = this.config[i];
5486 if(fn.call(scope||this, c, i) === true){
5494 * Returns true if the specified column is sortable.
5495 * @param {Number} col The column index
5498 isSortable : function(col){
5499 if(typeof this.config[col].sortable == "undefined"){
5500 return this.defaultSortable;
5502 return this.config[col].sortable;
5506 * Returns the rendering (formatting) function defined for the column.
5507 * @param {Number} col The column index.
5508 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5510 getRenderer : function(col){
5511 if(!this.config[col].renderer){
5512 return Roo.grid.ColumnModel.defaultRenderer;
5514 return this.config[col].renderer;
5518 * Sets the rendering (formatting) function for a column.
5519 * @param {Number} col The column index
5520 * @param {Function} fn The function to use to process the cell's raw data
5521 * to return HTML markup for the grid view. The render function is called with
5522 * the following parameters:<ul>
5523 * <li>Data value.</li>
5524 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5525 * <li>css A CSS style string to apply to the table cell.</li>
5526 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5527 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5528 * <li>Row index</li>
5529 * <li>Column index</li>
5530 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5532 setRenderer : function(col, fn){
5533 this.config[col].renderer = fn;
5537 * Returns the width for the specified column.
5538 * @param {Number} col The column index
5541 getColumnWidth : function(col){
5542 return this.config[col].width * 1 || this.defaultWidth;
5546 * Sets the width for a column.
5547 * @param {Number} col The column index
5548 * @param {Number} width The new width
5550 setColumnWidth : function(col, width, suppressEvent){
5551 this.config[col].width = width;
5552 this.totalWidth = null;
5554 this.fireEvent("widthchange", this, col, width);
5559 * Returns the total width of all columns.
5560 * @param {Boolean} includeHidden True to include hidden column widths
5563 getTotalWidth : function(includeHidden){
5564 if(!this.totalWidth){
5565 this.totalWidth = 0;
5566 for(var i = 0, len = this.config.length; i < len; i++){
5567 if(includeHidden || !this.isHidden(i)){
5568 this.totalWidth += this.getColumnWidth(i);
5572 return this.totalWidth;
5576 * Returns the header for the specified column.
5577 * @param {Number} col The column index
5580 getColumnHeader : function(col){
5581 return this.config[col].header;
5585 * Sets the header for a column.
5586 * @param {Number} col The column index
5587 * @param {String} header The new header
5589 setColumnHeader : function(col, header){
5590 this.config[col].header = header;
5591 this.fireEvent("headerchange", this, col, header);
5595 * Returns the tooltip for the specified column.
5596 * @param {Number} col The column index
5599 getColumnTooltip : function(col){
5600 return this.config[col].tooltip;
5603 * Sets the tooltip for a column.
5604 * @param {Number} col The column index
5605 * @param {String} tooltip The new tooltip
5607 setColumnTooltip : function(col, tooltip){
5608 this.config[col].tooltip = tooltip;
5612 * Returns the dataIndex for the specified column.
5613 * @param {Number} col The column index
5616 getDataIndex : function(col){
5617 return this.config[col].dataIndex;
5621 * Sets the dataIndex for a column.
5622 * @param {Number} col The column index
5623 * @param {Number} dataIndex The new dataIndex
5625 setDataIndex : function(col, dataIndex){
5626 this.config[col].dataIndex = dataIndex;
5632 * Returns true if the cell is editable.
5633 * @param {Number} colIndex The column index
5634 * @param {Number} rowIndex The row index - this is nto actually used..?
5637 isCellEditable : function(colIndex, rowIndex){
5638 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5642 * Returns the editor defined for the cell/column.
5643 * return false or null to disable editing.
5644 * @param {Number} colIndex The column index
5645 * @param {Number} rowIndex The row index
5648 getCellEditor : function(colIndex, rowIndex){
5649 return this.config[colIndex].editor;
5653 * Sets if a column is editable.
5654 * @param {Number} col The column index
5655 * @param {Boolean} editable True if the column is editable
5657 setEditable : function(col, editable){
5658 this.config[col].editable = editable;
5663 * Returns true if the column is hidden.
5664 * @param {Number} colIndex The column index
5667 isHidden : function(colIndex){
5668 return this.config[colIndex].hidden;
5673 * Returns true if the column width cannot be changed
5675 isFixed : function(colIndex){
5676 return this.config[colIndex].fixed;
5680 * Returns true if the column can be resized
5683 isResizable : function(colIndex){
5684 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5687 * Sets if a column is hidden.
5688 * @param {Number} colIndex The column index
5689 * @param {Boolean} hidden True if the column is hidden
5691 setHidden : function(colIndex, hidden){
5692 this.config[colIndex].hidden = hidden;
5693 this.totalWidth = null;
5694 this.fireEvent("hiddenchange", this, colIndex, hidden);
5698 * Sets the editor for a column.
5699 * @param {Number} col The column index
5700 * @param {Object} editor The editor object
5702 setEditor : function(col, editor){
5703 this.config[col].editor = editor;
5707 Roo.grid.ColumnModel.defaultRenderer = function(value)
5709 if(typeof value == "object") {
5712 if(typeof value == "string" && value.length < 1){
5716 return String.format("{0}", value);
5719 // Alias for backwards compatibility
5720 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5723 * Ext JS Library 1.1.1
5724 * Copyright(c) 2006-2007, Ext JS, LLC.
5726 * Originally Released Under LGPL - original licence link has changed is not relivant.
5729 * <script type="text/javascript">
5733 * @class Roo.LoadMask
5734 * A simple utility class for generically masking elements while loading data. If the element being masked has
5735 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5736 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5737 * element's UpdateManager load indicator and will be destroyed after the initial load.
5739 * Create a new LoadMask
5740 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5741 * @param {Object} config The config object
5743 Roo.LoadMask = function(el, config){
5744 this.el = Roo.get(el);
5745 Roo.apply(this, config);
5747 this.store.on('beforeload', this.onBeforeLoad, this);
5748 this.store.on('load', this.onLoad, this);
5749 this.store.on('loadexception', this.onLoadException, this);
5750 this.removeMask = false;
5752 var um = this.el.getUpdateManager();
5753 um.showLoadIndicator = false; // disable the default indicator
5754 um.on('beforeupdate', this.onBeforeLoad, this);
5755 um.on('update', this.onLoad, this);
5756 um.on('failure', this.onLoad, this);
5757 this.removeMask = true;
5761 Roo.LoadMask.prototype = {
5763 * @cfg {Boolean} removeMask
5764 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5765 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5769 * The text to display in a centered loading message box (defaults to 'Loading...')
5773 * @cfg {String} msgCls
5774 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5776 msgCls : 'x-mask-loading',
5779 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5785 * Disables the mask to prevent it from being displayed
5787 disable : function(){
5788 this.disabled = true;
5792 * Enables the mask so that it can be displayed
5794 enable : function(){
5795 this.disabled = false;
5798 onLoadException : function()
5802 if (typeof(arguments[3]) != 'undefined') {
5803 Roo.MessageBox.alert("Error loading",arguments[3]);
5807 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5808 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5815 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5820 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5824 onBeforeLoad : function(){
5826 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5831 destroy : function(){
5833 this.store.un('beforeload', this.onBeforeLoad, this);
5834 this.store.un('load', this.onLoad, this);
5835 this.store.un('loadexception', this.onLoadException, this);
5837 var um = this.el.getUpdateManager();
5838 um.un('beforeupdate', this.onBeforeLoad, this);
5839 um.un('update', this.onLoad, this);
5840 um.un('failure', this.onLoad, this);
5851 * @class Roo.bootstrap.Table
5852 * @extends Roo.bootstrap.Component
5853 * Bootstrap Table class
5854 * @cfg {String} cls table class
5855 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5856 * @cfg {String} bgcolor Specifies the background color for a table
5857 * @cfg {Number} border Specifies whether the table cells should have borders or not
5858 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5859 * @cfg {Number} cellspacing Specifies the space between cells
5860 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5861 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5862 * @cfg {String} sortable Specifies that the table should be sortable
5863 * @cfg {String} summary Specifies a summary of the content of a table
5864 * @cfg {Number} width Specifies the width of a table
5865 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5867 * @cfg {boolean} striped Should the rows be alternative striped
5868 * @cfg {boolean} bordered Add borders to the table
5869 * @cfg {boolean} hover Add hover highlighting
5870 * @cfg {boolean} condensed Format condensed
5871 * @cfg {boolean} responsive Format condensed
5872 * @cfg {Boolean} loadMask (true|false) default false
5873 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5874 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5875 * @cfg {Boolean} rowSelection (true|false) default false
5876 * @cfg {Boolean} cellSelection (true|false) default false
5877 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5878 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5879 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5883 * Create a new Table
5884 * @param {Object} config The config object
5887 Roo.bootstrap.Table = function(config){
5888 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5893 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5894 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5895 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5896 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5898 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5900 this.sm.grid = this;
5901 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5902 this.sm = this.selModel;
5903 this.sm.xmodule = this.xmodule || false;
5906 if (this.cm && typeof(this.cm.config) == 'undefined') {
5907 this.colModel = new Roo.grid.ColumnModel(this.cm);
5908 this.cm = this.colModel;
5909 this.cm.xmodule = this.xmodule || false;
5912 this.store= Roo.factory(this.store, Roo.data);
5913 this.ds = this.store;
5914 this.ds.xmodule = this.xmodule || false;
5917 if (this.footer && this.store) {
5918 this.footer.dataSource = this.ds;
5919 this.footer = Roo.factory(this.footer);
5926 * Fires when a cell is clicked
5927 * @param {Roo.bootstrap.Table} this
5928 * @param {Roo.Element} el
5929 * @param {Number} rowIndex
5930 * @param {Number} columnIndex
5931 * @param {Roo.EventObject} e
5935 * @event celldblclick
5936 * Fires when a cell is double clicked
5937 * @param {Roo.bootstrap.Table} this
5938 * @param {Roo.Element} el
5939 * @param {Number} rowIndex
5940 * @param {Number} columnIndex
5941 * @param {Roo.EventObject} e
5943 "celldblclick" : true,
5946 * Fires when a row is clicked
5947 * @param {Roo.bootstrap.Table} this
5948 * @param {Roo.Element} el
5949 * @param {Number} rowIndex
5950 * @param {Roo.EventObject} e
5954 * @event rowdblclick
5955 * Fires when a row is double clicked
5956 * @param {Roo.bootstrap.Table} this
5957 * @param {Roo.Element} el
5958 * @param {Number} rowIndex
5959 * @param {Roo.EventObject} e
5961 "rowdblclick" : true,
5964 * Fires when a mouseover occur
5965 * @param {Roo.bootstrap.Table} this
5966 * @param {Roo.Element} el
5967 * @param {Number} rowIndex
5968 * @param {Number} columnIndex
5969 * @param {Roo.EventObject} e
5974 * Fires when a mouseout occur
5975 * @param {Roo.bootstrap.Table} this
5976 * @param {Roo.Element} el
5977 * @param {Number} rowIndex
5978 * @param {Number} columnIndex
5979 * @param {Roo.EventObject} e
5984 * Fires when a row is rendered, so you can change add a style to it.
5985 * @param {Roo.bootstrap.Table} this
5986 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5990 * @event rowsrendered
5991 * Fires when all the rows have been rendered
5992 * @param {Roo.bootstrap.Table} this
5994 'rowsrendered' : true,
5996 * @event contextmenu
5997 * The raw contextmenu event for the entire grid.
5998 * @param {Roo.EventObject} e
6000 "contextmenu" : true,
6002 * @event rowcontextmenu
6003 * Fires when a row is right clicked
6004 * @param {Roo.bootstrap.Table} this
6005 * @param {Number} rowIndex
6006 * @param {Roo.EventObject} e
6008 "rowcontextmenu" : true,
6010 * @event cellcontextmenu
6011 * Fires when a cell is right clicked
6012 * @param {Roo.bootstrap.Table} this
6013 * @param {Number} rowIndex
6014 * @param {Number} cellIndex
6015 * @param {Roo.EventObject} e
6017 "cellcontextmenu" : true,
6019 * @event headercontextmenu
6020 * Fires when a header is right clicked
6021 * @param {Roo.bootstrap.Table} this
6022 * @param {Number} columnIndex
6023 * @param {Roo.EventObject} e
6025 "headercontextmenu" : true
6029 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6055 rowSelection : false,
6056 cellSelection : false,
6059 // Roo.Element - the tbody
6061 // Roo.Element - thead element
6064 container: false, // used by gridpanel...
6070 getAutoCreate : function()
6072 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6079 if (this.scrollBody) {
6080 cfg.cls += ' table-body-fixed';
6083 cfg.cls += ' table-striped';
6087 cfg.cls += ' table-hover';
6089 if (this.bordered) {
6090 cfg.cls += ' table-bordered';
6092 if (this.condensed) {
6093 cfg.cls += ' table-condensed';
6095 if (this.responsive) {
6096 cfg.cls += ' table-responsive';
6100 cfg.cls+= ' ' +this.cls;
6103 // this lot should be simplifed...
6106 cfg.align=this.align;
6109 cfg.bgcolor=this.bgcolor;
6112 cfg.border=this.border;
6114 if (this.cellpadding) {
6115 cfg.cellpadding=this.cellpadding;
6117 if (this.cellspacing) {
6118 cfg.cellspacing=this.cellspacing;
6121 cfg.frame=this.frame;
6124 cfg.rules=this.rules;
6126 if (this.sortable) {
6127 cfg.sortable=this.sortable;
6130 cfg.summary=this.summary;
6133 cfg.width=this.width;
6136 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6139 if(this.store || this.cm){
6140 if(this.headerShow){
6141 cfg.cn.push(this.renderHeader());
6144 cfg.cn.push(this.renderBody());
6146 if(this.footerShow){
6147 cfg.cn.push(this.renderFooter());
6149 // where does this come from?
6150 //cfg.cls+= ' TableGrid';
6153 return { cn : [ cfg ] };
6156 initEvents : function()
6158 if(!this.store || !this.cm){
6161 if (this.selModel) {
6162 this.selModel.initEvents();
6166 //Roo.log('initEvents with ds!!!!');
6168 this.mainBody = this.el.select('tbody', true).first();
6169 this.mainHead = this.el.select('thead', true).first();
6176 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6177 e.on('click', _this.sort, _this);
6180 this.mainBody.on("click", this.onClick, this);
6181 this.mainBody.on("dblclick", this.onDblClick, this);
6183 // why is this done????? = it breaks dialogs??
6184 //this.parent().el.setStyle('position', 'relative');
6188 this.footer.parentId = this.id;
6189 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6192 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' });
6198 this.store.on('load', this.onLoad, this);
6199 this.store.on('beforeload', this.onBeforeLoad, this);
6200 this.store.on('update', this.onUpdate, this);
6201 this.store.on('add', this.onAdd, this);
6202 this.store.on("clear", this.clear, this);
6204 this.el.on("contextmenu", this.onContextMenu, this);
6206 this.mainBody.on('scroll', this.onBodyScroll, this);
6208 this.cm.on("headerchange", this.onHeaderChange, this);
6210 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6214 onContextMenu : function(e, t)
6216 this.processEvent("contextmenu", e);
6219 processEvent : function(name, e)
6221 if (name != 'touchstart' ) {
6222 this.fireEvent(name, e);
6225 var t = e.getTarget();
6227 var cell = Roo.get(t);
6233 if(cell.findParent('tfoot', false, true)){
6237 if(cell.findParent('thead', false, true)){
6239 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6240 cell = Roo.get(t).findParent('th', false, true);
6242 Roo.log("failed to find th in thead?");
6243 Roo.log(e.getTarget());
6248 var cellIndex = cell.dom.cellIndex;
6250 var ename = name == 'touchstart' ? 'click' : name;
6251 this.fireEvent("header" + ename, this, cellIndex, e);
6256 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6257 cell = Roo.get(t).findParent('td', false, true);
6259 Roo.log("failed to find th in tbody?");
6260 Roo.log(e.getTarget());
6265 var row = cell.findParent('tr', false, true);
6266 var cellIndex = cell.dom.cellIndex;
6267 var rowIndex = row.dom.rowIndex - 1;
6271 this.fireEvent("row" + name, this, rowIndex, e);
6275 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6281 onMouseover : function(e, el)
6283 var cell = Roo.get(el);
6289 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6290 cell = cell.findParent('td', false, true);
6293 var row = cell.findParent('tr', false, true);
6294 var cellIndex = cell.dom.cellIndex;
6295 var rowIndex = row.dom.rowIndex - 1; // start from 0
6297 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6301 onMouseout : function(e, el)
6303 var cell = Roo.get(el);
6309 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6310 cell = cell.findParent('td', false, true);
6313 var row = cell.findParent('tr', false, true);
6314 var cellIndex = cell.dom.cellIndex;
6315 var rowIndex = row.dom.rowIndex - 1; // start from 0
6317 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6321 onClick : function(e, el)
6323 var cell = Roo.get(el);
6325 if(!cell || (!this.cellSelection && !this.rowSelection)){
6329 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6330 cell = cell.findParent('td', false, true);
6333 if(!cell || typeof(cell) == 'undefined'){
6337 var row = cell.findParent('tr', false, true);
6339 if(!row || typeof(row) == 'undefined'){
6343 var cellIndex = cell.dom.cellIndex;
6344 var rowIndex = this.getRowIndex(row);
6346 // why??? - should these not be based on SelectionModel?
6347 if(this.cellSelection){
6348 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6351 if(this.rowSelection){
6352 this.fireEvent('rowclick', this, row, rowIndex, e);
6358 onDblClick : function(e,el)
6360 var cell = Roo.get(el);
6362 if(!cell || (!this.cellSelection && !this.rowSelection)){
6366 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6367 cell = cell.findParent('td', false, true);
6370 if(!cell || typeof(cell) == 'undefined'){
6374 var row = cell.findParent('tr', false, true);
6376 if(!row || typeof(row) == 'undefined'){
6380 var cellIndex = cell.dom.cellIndex;
6381 var rowIndex = this.getRowIndex(row);
6383 if(this.cellSelection){
6384 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6387 if(this.rowSelection){
6388 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6392 sort : function(e,el)
6394 var col = Roo.get(el);
6396 if(!col.hasClass('sortable')){
6400 var sort = col.attr('sort');
6403 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6407 this.store.sortInfo = {field : sort, direction : dir};
6410 Roo.log("calling footer first");
6411 this.footer.onClick('first');
6414 this.store.load({ params : { start : 0 } });
6418 renderHeader : function()
6426 this.totalWidth = 0;
6428 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6430 var config = cm.config[i];
6434 cls : 'x-hcol-' + i,
6436 html: cm.getColumnHeader(i)
6441 if(typeof(config.sortable) != 'undefined' && config.sortable){
6443 c.html = '<i class="glyphicon"></i>' + c.html;
6446 if(typeof(config.lgHeader) != 'undefined'){
6447 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6450 if(typeof(config.mdHeader) != 'undefined'){
6451 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6454 if(typeof(config.smHeader) != 'undefined'){
6455 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6458 if(typeof(config.xsHeader) != 'undefined'){
6459 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6466 if(typeof(config.tooltip) != 'undefined'){
6467 c.tooltip = config.tooltip;
6470 if(typeof(config.colspan) != 'undefined'){
6471 c.colspan = config.colspan;
6474 if(typeof(config.hidden) != 'undefined' && config.hidden){
6475 c.style += ' display:none;';
6478 if(typeof(config.dataIndex) != 'undefined'){
6479 c.sort = config.dataIndex;
6484 if(typeof(config.align) != 'undefined' && config.align.length){
6485 c.style += ' text-align:' + config.align + ';';
6488 if(typeof(config.width) != 'undefined'){
6489 c.style += ' width:' + config.width + 'px;';
6490 this.totalWidth += config.width;
6492 this.totalWidth += 100; // assume minimum of 100 per column?
6495 if(typeof(config.cls) != 'undefined'){
6496 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6499 ['xs','sm','md','lg'].map(function(size){
6501 if(typeof(config[size]) == 'undefined'){
6505 if (!config[size]) { // 0 = hidden
6506 c.cls += ' hidden-' + size;
6510 c.cls += ' col-' + size + '-' + config[size];
6520 renderBody : function()
6530 colspan : this.cm.getColumnCount()
6540 renderFooter : function()
6550 colspan : this.cm.getColumnCount()
6564 // Roo.log('ds onload');
6569 var ds = this.store;
6571 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6572 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6573 if (_this.store.sortInfo) {
6575 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6576 e.select('i', true).addClass(['glyphicon-arrow-up']);
6579 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6580 e.select('i', true).addClass(['glyphicon-arrow-down']);
6585 var tbody = this.mainBody;
6587 if(ds.getCount() > 0){
6588 ds.data.each(function(d,rowIndex){
6589 var row = this.renderRow(cm, ds, rowIndex);
6591 tbody.createChild(row);
6595 if(row.cellObjects.length){
6596 Roo.each(row.cellObjects, function(r){
6597 _this.renderCellObject(r);
6604 Roo.each(this.el.select('tbody td', true).elements, function(e){
6605 e.on('mouseover', _this.onMouseover, _this);
6608 Roo.each(this.el.select('tbody td', true).elements, function(e){
6609 e.on('mouseout', _this.onMouseout, _this);
6611 this.fireEvent('rowsrendered', this);
6612 //if(this.loadMask){
6613 // this.maskEl.hide();
6620 onUpdate : function(ds,record)
6622 this.refreshRow(record);
6626 onRemove : function(ds, record, index, isUpdate){
6627 if(isUpdate !== true){
6628 this.fireEvent("beforerowremoved", this, index, record);
6630 var bt = this.mainBody.dom;
6632 var rows = this.el.select('tbody > tr', true).elements;
6634 if(typeof(rows[index]) != 'undefined'){
6635 bt.removeChild(rows[index].dom);
6638 // if(bt.rows[index]){
6639 // bt.removeChild(bt.rows[index]);
6642 if(isUpdate !== true){
6643 //this.stripeRows(index);
6644 //this.syncRowHeights(index, index);
6646 this.fireEvent("rowremoved", this, index, record);
6650 onAdd : function(ds, records, rowIndex)
6652 //Roo.log('on Add called');
6653 // - note this does not handle multiple adding very well..
6654 var bt = this.mainBody.dom;
6655 for (var i =0 ; i < records.length;i++) {
6656 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6657 //Roo.log(records[i]);
6658 //Roo.log(this.store.getAt(rowIndex+i));
6659 this.insertRow(this.store, rowIndex + i, false);
6666 refreshRow : function(record){
6667 var ds = this.store, index;
6668 if(typeof record == 'number'){
6670 record = ds.getAt(index);
6672 index = ds.indexOf(record);
6674 this.insertRow(ds, index, true);
6676 this.onRemove(ds, record, index+1, true);
6678 //this.syncRowHeights(index, index);
6680 this.fireEvent("rowupdated", this, index, record);
6683 insertRow : function(dm, rowIndex, isUpdate){
6686 this.fireEvent("beforerowsinserted", this, rowIndex);
6688 //var s = this.getScrollState();
6689 var row = this.renderRow(this.cm, this.store, rowIndex);
6690 // insert before rowIndex..
6691 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6695 if(row.cellObjects.length){
6696 Roo.each(row.cellObjects, function(r){
6697 _this.renderCellObject(r);
6702 this.fireEvent("rowsinserted", this, rowIndex);
6703 //this.syncRowHeights(firstRow, lastRow);
6704 //this.stripeRows(firstRow);
6711 getRowDom : function(rowIndex)
6713 var rows = this.el.select('tbody > tr', true).elements;
6715 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6718 // returns the object tree for a tr..
6721 renderRow : function(cm, ds, rowIndex)
6723 var d = ds.getAt(rowIndex);
6727 cls : 'x-row-' + rowIndex,
6731 var cellObjects = [];
6733 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6734 var config = cm.config[i];
6736 var renderer = cm.getRenderer(i);
6740 if(typeof(renderer) !== 'undefined'){
6741 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6743 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6744 // and are rendered into the cells after the row is rendered - using the id for the element.
6746 if(typeof(value) === 'object'){
6756 rowIndex : rowIndex,
6761 this.fireEvent('rowclass', this, rowcfg);
6765 cls : rowcfg.rowClass + ' x-col-' + i,
6767 html: (typeof(value) === 'object') ? '' : value
6774 if(typeof(config.colspan) != 'undefined'){
6775 td.colspan = config.colspan;
6778 if(typeof(config.hidden) != 'undefined' && config.hidden){
6779 td.style += ' display:none;';
6782 if(typeof(config.align) != 'undefined' && config.align.length){
6783 td.style += ' text-align:' + config.align + ';';
6786 if(typeof(config.width) != 'undefined'){
6787 td.style += ' width:' + config.width + 'px;';
6790 if(typeof(config.cursor) != 'undefined'){
6791 td.style += ' cursor:' + config.cursor + ';';
6794 if(typeof(config.cls) != 'undefined'){
6795 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6798 ['xs','sm','md','lg'].map(function(size){
6800 if(typeof(config[size]) == 'undefined'){
6804 if (!config[size]) { // 0 = hidden
6805 td.cls += ' hidden-' + size;
6809 td.cls += ' col-' + size + '-' + config[size];
6817 row.cellObjects = cellObjects;
6825 onBeforeLoad : function()
6827 //Roo.log('ds onBeforeLoad');
6831 //if(this.loadMask){
6832 // this.maskEl.show();
6840 this.el.select('tbody', true).first().dom.innerHTML = '';
6843 * Show or hide a row.
6844 * @param {Number} rowIndex to show or hide
6845 * @param {Boolean} state hide
6847 setRowVisibility : function(rowIndex, state)
6849 var bt = this.mainBody.dom;
6851 var rows = this.el.select('tbody > tr', true).elements;
6853 if(typeof(rows[rowIndex]) == 'undefined'){
6856 rows[rowIndex].dom.style.display = state ? '' : 'none';
6860 getSelectionModel : function(){
6862 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6864 return this.selModel;
6867 * Render the Roo.bootstrap object from renderder
6869 renderCellObject : function(r)
6873 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6875 var t = r.cfg.render(r.container);
6878 Roo.each(r.cfg.cn, function(c){
6880 container: t.getChildContainer(),
6883 _this.renderCellObject(child);
6888 getRowIndex : function(row)
6892 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6903 * Returns the grid's underlying element = used by panel.Grid
6904 * @return {Element} The element
6906 getGridEl : function(){
6910 * Forces a resize - used by panel.Grid
6911 * @return {Element} The element
6913 autoSize : function()
6915 //var ctr = Roo.get(this.container.dom.parentElement);
6916 var ctr = Roo.get(this.el.dom);
6918 var thd = this.getGridEl().select('thead',true).first();
6919 var tbd = this.getGridEl().select('tbody', true).first();
6920 var tfd = this.getGridEl().select('tfoot', true).first();
6922 var cw = ctr.getWidth();
6926 tbd.setSize(ctr.getWidth(),
6927 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6929 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6932 cw = Math.max(cw, this.totalWidth);
6933 this.getGridEl().select('tr',true).setWidth(cw);
6934 // resize 'expandable coloumn?
6936 return; // we doe not have a view in this design..
6939 onBodyScroll: function()
6941 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6943 this.mainHead.setStyle({
6944 'position' : 'relative',
6945 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6951 var scrollHeight = this.mainBody.dom.scrollHeight;
6953 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6955 var height = this.mainBody.getHeight();
6957 if(scrollHeight - height == scrollTop) {
6959 var total = this.ds.getTotalCount();
6961 if(this.footer.cursor + this.footer.pageSize < total){
6963 this.footer.ds.load({
6965 start : this.footer.cursor + this.footer.pageSize,
6966 limit : this.footer.pageSize
6976 onHeaderChange : function()
6978 var header = this.renderHeader();
6979 var table = this.el.select('table', true).first();
6981 this.mainHead.remove();
6982 this.mainHead = table.createChild(header, this.mainBody, false);
6985 onHiddenChange : function(colModel, colIndex, hidden)
6987 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6988 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6990 this.CSS.updateRule(thSelector, "display", "");
6991 this.CSS.updateRule(tdSelector, "display", "");
6994 this.CSS.updateRule(thSelector, "display", "none");
6995 this.CSS.updateRule(tdSelector, "display", "none");
6998 this.onHeaderChange();
7015 * @class Roo.bootstrap.TableCell
7016 * @extends Roo.bootstrap.Component
7017 * Bootstrap TableCell class
7018 * @cfg {String} html cell contain text
7019 * @cfg {String} cls cell class
7020 * @cfg {String} tag cell tag (td|th) default td
7021 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7022 * @cfg {String} align Aligns the content in a cell
7023 * @cfg {String} axis Categorizes cells
7024 * @cfg {String} bgcolor Specifies the background color of a cell
7025 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7026 * @cfg {Number} colspan Specifies the number of columns a cell should span
7027 * @cfg {String} headers Specifies one or more header cells a cell is related to
7028 * @cfg {Number} height Sets the height of a cell
7029 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7030 * @cfg {Number} rowspan Sets the number of rows a cell should span
7031 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7032 * @cfg {String} valign Vertical aligns the content in a cell
7033 * @cfg {Number} width Specifies the width of a cell
7036 * Create a new TableCell
7037 * @param {Object} config The config object
7040 Roo.bootstrap.TableCell = function(config){
7041 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7044 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7064 getAutoCreate : function(){
7065 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7085 cfg.align=this.align
7091 cfg.bgcolor=this.bgcolor
7094 cfg.charoff=this.charoff
7097 cfg.colspan=this.colspan
7100 cfg.headers=this.headers
7103 cfg.height=this.height
7106 cfg.nowrap=this.nowrap
7109 cfg.rowspan=this.rowspan
7112 cfg.scope=this.scope
7115 cfg.valign=this.valign
7118 cfg.width=this.width
7137 * @class Roo.bootstrap.TableRow
7138 * @extends Roo.bootstrap.Component
7139 * Bootstrap TableRow class
7140 * @cfg {String} cls row class
7141 * @cfg {String} align Aligns the content in a table row
7142 * @cfg {String} bgcolor Specifies a background color for a table row
7143 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7144 * @cfg {String} valign Vertical aligns the content in a table row
7147 * Create a new TableRow
7148 * @param {Object} config The config object
7151 Roo.bootstrap.TableRow = function(config){
7152 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7155 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7163 getAutoCreate : function(){
7164 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7174 cfg.align = this.align;
7177 cfg.bgcolor = this.bgcolor;
7180 cfg.charoff = this.charoff;
7183 cfg.valign = this.valign;
7201 * @class Roo.bootstrap.TableBody
7202 * @extends Roo.bootstrap.Component
7203 * Bootstrap TableBody class
7204 * @cfg {String} cls element class
7205 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7206 * @cfg {String} align Aligns the content inside the element
7207 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7208 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7211 * Create a new TableBody
7212 * @param {Object} config The config object
7215 Roo.bootstrap.TableBody = function(config){
7216 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7219 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7227 getAutoCreate : function(){
7228 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7242 cfg.align = this.align;
7245 cfg.charoff = this.charoff;
7248 cfg.valign = this.valign;
7255 // initEvents : function()
7262 // this.store = Roo.factory(this.store, Roo.data);
7263 // this.store.on('load', this.onLoad, this);
7265 // this.store.load();
7269 // onLoad: function ()
7271 // this.fireEvent('load', this);
7281 * Ext JS Library 1.1.1
7282 * Copyright(c) 2006-2007, Ext JS, LLC.
7284 * Originally Released Under LGPL - original licence link has changed is not relivant.
7287 * <script type="text/javascript">
7290 // as we use this in bootstrap.
7291 Roo.namespace('Roo.form');
7293 * @class Roo.form.Action
7294 * Internal Class used to handle form actions
7296 * @param {Roo.form.BasicForm} el The form element or its id
7297 * @param {Object} config Configuration options
7302 // define the action interface
7303 Roo.form.Action = function(form, options){
7305 this.options = options || {};
7308 * Client Validation Failed
7311 Roo.form.Action.CLIENT_INVALID = 'client';
7313 * Server Validation Failed
7316 Roo.form.Action.SERVER_INVALID = 'server';
7318 * Connect to Server Failed
7321 Roo.form.Action.CONNECT_FAILURE = 'connect';
7323 * Reading Data from Server Failed
7326 Roo.form.Action.LOAD_FAILURE = 'load';
7328 Roo.form.Action.prototype = {
7330 failureType : undefined,
7331 response : undefined,
7335 run : function(options){
7340 success : function(response){
7345 handleResponse : function(response){
7349 // default connection failure
7350 failure : function(response){
7352 this.response = response;
7353 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7354 this.form.afterAction(this, false);
7357 processResponse : function(response){
7358 this.response = response;
7359 if(!response.responseText){
7362 this.result = this.handleResponse(response);
7366 // utility functions used internally
7367 getUrl : function(appendParams){
7368 var url = this.options.url || this.form.url || this.form.el.dom.action;
7370 var p = this.getParams();
7372 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7378 getMethod : function(){
7379 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7382 getParams : function(){
7383 var bp = this.form.baseParams;
7384 var p = this.options.params;
7386 if(typeof p == "object"){
7387 p = Roo.urlEncode(Roo.applyIf(p, bp));
7388 }else if(typeof p == 'string' && bp){
7389 p += '&' + Roo.urlEncode(bp);
7392 p = Roo.urlEncode(bp);
7397 createCallback : function(){
7399 success: this.success,
7400 failure: this.failure,
7402 timeout: (this.form.timeout*1000),
7403 upload: this.form.fileUpload ? this.success : undefined
7408 Roo.form.Action.Submit = function(form, options){
7409 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7412 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7415 haveProgress : false,
7416 uploadComplete : false,
7418 // uploadProgress indicator.
7419 uploadProgress : function()
7421 if (!this.form.progressUrl) {
7425 if (!this.haveProgress) {
7426 Roo.MessageBox.progress("Uploading", "Uploading");
7428 if (this.uploadComplete) {
7429 Roo.MessageBox.hide();
7433 this.haveProgress = true;
7435 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7437 var c = new Roo.data.Connection();
7439 url : this.form.progressUrl,
7444 success : function(req){
7445 //console.log(data);
7449 rdata = Roo.decode(req.responseText)
7451 Roo.log("Invalid data from server..");
7455 if (!rdata || !rdata.success) {
7457 Roo.MessageBox.alert(Roo.encode(rdata));
7460 var data = rdata.data;
7462 if (this.uploadComplete) {
7463 Roo.MessageBox.hide();
7468 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7469 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7472 this.uploadProgress.defer(2000,this);
7475 failure: function(data) {
7476 Roo.log('progress url failed ');
7487 // run get Values on the form, so it syncs any secondary forms.
7488 this.form.getValues();
7490 var o = this.options;
7491 var method = this.getMethod();
7492 var isPost = method == 'POST';
7493 if(o.clientValidation === false || this.form.isValid()){
7495 if (this.form.progressUrl) {
7496 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7497 (new Date() * 1) + '' + Math.random());
7502 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7503 form:this.form.el.dom,
7504 url:this.getUrl(!isPost),
7506 params:isPost ? this.getParams() : null,
7507 isUpload: this.form.fileUpload
7510 this.uploadProgress();
7512 }else if (o.clientValidation !== false){ // client validation failed
7513 this.failureType = Roo.form.Action.CLIENT_INVALID;
7514 this.form.afterAction(this, false);
7518 success : function(response)
7520 this.uploadComplete= true;
7521 if (this.haveProgress) {
7522 Roo.MessageBox.hide();
7526 var result = this.processResponse(response);
7527 if(result === true || result.success){
7528 this.form.afterAction(this, true);
7532 this.form.markInvalid(result.errors);
7533 this.failureType = Roo.form.Action.SERVER_INVALID;
7535 this.form.afterAction(this, false);
7537 failure : function(response)
7539 this.uploadComplete= true;
7540 if (this.haveProgress) {
7541 Roo.MessageBox.hide();
7544 this.response = response;
7545 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7546 this.form.afterAction(this, false);
7549 handleResponse : function(response){
7550 if(this.form.errorReader){
7551 var rs = this.form.errorReader.read(response);
7554 for(var i = 0, len = rs.records.length; i < len; i++) {
7555 var r = rs.records[i];
7559 if(errors.length < 1){
7563 success : rs.success,
7569 ret = Roo.decode(response.responseText);
7573 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7583 Roo.form.Action.Load = function(form, options){
7584 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7585 this.reader = this.form.reader;
7588 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7593 Roo.Ajax.request(Roo.apply(
7594 this.createCallback(), {
7595 method:this.getMethod(),
7596 url:this.getUrl(false),
7597 params:this.getParams()
7601 success : function(response){
7603 var result = this.processResponse(response);
7604 if(result === true || !result.success || !result.data){
7605 this.failureType = Roo.form.Action.LOAD_FAILURE;
7606 this.form.afterAction(this, false);
7609 this.form.clearInvalid();
7610 this.form.setValues(result.data);
7611 this.form.afterAction(this, true);
7614 handleResponse : function(response){
7615 if(this.form.reader){
7616 var rs = this.form.reader.read(response);
7617 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7619 success : rs.success,
7623 return Roo.decode(response.responseText);
7627 Roo.form.Action.ACTION_TYPES = {
7628 'load' : Roo.form.Action.Load,
7629 'submit' : Roo.form.Action.Submit
7638 * @class Roo.bootstrap.Form
7639 * @extends Roo.bootstrap.Component
7640 * Bootstrap Form class
7641 * @cfg {String} method GET | POST (default POST)
7642 * @cfg {String} labelAlign top | left (default top)
7643 * @cfg {String} align left | right - for navbars
7644 * @cfg {Boolean} loadMask load mask when submit (default true)
7649 * @param {Object} config The config object
7653 Roo.bootstrap.Form = function(config){
7655 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7657 Roo.bootstrap.Form.popover.apply();
7661 * @event clientvalidation
7662 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7663 * @param {Form} this
7664 * @param {Boolean} valid true if the form has passed client-side validation
7666 clientvalidation: true,
7668 * @event beforeaction
7669 * Fires before any action is performed. Return false to cancel the action.
7670 * @param {Form} this
7671 * @param {Action} action The action to be performed
7675 * @event actionfailed
7676 * Fires when an action fails.
7677 * @param {Form} this
7678 * @param {Action} action The action that failed
7680 actionfailed : true,
7682 * @event actioncomplete
7683 * Fires when an action is completed.
7684 * @param {Form} this
7685 * @param {Action} action The action that completed
7687 actioncomplete : true
7691 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7694 * @cfg {String} method
7695 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7700 * The URL to use for form actions if one isn't supplied in the action options.
7703 * @cfg {Boolean} fileUpload
7704 * Set to true if this form is a file upload.
7708 * @cfg {Object} baseParams
7709 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7713 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7717 * @cfg {Sting} align (left|right) for navbar forms
7722 activeAction : null,
7725 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7726 * element by passing it or its id or mask the form itself by passing in true.
7729 waitMsgTarget : false,
7734 * @cfg {Boolean} errorMask (true|false) default false
7739 * @cfg {Number} maskOffset Default 100
7744 * @cfg {Boolean} maskBody
7748 getAutoCreate : function(){
7752 method : this.method || 'POST',
7753 id : this.id || Roo.id(),
7756 if (this.parent().xtype.match(/^Nav/)) {
7757 cfg.cls = 'navbar-form navbar-' + this.align;
7761 if (this.labelAlign == 'left' ) {
7762 cfg.cls += ' form-horizontal';
7768 initEvents : function()
7770 this.el.on('submit', this.onSubmit, this);
7771 // this was added as random key presses on the form where triggering form submit.
7772 this.el.on('keypress', function(e) {
7773 if (e.getCharCode() != 13) {
7776 // we might need to allow it for textareas.. and some other items.
7777 // check e.getTarget().
7779 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7783 Roo.log("keypress blocked");
7791 onSubmit : function(e){
7796 * Returns true if client-side validation on the form is successful.
7799 isValid : function(){
7800 var items = this.getItems();
7804 items.each(function(f){
7811 if(!target && f.el.isVisible(true)){
7817 if(this.errorMask && !valid){
7818 Roo.bootstrap.Form.popover.mask(this, target);
7825 * Returns true if any fields in this form have changed since their original load.
7828 isDirty : function(){
7830 var items = this.getItems();
7831 items.each(function(f){
7841 * Performs a predefined action (submit or load) or custom actions you define on this form.
7842 * @param {String} actionName The name of the action type
7843 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7844 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7845 * accept other config options):
7847 Property Type Description
7848 ---------------- --------------- ----------------------------------------------------------------------------------
7849 url String The url for the action (defaults to the form's url)
7850 method String The form method to use (defaults to the form's method, or POST if not defined)
7851 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7852 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7853 validate the form on the client (defaults to false)
7855 * @return {BasicForm} this
7857 doAction : function(action, options){
7858 if(typeof action == 'string'){
7859 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7861 if(this.fireEvent('beforeaction', this, action) !== false){
7862 this.beforeAction(action);
7863 action.run.defer(100, action);
7869 beforeAction : function(action){
7870 var o = action.options;
7875 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7877 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7880 // not really supported yet.. ??
7882 //if(this.waitMsgTarget === true){
7883 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7884 //}else if(this.waitMsgTarget){
7885 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7886 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7888 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7894 afterAction : function(action, success){
7895 this.activeAction = null;
7896 var o = action.options;
7901 Roo.get(document.body).unmask();
7907 //if(this.waitMsgTarget === true){
7908 // this.el.unmask();
7909 //}else if(this.waitMsgTarget){
7910 // this.waitMsgTarget.unmask();
7912 // Roo.MessageBox.updateProgress(1);
7913 // Roo.MessageBox.hide();
7920 Roo.callback(o.success, o.scope, [this, action]);
7921 this.fireEvent('actioncomplete', this, action);
7925 // failure condition..
7926 // we have a scenario where updates need confirming.
7927 // eg. if a locking scenario exists..
7928 // we look for { errors : { needs_confirm : true }} in the response.
7930 (typeof(action.result) != 'undefined') &&
7931 (typeof(action.result.errors) != 'undefined') &&
7932 (typeof(action.result.errors.needs_confirm) != 'undefined')
7935 Roo.log("not supported yet");
7938 Roo.MessageBox.confirm(
7939 "Change requires confirmation",
7940 action.result.errorMsg,
7945 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7955 Roo.callback(o.failure, o.scope, [this, action]);
7956 // show an error message if no failed handler is set..
7957 if (!this.hasListener('actionfailed')) {
7958 Roo.log("need to add dialog support");
7960 Roo.MessageBox.alert("Error",
7961 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7962 action.result.errorMsg :
7963 "Saving Failed, please check your entries or try again"
7968 this.fireEvent('actionfailed', this, action);
7973 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7974 * @param {String} id The value to search for
7977 findField : function(id){
7978 var items = this.getItems();
7979 var field = items.get(id);
7981 items.each(function(f){
7982 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7989 return field || null;
7992 * Mark fields in this form invalid in bulk.
7993 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7994 * @return {BasicForm} this
7996 markInvalid : function(errors){
7997 if(errors instanceof Array){
7998 for(var i = 0, len = errors.length; i < len; i++){
7999 var fieldError = errors[i];
8000 var f = this.findField(fieldError.id);
8002 f.markInvalid(fieldError.msg);
8008 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8009 field.markInvalid(errors[id]);
8013 //Roo.each(this.childForms || [], function (f) {
8014 // f.markInvalid(errors);
8021 * Set values for fields in this form in bulk.
8022 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8023 * @return {BasicForm} this
8025 setValues : function(values){
8026 if(values instanceof Array){ // array of objects
8027 for(var i = 0, len = values.length; i < len; i++){
8029 var f = this.findField(v.id);
8031 f.setValue(v.value);
8032 if(this.trackResetOnLoad){
8033 f.originalValue = f.getValue();
8037 }else{ // object hash
8040 if(typeof values[id] != 'function' && (field = this.findField(id))){
8042 if (field.setFromData &&
8044 field.displayField &&
8045 // combos' with local stores can
8046 // be queried via setValue()
8047 // to set their value..
8048 (field.store && !field.store.isLocal)
8052 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8053 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8054 field.setFromData(sd);
8056 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8058 field.setFromData(values);
8061 field.setValue(values[id]);
8065 if(this.trackResetOnLoad){
8066 field.originalValue = field.getValue();
8072 //Roo.each(this.childForms || [], function (f) {
8073 // f.setValues(values);
8080 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8081 * they are returned as an array.
8082 * @param {Boolean} asString
8085 getValues : function(asString){
8086 //if (this.childForms) {
8087 // copy values from the child forms
8088 // Roo.each(this.childForms, function (f) {
8089 // this.setValues(f.getValues());
8095 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8096 if(asString === true){
8099 return Roo.urlDecode(fs);
8103 * Returns the fields in this form as an object with key/value pairs.
8104 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8107 getFieldValues : function(with_hidden)
8109 var items = this.getItems();
8111 items.each(function(f){
8117 var v = f.getValue();
8119 if (f.inputType =='radio') {
8120 if (typeof(ret[f.getName()]) == 'undefined') {
8121 ret[f.getName()] = ''; // empty..
8124 if (!f.el.dom.checked) {
8132 if(f.xtype == 'MoneyField'){
8133 ret[f.currencyName] = f.getCurrency();
8136 // not sure if this supported any more..
8137 if ((typeof(v) == 'object') && f.getRawValue) {
8138 v = f.getRawValue() ; // dates..
8140 // combo boxes where name != hiddenName...
8141 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8142 ret[f.name] = f.getRawValue();
8144 ret[f.getName()] = v;
8151 * Clears all invalid messages in this form.
8152 * @return {BasicForm} this
8154 clearInvalid : function(){
8155 var items = this.getItems();
8157 items.each(function(f){
8166 * @return {BasicForm} this
8169 var items = this.getItems();
8170 items.each(function(f){
8174 Roo.each(this.childForms || [], function (f) {
8182 getItems : function()
8184 var r=new Roo.util.MixedCollection(false, function(o){
8185 return o.id || (o.id = Roo.id());
8187 var iter = function(el) {
8194 Roo.each(el.items,function(e) {
8203 hideFields : function(items)
8205 Roo.each(items, function(i){
8207 var f = this.findField(i);
8213 if(f.xtype == 'DateField'){
8214 f.setVisible(false);
8223 showFields : function(items)
8225 Roo.each(items, function(i){
8227 var f = this.findField(i);
8233 if(f.xtype == 'DateField'){
8245 Roo.apply(Roo.bootstrap.Form, {
8272 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8273 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8274 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8275 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8278 this.maskEl.top.enableDisplayMode("block");
8279 this.maskEl.left.enableDisplayMode("block");
8280 this.maskEl.bottom.enableDisplayMode("block");
8281 this.maskEl.right.enableDisplayMode("block");
8283 this.toolTip = new Roo.bootstrap.Tooltip({
8284 cls : 'roo-form-error-popover',
8286 'left' : ['r-l', [-2,0], 'right'],
8287 'right' : ['l-r', [2,0], 'left'],
8288 'bottom' : ['tl-bl', [0,2], 'top'],
8289 'top' : [ 'bl-tl', [0,-2], 'bottom']
8293 this.toolTip.render(Roo.get(document.body));
8295 this.toolTip.el.enableDisplayMode("block");
8297 Roo.get(document.body).on('click', function(){
8301 Roo.get(document.body).on('touchstart', function(){
8305 this.isApplied = true
8308 mask : function(form, target)
8312 this.target = target;
8314 if(!this.form.errorMask || !target.el){
8318 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8320 Roo.log(scrollable);
8322 var ot = this.target.el.calcOffsetsTo(scrollable);
8324 var scrollTo = ot[1] - this.form.maskOffset;
8326 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8328 scrollable.scrollTo('top', scrollTo);
8330 var box = this.target.el.getBox();
8332 var zIndex = Roo.bootstrap.Modal.zIndex++;
8335 this.maskEl.top.setStyle('position', 'absolute');
8336 this.maskEl.top.setStyle('z-index', zIndex);
8337 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8338 this.maskEl.top.setLeft(0);
8339 this.maskEl.top.setTop(0);
8340 this.maskEl.top.show();
8342 this.maskEl.left.setStyle('position', 'absolute');
8343 this.maskEl.left.setStyle('z-index', zIndex);
8344 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8345 this.maskEl.left.setLeft(0);
8346 this.maskEl.left.setTop(box.y - this.padding);
8347 this.maskEl.left.show();
8349 this.maskEl.bottom.setStyle('position', 'absolute');
8350 this.maskEl.bottom.setStyle('z-index', zIndex);
8351 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8352 this.maskEl.bottom.setLeft(0);
8353 this.maskEl.bottom.setTop(box.bottom + this.padding);
8354 this.maskEl.bottom.show();
8356 this.maskEl.right.setStyle('position', 'absolute');
8357 this.maskEl.right.setStyle('z-index', zIndex);
8358 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8359 this.maskEl.right.setLeft(box.right + this.padding);
8360 this.maskEl.right.setTop(box.y - this.padding);
8361 this.maskEl.right.show();
8363 this.toolTip.bindEl = this.target.el;
8365 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8367 var tip = this.target.blankText;
8369 if(this.target.getValue() !== '' ) {
8371 if (this.target.invalidText.length) {
8372 tip = this.target.invalidText;
8373 } else if (this.target.regexText.length){
8374 tip = this.target.regexText;
8378 this.toolTip.show(tip);
8380 this.intervalID = window.setInterval(function() {
8381 Roo.bootstrap.Form.popover.unmask();
8384 window.onwheel = function(){ return false;};
8386 (function(){ this.isMasked = true; }).defer(500, this);
8392 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8396 this.maskEl.top.setStyle('position', 'absolute');
8397 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8398 this.maskEl.top.hide();
8400 this.maskEl.left.setStyle('position', 'absolute');
8401 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8402 this.maskEl.left.hide();
8404 this.maskEl.bottom.setStyle('position', 'absolute');
8405 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8406 this.maskEl.bottom.hide();
8408 this.maskEl.right.setStyle('position', 'absolute');
8409 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8410 this.maskEl.right.hide();
8412 this.toolTip.hide();
8414 this.toolTip.el.hide();
8416 window.onwheel = function(){ return true;};
8418 if(this.intervalID){
8419 window.clearInterval(this.intervalID);
8420 this.intervalID = false;
8423 this.isMasked = false;
8433 * Ext JS Library 1.1.1
8434 * Copyright(c) 2006-2007, Ext JS, LLC.
8436 * Originally Released Under LGPL - original licence link has changed is not relivant.
8439 * <script type="text/javascript">
8442 * @class Roo.form.VTypes
8443 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8446 Roo.form.VTypes = function(){
8447 // closure these in so they are only created once.
8448 var alpha = /^[a-zA-Z_]+$/;
8449 var alphanum = /^[a-zA-Z0-9_]+$/;
8450 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8451 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8453 // All these messages and functions are configurable
8456 * The function used to validate email addresses
8457 * @param {String} value The email address
8459 'email' : function(v){
8460 return email.test(v);
8463 * The error text to display when the email validation function returns false
8466 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8468 * The keystroke filter mask to be applied on email input
8471 'emailMask' : /[a-z0-9_\.\-@]/i,
8474 * The function used to validate URLs
8475 * @param {String} value The URL
8477 'url' : function(v){
8481 * The error text to display when the url validation function returns false
8484 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8487 * The function used to validate alpha values
8488 * @param {String} value The value
8490 'alpha' : function(v){
8491 return alpha.test(v);
8494 * The error text to display when the alpha validation function returns false
8497 'alphaText' : 'This field should only contain letters and _',
8499 * The keystroke filter mask to be applied on alpha input
8502 'alphaMask' : /[a-z_]/i,
8505 * The function used to validate alphanumeric values
8506 * @param {String} value The value
8508 'alphanum' : function(v){
8509 return alphanum.test(v);
8512 * The error text to display when the alphanumeric validation function returns false
8515 'alphanumText' : 'This field should only contain letters, numbers and _',
8517 * The keystroke filter mask to be applied on alphanumeric input
8520 'alphanumMask' : /[a-z0-9_]/i
8530 * @class Roo.bootstrap.Input
8531 * @extends Roo.bootstrap.Component
8532 * Bootstrap Input class
8533 * @cfg {Boolean} disabled is it disabled
8534 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8535 * @cfg {String} name name of the input
8536 * @cfg {string} fieldLabel - the label associated
8537 * @cfg {string} placeholder - placeholder to put in text.
8538 * @cfg {string} before - input group add on before
8539 * @cfg {string} after - input group add on after
8540 * @cfg {string} size - (lg|sm) or leave empty..
8541 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8542 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8543 * @cfg {Number} md colspan out of 12 for computer-sized screens
8544 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8545 * @cfg {string} value default value of the input
8546 * @cfg {Number} labelWidth set the width of label
8547 * @cfg {Number} labellg set the width of label (1-12)
8548 * @cfg {Number} labelmd set the width of label (1-12)
8549 * @cfg {Number} labelsm set the width of label (1-12)
8550 * @cfg {Number} labelxs set the width of label (1-12)
8551 * @cfg {String} labelAlign (top|left)
8552 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8553 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8554 * @cfg {String} indicatorpos (left|right) default left
8555 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8556 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8558 * @cfg {String} align (left|center|right) Default left
8559 * @cfg {Boolean} forceFeedback (true|false) Default false
8562 * Create a new Input
8563 * @param {Object} config The config object
8566 Roo.bootstrap.Input = function(config){
8568 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8573 * Fires when this field receives input focus.
8574 * @param {Roo.form.Field} this
8579 * Fires when this field loses input focus.
8580 * @param {Roo.form.Field} this
8585 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8586 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8587 * @param {Roo.form.Field} this
8588 * @param {Roo.EventObject} e The event object
8593 * Fires just before the field blurs if the field value has changed.
8594 * @param {Roo.form.Field} this
8595 * @param {Mixed} newValue The new value
8596 * @param {Mixed} oldValue The original value
8601 * Fires after the field has been marked as invalid.
8602 * @param {Roo.form.Field} this
8603 * @param {String} msg The validation message
8608 * Fires after the field has been validated with no errors.
8609 * @param {Roo.form.Field} this
8614 * Fires after the key up
8615 * @param {Roo.form.Field} this
8616 * @param {Roo.EventObject} e The event Object
8622 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8624 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8625 automatic validation (defaults to "keyup").
8627 validationEvent : "keyup",
8629 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8631 validateOnBlur : true,
8633 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8635 validationDelay : 250,
8637 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8639 focusClass : "x-form-focus", // not needed???
8643 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8645 invalidClass : "has-warning",
8648 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8650 validClass : "has-success",
8653 * @cfg {Boolean} hasFeedback (true|false) default true
8658 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8660 invalidFeedbackClass : "glyphicon-warning-sign",
8663 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8665 validFeedbackClass : "glyphicon-ok",
8668 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8670 selectOnFocus : false,
8673 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8677 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8682 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8684 disableKeyFilter : false,
8687 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8691 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8695 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8697 blankText : "Please complete this mandatory field",
8700 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8704 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8706 maxLength : Number.MAX_VALUE,
8708 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8710 minLengthText : "The minimum length for this field is {0}",
8712 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8714 maxLengthText : "The maximum length for this field is {0}",
8718 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8719 * If available, this function will be called only after the basic validators all return true, and will be passed the
8720 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8724 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8725 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8726 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8730 * @cfg {String} regexText -- Depricated - use Invalid Text
8735 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8741 autocomplete: false,
8760 formatedValue : false,
8761 forceFeedback : false,
8763 indicatorpos : 'left',
8773 parentLabelAlign : function()
8776 while (parent.parent()) {
8777 parent = parent.parent();
8778 if (typeof(parent.labelAlign) !='undefined') {
8779 return parent.labelAlign;
8786 getAutoCreate : function()
8788 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8794 if(this.inputType != 'hidden'){
8795 cfg.cls = 'form-group' //input-group
8801 type : this.inputType,
8803 cls : 'form-control',
8804 placeholder : this.placeholder || '',
8805 autocomplete : this.autocomplete || 'new-password'
8808 if(this.capture.length){
8809 input.capture = this.capture;
8812 if(this.accept.length){
8813 input.accept = this.accept + "/*";
8817 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8820 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8821 input.maxLength = this.maxLength;
8824 if (this.disabled) {
8825 input.disabled=true;
8828 if (this.readOnly) {
8829 input.readonly=true;
8833 input.name = this.name;
8837 input.cls += ' input-' + this.size;
8841 ['xs','sm','md','lg'].map(function(size){
8842 if (settings[size]) {
8843 cfg.cls += ' col-' + size + '-' + settings[size];
8847 var inputblock = input;
8851 cls: 'glyphicon form-control-feedback'
8854 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8857 cls : 'has-feedback',
8865 if (this.before || this.after) {
8868 cls : 'input-group',
8872 if (this.before && typeof(this.before) == 'string') {
8874 inputblock.cn.push({
8876 cls : 'roo-input-before input-group-addon',
8880 if (this.before && typeof(this.before) == 'object') {
8881 this.before = Roo.factory(this.before);
8883 inputblock.cn.push({
8885 cls : 'roo-input-before input-group-' +
8886 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8890 inputblock.cn.push(input);
8892 if (this.after && typeof(this.after) == 'string') {
8893 inputblock.cn.push({
8895 cls : 'roo-input-after input-group-addon',
8899 if (this.after && typeof(this.after) == 'object') {
8900 this.after = Roo.factory(this.after);
8902 inputblock.cn.push({
8904 cls : 'roo-input-after input-group-' +
8905 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8909 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8910 inputblock.cls += ' has-feedback';
8911 inputblock.cn.push(feedback);
8915 if (align ==='left' && this.fieldLabel.length) {
8917 cfg.cls += ' roo-form-group-label-left';
8922 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8923 tooltip : 'This field is required'
8928 cls : 'control-label',
8929 html : this.fieldLabel
8940 var labelCfg = cfg.cn[1];
8941 var contentCfg = cfg.cn[2];
8943 if(this.indicatorpos == 'right'){
8948 cls : 'control-label',
8952 html : this.fieldLabel
8956 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8957 tooltip : 'This field is required'
8970 labelCfg = cfg.cn[0];
8971 contentCfg = cfg.cn[1];
8975 if(this.labelWidth > 12){
8976 labelCfg.style = "width: " + this.labelWidth + 'px';
8979 if(this.labelWidth < 13 && this.labelmd == 0){
8980 this.labelmd = this.labelWidth;
8983 if(this.labellg > 0){
8984 labelCfg.cls += ' col-lg-' + this.labellg;
8985 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8988 if(this.labelmd > 0){
8989 labelCfg.cls += ' col-md-' + this.labelmd;
8990 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8993 if(this.labelsm > 0){
8994 labelCfg.cls += ' col-sm-' + this.labelsm;
8995 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8998 if(this.labelxs > 0){
8999 labelCfg.cls += ' col-xs-' + this.labelxs;
9000 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9004 } else if ( this.fieldLabel.length) {
9009 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9010 tooltip : 'This field is required'
9014 //cls : 'input-group-addon',
9015 html : this.fieldLabel
9023 if(this.indicatorpos == 'right'){
9028 //cls : 'input-group-addon',
9029 html : this.fieldLabel
9034 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9035 tooltip : 'This field is required'
9055 if (this.parentType === 'Navbar' && this.parent().bar) {
9056 cfg.cls += ' navbar-form';
9059 if (this.parentType === 'NavGroup') {
9060 cfg.cls += ' navbar-form';
9068 * return the real input element.
9070 inputEl: function ()
9072 return this.el.select('input.form-control',true).first();
9075 tooltipEl : function()
9077 return this.inputEl();
9080 indicatorEl : function()
9082 var indicator = this.el.select('i.roo-required-indicator',true).first();
9092 setDisabled : function(v)
9094 var i = this.inputEl().dom;
9096 i.removeAttribute('disabled');
9100 i.setAttribute('disabled','true');
9102 initEvents : function()
9105 this.inputEl().on("keydown" , this.fireKey, this);
9106 this.inputEl().on("focus", this.onFocus, this);
9107 this.inputEl().on("blur", this.onBlur, this);
9109 this.inputEl().relayEvent('keyup', this);
9111 this.indicator = this.indicatorEl();
9114 this.indicator.addClass('invisible');
9117 // reference to original value for reset
9118 this.originalValue = this.getValue();
9119 //Roo.form.TextField.superclass.initEvents.call(this);
9120 if(this.validationEvent == 'keyup'){
9121 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9122 this.inputEl().on('keyup', this.filterValidation, this);
9124 else if(this.validationEvent !== false){
9125 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9128 if(this.selectOnFocus){
9129 this.on("focus", this.preFocus, this);
9132 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9133 this.inputEl().on("keypress", this.filterKeys, this);
9135 this.inputEl().relayEvent('keypress', this);
9138 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9139 this.el.on("click", this.autoSize, this);
9142 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9143 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9146 if (typeof(this.before) == 'object') {
9147 this.before.render(this.el.select('.roo-input-before',true).first());
9149 if (typeof(this.after) == 'object') {
9150 this.after.render(this.el.select('.roo-input-after',true).first());
9153 this.inputEl().on('change', this.onChange, this);
9156 filterValidation : function(e){
9157 if(!e.isNavKeyPress()){
9158 this.validationTask.delay(this.validationDelay);
9162 * Validates the field value
9163 * @return {Boolean} True if the value is valid, else false
9165 validate : function(){
9166 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9167 if(this.disabled || this.validateValue(this.getRawValue())){
9178 * Validates a value according to the field's validation rules and marks the field as invalid
9179 * if the validation fails
9180 * @param {Mixed} value The value to validate
9181 * @return {Boolean} True if the value is valid, else false
9183 validateValue : function(value)
9185 if(this.getVisibilityEl().hasClass('hidden')){
9189 if(value.length < 1) { // if it's blank
9190 if(this.allowBlank){
9196 if(value.length < this.minLength){
9199 if(value.length > this.maxLength){
9203 var vt = Roo.form.VTypes;
9204 if(!vt[this.vtype](value, this)){
9208 if(typeof this.validator == "function"){
9209 var msg = this.validator(value);
9213 if (typeof(msg) == 'string') {
9214 this.invalidText = msg;
9218 if(this.regex && !this.regex.test(value)){
9226 fireKey : function(e){
9227 //Roo.log('field ' + e.getKey());
9228 if(e.isNavKeyPress()){
9229 this.fireEvent("specialkey", this, e);
9232 focus : function (selectText){
9234 this.inputEl().focus();
9235 if(selectText === true){
9236 this.inputEl().dom.select();
9242 onFocus : function(){
9243 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9244 // this.el.addClass(this.focusClass);
9247 this.hasFocus = true;
9248 this.startValue = this.getValue();
9249 this.fireEvent("focus", this);
9253 beforeBlur : Roo.emptyFn,
9257 onBlur : function(){
9259 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9260 //this.el.removeClass(this.focusClass);
9262 this.hasFocus = false;
9263 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9266 var v = this.getValue();
9267 if(String(v) !== String(this.startValue)){
9268 this.fireEvent('change', this, v, this.startValue);
9270 this.fireEvent("blur", this);
9273 onChange : function(e)
9275 var v = this.getValue();
9276 if(String(v) !== String(this.startValue)){
9277 this.fireEvent('change', this, v, this.startValue);
9283 * Resets the current field value to the originally loaded value and clears any validation messages
9286 this.setValue(this.originalValue);
9290 * Returns the name of the field
9291 * @return {Mixed} name The name field
9293 getName: function(){
9297 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9298 * @return {Mixed} value The field value
9300 getValue : function(){
9302 var v = this.inputEl().getValue();
9307 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9308 * @return {Mixed} value The field value
9310 getRawValue : function(){
9311 var v = this.inputEl().getValue();
9317 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9318 * @param {Mixed} value The value to set
9320 setRawValue : function(v){
9321 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9324 selectText : function(start, end){
9325 var v = this.getRawValue();
9327 start = start === undefined ? 0 : start;
9328 end = end === undefined ? v.length : end;
9329 var d = this.inputEl().dom;
9330 if(d.setSelectionRange){
9331 d.setSelectionRange(start, end);
9332 }else if(d.createTextRange){
9333 var range = d.createTextRange();
9334 range.moveStart("character", start);
9335 range.moveEnd("character", v.length-end);
9342 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9343 * @param {Mixed} value The value to set
9345 setValue : function(v){
9348 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9354 processValue : function(value){
9355 if(this.stripCharsRe){
9356 var newValue = value.replace(this.stripCharsRe, '');
9357 if(newValue !== value){
9358 this.setRawValue(newValue);
9365 preFocus : function(){
9367 if(this.selectOnFocus){
9368 this.inputEl().dom.select();
9371 filterKeys : function(e){
9373 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9376 var c = e.getCharCode(), cc = String.fromCharCode(c);
9377 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9380 if(!this.maskRe.test(cc)){
9385 * Clear any invalid styles/messages for this field
9387 clearInvalid : function(){
9389 if(!this.el || this.preventMark){ // not rendered
9394 this.el.removeClass(this.invalidClass);
9396 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9398 var feedback = this.el.select('.form-control-feedback', true).first();
9401 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9406 this.fireEvent('valid', this);
9410 * Mark this field as valid
9412 markValid : function()
9414 if(!this.el || this.preventMark){ // not rendered...
9418 this.el.removeClass([this.invalidClass, this.validClass]);
9420 var feedback = this.el.select('.form-control-feedback', true).first();
9423 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9427 this.indicator.removeClass('visible');
9428 this.indicator.addClass('invisible');
9435 if(this.allowBlank && !this.getRawValue().length){
9439 this.el.addClass(this.validClass);
9441 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9443 var feedback = this.el.select('.form-control-feedback', true).first();
9446 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9447 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9452 this.fireEvent('valid', this);
9456 * Mark this field as invalid
9457 * @param {String} msg The validation message
9459 markInvalid : function(msg)
9461 if(!this.el || this.preventMark){ // not rendered
9465 this.el.removeClass([this.invalidClass, this.validClass]);
9467 var feedback = this.el.select('.form-control-feedback', true).first();
9470 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9477 if(this.allowBlank && !this.getRawValue().length){
9482 this.indicator.removeClass('invisible');
9483 this.indicator.addClass('visible');
9486 this.el.addClass(this.invalidClass);
9488 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9490 var feedback = this.el.select('.form-control-feedback', true).first();
9493 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9495 if(this.getValue().length || this.forceFeedback){
9496 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9503 this.fireEvent('invalid', this, msg);
9506 SafariOnKeyDown : function(event)
9508 // this is a workaround for a password hang bug on chrome/ webkit.
9509 if (this.inputEl().dom.type != 'password') {
9513 var isSelectAll = false;
9515 if(this.inputEl().dom.selectionEnd > 0){
9516 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9518 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9519 event.preventDefault();
9524 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9526 event.preventDefault();
9527 // this is very hacky as keydown always get's upper case.
9529 var cc = String.fromCharCode(event.getCharCode());
9530 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9534 adjustWidth : function(tag, w){
9535 tag = tag.toLowerCase();
9536 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9537 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9541 if(tag == 'textarea'){
9544 }else if(Roo.isOpera){
9548 if(tag == 'textarea'){
9556 setFieldLabel : function(v)
9563 var ar = this.el.select('label > span',true);
9565 if (ar.elements.length) {
9566 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9567 this.fieldLabel = v;
9571 var br = this.el.select('label',true);
9573 if(br.elements.length) {
9574 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9575 this.fieldLabel = v;
9579 Roo.log('Cannot Found any of label > span || label in input');
9583 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9584 this.fieldLabel = v;
9599 * @class Roo.bootstrap.TextArea
9600 * @extends Roo.bootstrap.Input
9601 * Bootstrap TextArea class
9602 * @cfg {Number} cols Specifies the visible width of a text area
9603 * @cfg {Number} rows Specifies the visible number of lines in a text area
9604 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9605 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9606 * @cfg {string} html text
9609 * Create a new TextArea
9610 * @param {Object} config The config object
9613 Roo.bootstrap.TextArea = function(config){
9614 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9618 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9628 getAutoCreate : function(){
9630 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9636 if(this.inputType != 'hidden'){
9637 cfg.cls = 'form-group' //input-group
9645 value : this.value || '',
9646 html: this.html || '',
9647 cls : 'form-control',
9648 placeholder : this.placeholder || ''
9652 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9653 input.maxLength = this.maxLength;
9657 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9661 input.cols = this.cols;
9664 if (this.readOnly) {
9665 input.readonly = true;
9669 input.name = this.name;
9673 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9677 ['xs','sm','md','lg'].map(function(size){
9678 if (settings[size]) {
9679 cfg.cls += ' col-' + size + '-' + settings[size];
9683 var inputblock = input;
9685 if(this.hasFeedback && !this.allowBlank){
9689 cls: 'glyphicon form-control-feedback'
9693 cls : 'has-feedback',
9702 if (this.before || this.after) {
9705 cls : 'input-group',
9709 inputblock.cn.push({
9711 cls : 'input-group-addon',
9716 inputblock.cn.push(input);
9718 if(this.hasFeedback && !this.allowBlank){
9719 inputblock.cls += ' has-feedback';
9720 inputblock.cn.push(feedback);
9724 inputblock.cn.push({
9726 cls : 'input-group-addon',
9733 if (align ==='left' && this.fieldLabel.length) {
9738 cls : 'control-label',
9739 html : this.fieldLabel
9750 if(this.labelWidth > 12){
9751 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9754 if(this.labelWidth < 13 && this.labelmd == 0){
9755 this.labelmd = this.labelWidth;
9758 if(this.labellg > 0){
9759 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9760 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9763 if(this.labelmd > 0){
9764 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9765 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9768 if(this.labelsm > 0){
9769 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9770 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9773 if(this.labelxs > 0){
9774 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9775 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9778 } else if ( this.fieldLabel.length) {
9783 //cls : 'input-group-addon',
9784 html : this.fieldLabel
9802 if (this.disabled) {
9803 input.disabled=true;
9810 * return the real textarea element.
9812 inputEl: function ()
9814 return this.el.select('textarea.form-control',true).first();
9818 * Clear any invalid styles/messages for this field
9820 clearInvalid : function()
9823 if(!this.el || this.preventMark){ // not rendered
9827 var label = this.el.select('label', true).first();
9828 var icon = this.el.select('i.fa-star', true).first();
9834 this.el.removeClass(this.invalidClass);
9836 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9838 var feedback = this.el.select('.form-control-feedback', true).first();
9841 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9846 this.fireEvent('valid', this);
9850 * Mark this field as valid
9852 markValid : function()
9854 if(!this.el || this.preventMark){ // not rendered
9858 this.el.removeClass([this.invalidClass, this.validClass]);
9860 var feedback = this.el.select('.form-control-feedback', true).first();
9863 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9866 if(this.disabled || this.allowBlank){
9870 var label = this.el.select('label', true).first();
9871 var icon = this.el.select('i.fa-star', true).first();
9877 this.el.addClass(this.validClass);
9879 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9881 var feedback = this.el.select('.form-control-feedback', true).first();
9884 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9885 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9890 this.fireEvent('valid', this);
9894 * Mark this field as invalid
9895 * @param {String} msg The validation message
9897 markInvalid : function(msg)
9899 if(!this.el || this.preventMark){ // not rendered
9903 this.el.removeClass([this.invalidClass, this.validClass]);
9905 var feedback = this.el.select('.form-control-feedback', true).first();
9908 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9911 if(this.disabled || this.allowBlank){
9915 var label = this.el.select('label', true).first();
9916 var icon = this.el.select('i.fa-star', true).first();
9918 if(!this.getValue().length && label && !icon){
9919 this.el.createChild({
9921 cls : 'text-danger fa fa-lg fa-star',
9922 tooltip : 'This field is required',
9923 style : 'margin-right:5px;'
9927 this.el.addClass(this.invalidClass);
9929 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9931 var feedback = this.el.select('.form-control-feedback', true).first();
9934 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9936 if(this.getValue().length || this.forceFeedback){
9937 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9944 this.fireEvent('invalid', this, msg);
9952 * trigger field - base class for combo..
9957 * @class Roo.bootstrap.TriggerField
9958 * @extends Roo.bootstrap.Input
9959 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9960 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9961 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9962 * for which you can provide a custom implementation. For example:
9964 var trigger = new Roo.bootstrap.TriggerField();
9965 trigger.onTriggerClick = myTriggerFn;
9966 trigger.applyTo('my-field');
9969 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9970 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9971 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9972 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9973 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9976 * Create a new TriggerField.
9977 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9978 * to the base TextField)
9980 Roo.bootstrap.TriggerField = function(config){
9981 this.mimicing = false;
9982 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9985 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9987 * @cfg {String} triggerClass A CSS class to apply to the trigger
9990 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9995 * @cfg {Boolean} removable (true|false) special filter default false
9999 /** @cfg {Boolean} grow @hide */
10000 /** @cfg {Number} growMin @hide */
10001 /** @cfg {Number} growMax @hide */
10007 autoSize: Roo.emptyFn,
10011 deferHeight : true,
10014 actionMode : 'wrap',
10019 getAutoCreate : function(){
10021 var align = this.labelAlign || this.parentLabelAlign();
10026 cls: 'form-group' //input-group
10033 type : this.inputType,
10034 cls : 'form-control',
10035 autocomplete: 'new-password',
10036 placeholder : this.placeholder || ''
10040 input.name = this.name;
10043 input.cls += ' input-' + this.size;
10046 if (this.disabled) {
10047 input.disabled=true;
10050 var inputblock = input;
10052 if(this.hasFeedback && !this.allowBlank){
10056 cls: 'glyphicon form-control-feedback'
10059 if(this.removable && !this.editable && !this.tickable){
10061 cls : 'has-feedback',
10067 cls : 'roo-combo-removable-btn close'
10074 cls : 'has-feedback',
10083 if(this.removable && !this.editable && !this.tickable){
10085 cls : 'roo-removable',
10091 cls : 'roo-combo-removable-btn close'
10098 if (this.before || this.after) {
10101 cls : 'input-group',
10105 inputblock.cn.push({
10107 cls : 'input-group-addon',
10112 inputblock.cn.push(input);
10114 if(this.hasFeedback && !this.allowBlank){
10115 inputblock.cls += ' has-feedback';
10116 inputblock.cn.push(feedback);
10120 inputblock.cn.push({
10122 cls : 'input-group-addon',
10135 cls: 'form-hidden-field'
10149 cls: 'form-hidden-field'
10153 cls: 'roo-select2-choices',
10157 cls: 'roo-select2-search-field',
10170 cls: 'roo-select2-container input-group',
10175 // cls: 'typeahead typeahead-long dropdown-menu',
10176 // style: 'display:none'
10181 if(!this.multiple && this.showToggleBtn){
10187 if (this.caret != false) {
10190 cls: 'fa fa-' + this.caret
10197 cls : 'input-group-addon btn dropdown-toggle',
10202 cls: 'combobox-clear',
10216 combobox.cls += ' roo-select2-container-multi';
10219 if (align ==='left' && this.fieldLabel.length) {
10221 cfg.cls += ' roo-form-group-label-left';
10226 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10227 tooltip : 'This field is required'
10232 cls : 'control-label',
10233 html : this.fieldLabel
10245 var labelCfg = cfg.cn[1];
10246 var contentCfg = cfg.cn[2];
10248 if(this.indicatorpos == 'right'){
10253 cls : 'control-label',
10257 html : this.fieldLabel
10261 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10262 tooltip : 'This field is required'
10275 labelCfg = cfg.cn[0];
10276 contentCfg = cfg.cn[1];
10279 if(this.labelWidth > 12){
10280 labelCfg.style = "width: " + this.labelWidth + 'px';
10283 if(this.labelWidth < 13 && this.labelmd == 0){
10284 this.labelmd = this.labelWidth;
10287 if(this.labellg > 0){
10288 labelCfg.cls += ' col-lg-' + this.labellg;
10289 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10292 if(this.labelmd > 0){
10293 labelCfg.cls += ' col-md-' + this.labelmd;
10294 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10297 if(this.labelsm > 0){
10298 labelCfg.cls += ' col-sm-' + this.labelsm;
10299 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10302 if(this.labelxs > 0){
10303 labelCfg.cls += ' col-xs-' + this.labelxs;
10304 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10307 } else if ( this.fieldLabel.length) {
10308 // Roo.log(" label");
10312 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10313 tooltip : 'This field is required'
10317 //cls : 'input-group-addon',
10318 html : this.fieldLabel
10326 if(this.indicatorpos == 'right'){
10334 html : this.fieldLabel
10338 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10339 tooltip : 'This field is required'
10352 // Roo.log(" no label && no align");
10359 ['xs','sm','md','lg'].map(function(size){
10360 if (settings[size]) {
10361 cfg.cls += ' col-' + size + '-' + settings[size];
10372 onResize : function(w, h){
10373 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10374 // if(typeof w == 'number'){
10375 // var x = w - this.trigger.getWidth();
10376 // this.inputEl().setWidth(this.adjustWidth('input', x));
10377 // this.trigger.setStyle('left', x+'px');
10382 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10385 getResizeEl : function(){
10386 return this.inputEl();
10390 getPositionEl : function(){
10391 return this.inputEl();
10395 alignErrorIcon : function(){
10396 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10400 initEvents : function(){
10404 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10405 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10406 if(!this.multiple && this.showToggleBtn){
10407 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10408 if(this.hideTrigger){
10409 this.trigger.setDisplayed(false);
10411 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10415 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10418 if(this.removable && !this.editable && !this.tickable){
10419 var close = this.closeTriggerEl();
10422 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10423 close.on('click', this.removeBtnClick, this, close);
10427 //this.trigger.addClassOnOver('x-form-trigger-over');
10428 //this.trigger.addClassOnClick('x-form-trigger-click');
10431 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10435 closeTriggerEl : function()
10437 var close = this.el.select('.roo-combo-removable-btn', true).first();
10438 return close ? close : false;
10441 removeBtnClick : function(e, h, el)
10443 e.preventDefault();
10445 if(this.fireEvent("remove", this) !== false){
10447 this.fireEvent("afterremove", this)
10451 createList : function()
10453 this.list = Roo.get(document.body).createChild({
10455 cls: 'typeahead typeahead-long dropdown-menu',
10456 style: 'display:none'
10459 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10464 initTrigger : function(){
10469 onDestroy : function(){
10471 this.trigger.removeAllListeners();
10472 // this.trigger.remove();
10475 // this.wrap.remove();
10477 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10481 onFocus : function(){
10482 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10484 if(!this.mimicing){
10485 this.wrap.addClass('x-trigger-wrap-focus');
10486 this.mimicing = true;
10487 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10488 if(this.monitorTab){
10489 this.el.on("keydown", this.checkTab, this);
10496 checkTab : function(e){
10497 if(e.getKey() == e.TAB){
10498 this.triggerBlur();
10503 onBlur : function(){
10508 mimicBlur : function(e, t){
10510 if(!this.wrap.contains(t) && this.validateBlur()){
10511 this.triggerBlur();
10517 triggerBlur : function(){
10518 this.mimicing = false;
10519 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10520 if(this.monitorTab){
10521 this.el.un("keydown", this.checkTab, this);
10523 //this.wrap.removeClass('x-trigger-wrap-focus');
10524 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10528 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10529 validateBlur : function(e, t){
10534 onDisable : function(){
10535 this.inputEl().dom.disabled = true;
10536 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10538 // this.wrap.addClass('x-item-disabled');
10543 onEnable : function(){
10544 this.inputEl().dom.disabled = false;
10545 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10547 // this.el.removeClass('x-item-disabled');
10552 onShow : function(){
10553 var ae = this.getActionEl();
10556 ae.dom.style.display = '';
10557 ae.dom.style.visibility = 'visible';
10563 onHide : function(){
10564 var ae = this.getActionEl();
10565 ae.dom.style.display = 'none';
10569 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10570 * by an implementing function.
10572 * @param {EventObject} e
10574 onTriggerClick : Roo.emptyFn
10578 * Ext JS Library 1.1.1
10579 * Copyright(c) 2006-2007, Ext JS, LLC.
10581 * Originally Released Under LGPL - original licence link has changed is not relivant.
10584 * <script type="text/javascript">
10589 * @class Roo.data.SortTypes
10591 * Defines the default sorting (casting?) comparison functions used when sorting data.
10593 Roo.data.SortTypes = {
10595 * Default sort that does nothing
10596 * @param {Mixed} s The value being converted
10597 * @return {Mixed} The comparison value
10599 none : function(s){
10604 * The regular expression used to strip tags
10608 stripTagsRE : /<\/?[^>]+>/gi,
10611 * Strips all HTML tags to sort on text only
10612 * @param {Mixed} s The value being converted
10613 * @return {String} The comparison value
10615 asText : function(s){
10616 return String(s).replace(this.stripTagsRE, "");
10620 * Strips all HTML tags to sort on text only - Case insensitive
10621 * @param {Mixed} s The value being converted
10622 * @return {String} The comparison value
10624 asUCText : function(s){
10625 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10629 * Case insensitive string
10630 * @param {Mixed} s The value being converted
10631 * @return {String} The comparison value
10633 asUCString : function(s) {
10634 return String(s).toUpperCase();
10639 * @param {Mixed} s The value being converted
10640 * @return {Number} The comparison value
10642 asDate : function(s) {
10646 if(s instanceof Date){
10647 return s.getTime();
10649 return Date.parse(String(s));
10654 * @param {Mixed} s The value being converted
10655 * @return {Float} The comparison value
10657 asFloat : function(s) {
10658 var val = parseFloat(String(s).replace(/,/g, ""));
10667 * @param {Mixed} s The value being converted
10668 * @return {Number} The comparison value
10670 asInt : function(s) {
10671 var val = parseInt(String(s).replace(/,/g, ""));
10679 * Ext JS Library 1.1.1
10680 * Copyright(c) 2006-2007, Ext JS, LLC.
10682 * Originally Released Under LGPL - original licence link has changed is not relivant.
10685 * <script type="text/javascript">
10689 * @class Roo.data.Record
10690 * Instances of this class encapsulate both record <em>definition</em> information, and record
10691 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10692 * to access Records cached in an {@link Roo.data.Store} object.<br>
10694 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10695 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10698 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10700 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10701 * {@link #create}. The parameters are the same.
10702 * @param {Array} data An associative Array of data values keyed by the field name.
10703 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10704 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10705 * not specified an integer id is generated.
10707 Roo.data.Record = function(data, id){
10708 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10713 * Generate a constructor for a specific record layout.
10714 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10715 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10716 * Each field definition object may contain the following properties: <ul>
10717 * <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,
10718 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10719 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10720 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10721 * is being used, then this is a string containing the javascript expression to reference the data relative to
10722 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10723 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10724 * this may be omitted.</p></li>
10725 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10726 * <ul><li>auto (Default, implies no conversion)</li>
10731 * <li>date</li></ul></p></li>
10732 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10733 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10734 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10735 * by the Reader into an object that will be stored in the Record. It is passed the
10736 * following parameters:<ul>
10737 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10739 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10741 * <br>usage:<br><pre><code>
10742 var TopicRecord = Roo.data.Record.create(
10743 {name: 'title', mapping: 'topic_title'},
10744 {name: 'author', mapping: 'username'},
10745 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10746 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10747 {name: 'lastPoster', mapping: 'user2'},
10748 {name: 'excerpt', mapping: 'post_text'}
10751 var myNewRecord = new TopicRecord({
10752 title: 'Do my job please',
10755 lastPost: new Date(),
10756 lastPoster: 'Animal',
10757 excerpt: 'No way dude!'
10759 myStore.add(myNewRecord);
10764 Roo.data.Record.create = function(o){
10765 var f = function(){
10766 f.superclass.constructor.apply(this, arguments);
10768 Roo.extend(f, Roo.data.Record);
10769 var p = f.prototype;
10770 p.fields = new Roo.util.MixedCollection(false, function(field){
10773 for(var i = 0, len = o.length; i < len; i++){
10774 p.fields.add(new Roo.data.Field(o[i]));
10776 f.getField = function(name){
10777 return p.fields.get(name);
10782 Roo.data.Record.AUTO_ID = 1000;
10783 Roo.data.Record.EDIT = 'edit';
10784 Roo.data.Record.REJECT = 'reject';
10785 Roo.data.Record.COMMIT = 'commit';
10787 Roo.data.Record.prototype = {
10789 * Readonly flag - true if this record has been modified.
10798 join : function(store){
10799 this.store = store;
10803 * Set the named field to the specified value.
10804 * @param {String} name The name of the field to set.
10805 * @param {Object} value The value to set the field to.
10807 set : function(name, value){
10808 if(this.data[name] == value){
10812 if(!this.modified){
10813 this.modified = {};
10815 if(typeof this.modified[name] == 'undefined'){
10816 this.modified[name] = this.data[name];
10818 this.data[name] = value;
10819 if(!this.editing && this.store){
10820 this.store.afterEdit(this);
10825 * Get the value of the named field.
10826 * @param {String} name The name of the field to get the value of.
10827 * @return {Object} The value of the field.
10829 get : function(name){
10830 return this.data[name];
10834 beginEdit : function(){
10835 this.editing = true;
10836 this.modified = {};
10840 cancelEdit : function(){
10841 this.editing = false;
10842 delete this.modified;
10846 endEdit : function(){
10847 this.editing = false;
10848 if(this.dirty && this.store){
10849 this.store.afterEdit(this);
10854 * Usually called by the {@link Roo.data.Store} which owns the Record.
10855 * Rejects all changes made to the Record since either creation, or the last commit operation.
10856 * Modified fields are reverted to their original values.
10858 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10859 * of reject operations.
10861 reject : function(){
10862 var m = this.modified;
10864 if(typeof m[n] != "function"){
10865 this.data[n] = m[n];
10868 this.dirty = false;
10869 delete this.modified;
10870 this.editing = false;
10872 this.store.afterReject(this);
10877 * Usually called by the {@link Roo.data.Store} which owns the Record.
10878 * Commits all changes made to the Record since either creation, or the last commit operation.
10880 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10881 * of commit operations.
10883 commit : function(){
10884 this.dirty = false;
10885 delete this.modified;
10886 this.editing = false;
10888 this.store.afterCommit(this);
10893 hasError : function(){
10894 return this.error != null;
10898 clearError : function(){
10903 * Creates a copy of this record.
10904 * @param {String} id (optional) A new record id if you don't want to use this record's id
10907 copy : function(newId) {
10908 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10912 * Ext JS Library 1.1.1
10913 * Copyright(c) 2006-2007, Ext JS, LLC.
10915 * Originally Released Under LGPL - original licence link has changed is not relivant.
10918 * <script type="text/javascript">
10924 * @class Roo.data.Store
10925 * @extends Roo.util.Observable
10926 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10927 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10929 * 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
10930 * has no knowledge of the format of the data returned by the Proxy.<br>
10932 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10933 * instances from the data object. These records are cached and made available through accessor functions.
10935 * Creates a new Store.
10936 * @param {Object} config A config object containing the objects needed for the Store to access data,
10937 * and read the data into Records.
10939 Roo.data.Store = function(config){
10940 this.data = new Roo.util.MixedCollection(false);
10941 this.data.getKey = function(o){
10944 this.baseParams = {};
10946 this.paramNames = {
10951 "multisort" : "_multisort"
10954 if(config && config.data){
10955 this.inlineData = config.data;
10956 delete config.data;
10959 Roo.apply(this, config);
10961 if(this.reader){ // reader passed
10962 this.reader = Roo.factory(this.reader, Roo.data);
10963 this.reader.xmodule = this.xmodule || false;
10964 if(!this.recordType){
10965 this.recordType = this.reader.recordType;
10967 if(this.reader.onMetaChange){
10968 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10972 if(this.recordType){
10973 this.fields = this.recordType.prototype.fields;
10975 this.modified = [];
10979 * @event datachanged
10980 * Fires when the data cache has changed, and a widget which is using this Store
10981 * as a Record cache should refresh its view.
10982 * @param {Store} this
10984 datachanged : true,
10986 * @event metachange
10987 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10988 * @param {Store} this
10989 * @param {Object} meta The JSON metadata
10994 * Fires when Records have been added to the Store
10995 * @param {Store} this
10996 * @param {Roo.data.Record[]} records The array of Records added
10997 * @param {Number} index The index at which the record(s) were added
11002 * Fires when a Record has been removed from the Store
11003 * @param {Store} this
11004 * @param {Roo.data.Record} record The Record that was removed
11005 * @param {Number} index The index at which the record was removed
11010 * Fires when a Record has been updated
11011 * @param {Store} this
11012 * @param {Roo.data.Record} record The Record that was updated
11013 * @param {String} operation The update operation being performed. Value may be one of:
11015 Roo.data.Record.EDIT
11016 Roo.data.Record.REJECT
11017 Roo.data.Record.COMMIT
11023 * Fires when the data cache has been cleared.
11024 * @param {Store} this
11028 * @event beforeload
11029 * Fires before a request is made for a new data object. If the beforeload handler returns false
11030 * the load action will be canceled.
11031 * @param {Store} this
11032 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11036 * @event beforeloadadd
11037 * Fires after a new set of Records has been loaded.
11038 * @param {Store} this
11039 * @param {Roo.data.Record[]} records The Records that were loaded
11040 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11042 beforeloadadd : true,
11045 * Fires after a new set of Records has been loaded, before they are added to the store.
11046 * @param {Store} this
11047 * @param {Roo.data.Record[]} records The Records that were loaded
11048 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11049 * @params {Object} return from reader
11053 * @event loadexception
11054 * Fires if an exception occurs in the Proxy during loading.
11055 * Called with the signature of the Proxy's "loadexception" event.
11056 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11059 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11060 * @param {Object} load options
11061 * @param {Object} jsonData from your request (normally this contains the Exception)
11063 loadexception : true
11067 this.proxy = Roo.factory(this.proxy, Roo.data);
11068 this.proxy.xmodule = this.xmodule || false;
11069 this.relayEvents(this.proxy, ["loadexception"]);
11071 this.sortToggle = {};
11072 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11074 Roo.data.Store.superclass.constructor.call(this);
11076 if(this.inlineData){
11077 this.loadData(this.inlineData);
11078 delete this.inlineData;
11082 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11084 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11085 * without a remote query - used by combo/forms at present.
11089 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11092 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11095 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11096 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11099 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11100 * on any HTTP request
11103 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11106 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11110 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11111 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11113 remoteSort : false,
11116 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11117 * loaded or when a record is removed. (defaults to false).
11119 pruneModifiedRecords : false,
11122 lastOptions : null,
11125 * Add Records to the Store and fires the add event.
11126 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11128 add : function(records){
11129 records = [].concat(records);
11130 for(var i = 0, len = records.length; i < len; i++){
11131 records[i].join(this);
11133 var index = this.data.length;
11134 this.data.addAll(records);
11135 this.fireEvent("add", this, records, index);
11139 * Remove a Record from the Store and fires the remove event.
11140 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11142 remove : function(record){
11143 var index = this.data.indexOf(record);
11144 this.data.removeAt(index);
11146 if(this.pruneModifiedRecords){
11147 this.modified.remove(record);
11149 this.fireEvent("remove", this, record, index);
11153 * Remove all Records from the Store and fires the clear event.
11155 removeAll : function(){
11157 if(this.pruneModifiedRecords){
11158 this.modified = [];
11160 this.fireEvent("clear", this);
11164 * Inserts Records to the Store at the given index and fires the add event.
11165 * @param {Number} index The start index at which to insert the passed Records.
11166 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11168 insert : function(index, records){
11169 records = [].concat(records);
11170 for(var i = 0, len = records.length; i < len; i++){
11171 this.data.insert(index, records[i]);
11172 records[i].join(this);
11174 this.fireEvent("add", this, records, index);
11178 * Get the index within the cache of the passed Record.
11179 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11180 * @return {Number} The index of the passed Record. Returns -1 if not found.
11182 indexOf : function(record){
11183 return this.data.indexOf(record);
11187 * Get the index within the cache of the Record with the passed id.
11188 * @param {String} id The id of the Record to find.
11189 * @return {Number} The index of the Record. Returns -1 if not found.
11191 indexOfId : function(id){
11192 return this.data.indexOfKey(id);
11196 * Get the Record with the specified id.
11197 * @param {String} id The id of the Record to find.
11198 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11200 getById : function(id){
11201 return this.data.key(id);
11205 * Get the Record at the specified index.
11206 * @param {Number} index The index of the Record to find.
11207 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11209 getAt : function(index){
11210 return this.data.itemAt(index);
11214 * Returns a range of Records between specified indices.
11215 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11216 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11217 * @return {Roo.data.Record[]} An array of Records
11219 getRange : function(start, end){
11220 return this.data.getRange(start, end);
11224 storeOptions : function(o){
11225 o = Roo.apply({}, o);
11228 this.lastOptions = o;
11232 * Loads the Record cache from the configured Proxy using the configured Reader.
11234 * If using remote paging, then the first load call must specify the <em>start</em>
11235 * and <em>limit</em> properties in the options.params property to establish the initial
11236 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11238 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11239 * and this call will return before the new data has been loaded. Perform any post-processing
11240 * in a callback function, or in a "load" event handler.</strong>
11242 * @param {Object} options An object containing properties which control loading options:<ul>
11243 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11244 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11245 * passed the following arguments:<ul>
11246 * <li>r : Roo.data.Record[]</li>
11247 * <li>options: Options object from the load call</li>
11248 * <li>success: Boolean success indicator</li></ul></li>
11249 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11250 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11253 load : function(options){
11254 options = options || {};
11255 if(this.fireEvent("beforeload", this, options) !== false){
11256 this.storeOptions(options);
11257 var p = Roo.apply(options.params || {}, this.baseParams);
11258 // if meta was not loaded from remote source.. try requesting it.
11259 if (!this.reader.metaFromRemote) {
11260 p._requestMeta = 1;
11262 if(this.sortInfo && this.remoteSort){
11263 var pn = this.paramNames;
11264 p[pn["sort"]] = this.sortInfo.field;
11265 p[pn["dir"]] = this.sortInfo.direction;
11267 if (this.multiSort) {
11268 var pn = this.paramNames;
11269 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11272 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11277 * Reloads the Record cache from the configured Proxy using the configured Reader and
11278 * the options from the last load operation performed.
11279 * @param {Object} options (optional) An object containing properties which may override the options
11280 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11281 * the most recently used options are reused).
11283 reload : function(options){
11284 this.load(Roo.applyIf(options||{}, this.lastOptions));
11288 // Called as a callback by the Reader during a load operation.
11289 loadRecords : function(o, options, success){
11290 if(!o || success === false){
11291 if(success !== false){
11292 this.fireEvent("load", this, [], options, o);
11294 if(options.callback){
11295 options.callback.call(options.scope || this, [], options, false);
11299 // if data returned failure - throw an exception.
11300 if (o.success === false) {
11301 // show a message if no listener is registered.
11302 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11303 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11305 // loadmask wil be hooked into this..
11306 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11309 var r = o.records, t = o.totalRecords || r.length;
11311 this.fireEvent("beforeloadadd", this, r, options, o);
11313 if(!options || options.add !== true){
11314 if(this.pruneModifiedRecords){
11315 this.modified = [];
11317 for(var i = 0, len = r.length; i < len; i++){
11321 this.data = this.snapshot;
11322 delete this.snapshot;
11325 this.data.addAll(r);
11326 this.totalLength = t;
11328 this.fireEvent("datachanged", this);
11330 this.totalLength = Math.max(t, this.data.length+r.length);
11334 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11336 var e = new Roo.data.Record({});
11338 e.set(this.parent.displayField, this.parent.emptyTitle);
11339 e.set(this.parent.valueField, '');
11344 this.fireEvent("load", this, r, options, o);
11345 if(options.callback){
11346 options.callback.call(options.scope || this, r, options, true);
11352 * Loads data from a passed data block. A Reader which understands the format of the data
11353 * must have been configured in the constructor.
11354 * @param {Object} data The data block from which to read the Records. The format of the data expected
11355 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11356 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11358 loadData : function(o, append){
11359 var r = this.reader.readRecords(o);
11360 this.loadRecords(r, {add: append}, true);
11364 * Gets the number of cached records.
11366 * <em>If using paging, this may not be the total size of the dataset. If the data object
11367 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11368 * the data set size</em>
11370 getCount : function(){
11371 return this.data.length || 0;
11375 * Gets the total number of records in the dataset as returned by the server.
11377 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11378 * the dataset size</em>
11380 getTotalCount : function(){
11381 return this.totalLength || 0;
11385 * Returns the sort state of the Store as an object with two properties:
11387 field {String} The name of the field by which the Records are sorted
11388 direction {String} The sort order, "ASC" or "DESC"
11391 getSortState : function(){
11392 return this.sortInfo;
11396 applySort : function(){
11397 if(this.sortInfo && !this.remoteSort){
11398 var s = this.sortInfo, f = s.field;
11399 var st = this.fields.get(f).sortType;
11400 var fn = function(r1, r2){
11401 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11402 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11404 this.data.sort(s.direction, fn);
11405 if(this.snapshot && this.snapshot != this.data){
11406 this.snapshot.sort(s.direction, fn);
11412 * Sets the default sort column and order to be used by the next load operation.
11413 * @param {String} fieldName The name of the field to sort by.
11414 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11416 setDefaultSort : function(field, dir){
11417 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11421 * Sort the Records.
11422 * If remote sorting is used, the sort is performed on the server, and the cache is
11423 * reloaded. If local sorting is used, the cache is sorted internally.
11424 * @param {String} fieldName The name of the field to sort by.
11425 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11427 sort : function(fieldName, dir){
11428 var f = this.fields.get(fieldName);
11430 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11432 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11433 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11438 this.sortToggle[f.name] = dir;
11439 this.sortInfo = {field: f.name, direction: dir};
11440 if(!this.remoteSort){
11442 this.fireEvent("datachanged", this);
11444 this.load(this.lastOptions);
11449 * Calls the specified function for each of the Records in the cache.
11450 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11451 * Returning <em>false</em> aborts and exits the iteration.
11452 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11454 each : function(fn, scope){
11455 this.data.each(fn, scope);
11459 * Gets all records modified since the last commit. Modified records are persisted across load operations
11460 * (e.g., during paging).
11461 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11463 getModifiedRecords : function(){
11464 return this.modified;
11468 createFilterFn : function(property, value, anyMatch){
11469 if(!value.exec){ // not a regex
11470 value = String(value);
11471 if(value.length == 0){
11474 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11476 return function(r){
11477 return value.test(r.data[property]);
11482 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11483 * @param {String} property A field on your records
11484 * @param {Number} start The record index to start at (defaults to 0)
11485 * @param {Number} end The last record index to include (defaults to length - 1)
11486 * @return {Number} The sum
11488 sum : function(property, start, end){
11489 var rs = this.data.items, v = 0;
11490 start = start || 0;
11491 end = (end || end === 0) ? end : rs.length-1;
11493 for(var i = start; i <= end; i++){
11494 v += (rs[i].data[property] || 0);
11500 * Filter the records by a specified property.
11501 * @param {String} field A field on your records
11502 * @param {String/RegExp} value Either a string that the field
11503 * should start with or a RegExp to test against the field
11504 * @param {Boolean} anyMatch True to match any part not just the beginning
11506 filter : function(property, value, anyMatch){
11507 var fn = this.createFilterFn(property, value, anyMatch);
11508 return fn ? this.filterBy(fn) : this.clearFilter();
11512 * Filter by a function. The specified function will be called with each
11513 * record in this data source. If the function returns true the record is included,
11514 * otherwise it is filtered.
11515 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11516 * @param {Object} scope (optional) The scope of the function (defaults to this)
11518 filterBy : function(fn, scope){
11519 this.snapshot = this.snapshot || this.data;
11520 this.data = this.queryBy(fn, scope||this);
11521 this.fireEvent("datachanged", this);
11525 * Query the records by a specified property.
11526 * @param {String} field A field on your records
11527 * @param {String/RegExp} value Either a string that the field
11528 * should start with or a RegExp to test against the field
11529 * @param {Boolean} anyMatch True to match any part not just the beginning
11530 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11532 query : function(property, value, anyMatch){
11533 var fn = this.createFilterFn(property, value, anyMatch);
11534 return fn ? this.queryBy(fn) : this.data.clone();
11538 * Query by a function. The specified function will be called with each
11539 * record in this data source. If the function returns true the record is included
11541 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11542 * @param {Object} scope (optional) The scope of the function (defaults to this)
11543 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11545 queryBy : function(fn, scope){
11546 var data = this.snapshot || this.data;
11547 return data.filterBy(fn, scope||this);
11551 * Collects unique values for a particular dataIndex from this store.
11552 * @param {String} dataIndex The property to collect
11553 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11554 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11555 * @return {Array} An array of the unique values
11557 collect : function(dataIndex, allowNull, bypassFilter){
11558 var d = (bypassFilter === true && this.snapshot) ?
11559 this.snapshot.items : this.data.items;
11560 var v, sv, r = [], l = {};
11561 for(var i = 0, len = d.length; i < len; i++){
11562 v = d[i].data[dataIndex];
11564 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11573 * Revert to a view of the Record cache with no filtering applied.
11574 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11576 clearFilter : function(suppressEvent){
11577 if(this.snapshot && this.snapshot != this.data){
11578 this.data = this.snapshot;
11579 delete this.snapshot;
11580 if(suppressEvent !== true){
11581 this.fireEvent("datachanged", this);
11587 afterEdit : function(record){
11588 if(this.modified.indexOf(record) == -1){
11589 this.modified.push(record);
11591 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11595 afterReject : function(record){
11596 this.modified.remove(record);
11597 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11601 afterCommit : function(record){
11602 this.modified.remove(record);
11603 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11607 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11608 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11610 commitChanges : function(){
11611 var m = this.modified.slice(0);
11612 this.modified = [];
11613 for(var i = 0, len = m.length; i < len; i++){
11619 * Cancel outstanding changes on all changed records.
11621 rejectChanges : function(){
11622 var m = this.modified.slice(0);
11623 this.modified = [];
11624 for(var i = 0, len = m.length; i < len; i++){
11629 onMetaChange : function(meta, rtype, o){
11630 this.recordType = rtype;
11631 this.fields = rtype.prototype.fields;
11632 delete this.snapshot;
11633 this.sortInfo = meta.sortInfo || this.sortInfo;
11634 this.modified = [];
11635 this.fireEvent('metachange', this, this.reader.meta);
11638 moveIndex : function(data, type)
11640 var index = this.indexOf(data);
11642 var newIndex = index + type;
11646 this.insert(newIndex, data);
11651 * Ext JS Library 1.1.1
11652 * Copyright(c) 2006-2007, Ext JS, LLC.
11654 * Originally Released Under LGPL - original licence link has changed is not relivant.
11657 * <script type="text/javascript">
11661 * @class Roo.data.SimpleStore
11662 * @extends Roo.data.Store
11663 * Small helper class to make creating Stores from Array data easier.
11664 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11665 * @cfg {Array} fields An array of field definition objects, or field name strings.
11666 * @cfg {Array} data The multi-dimensional array of data
11668 * @param {Object} config
11670 Roo.data.SimpleStore = function(config){
11671 Roo.data.SimpleStore.superclass.constructor.call(this, {
11673 reader: new Roo.data.ArrayReader({
11676 Roo.data.Record.create(config.fields)
11678 proxy : new Roo.data.MemoryProxy(config.data)
11682 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11684 * Ext JS Library 1.1.1
11685 * Copyright(c) 2006-2007, Ext JS, LLC.
11687 * Originally Released Under LGPL - original licence link has changed is not relivant.
11690 * <script type="text/javascript">
11695 * @extends Roo.data.Store
11696 * @class Roo.data.JsonStore
11697 * Small helper class to make creating Stores for JSON data easier. <br/>
11699 var store = new Roo.data.JsonStore({
11700 url: 'get-images.php',
11702 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11705 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11706 * JsonReader and HttpProxy (unless inline data is provided).</b>
11707 * @cfg {Array} fields An array of field definition objects, or field name strings.
11709 * @param {Object} config
11711 Roo.data.JsonStore = function(c){
11712 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11713 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11714 reader: new Roo.data.JsonReader(c, c.fields)
11717 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11719 * Ext JS Library 1.1.1
11720 * Copyright(c) 2006-2007, Ext JS, LLC.
11722 * Originally Released Under LGPL - original licence link has changed is not relivant.
11725 * <script type="text/javascript">
11729 Roo.data.Field = function(config){
11730 if(typeof config == "string"){
11731 config = {name: config};
11733 Roo.apply(this, config);
11736 this.type = "auto";
11739 var st = Roo.data.SortTypes;
11740 // named sortTypes are supported, here we look them up
11741 if(typeof this.sortType == "string"){
11742 this.sortType = st[this.sortType];
11745 // set default sortType for strings and dates
11746 if(!this.sortType){
11749 this.sortType = st.asUCString;
11752 this.sortType = st.asDate;
11755 this.sortType = st.none;
11760 var stripRe = /[\$,%]/g;
11762 // prebuilt conversion function for this field, instead of
11763 // switching every time we're reading a value
11765 var cv, dateFormat = this.dateFormat;
11770 cv = function(v){ return v; };
11773 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11777 return v !== undefined && v !== null && v !== '' ?
11778 parseInt(String(v).replace(stripRe, ""), 10) : '';
11783 return v !== undefined && v !== null && v !== '' ?
11784 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11789 cv = function(v){ return v === true || v === "true" || v == 1; };
11796 if(v instanceof Date){
11800 if(dateFormat == "timestamp"){
11801 return new Date(v*1000);
11803 return Date.parseDate(v, dateFormat);
11805 var parsed = Date.parse(v);
11806 return parsed ? new Date(parsed) : null;
11815 Roo.data.Field.prototype = {
11823 * Ext JS Library 1.1.1
11824 * Copyright(c) 2006-2007, Ext JS, LLC.
11826 * Originally Released Under LGPL - original licence link has changed is not relivant.
11829 * <script type="text/javascript">
11832 // Base class for reading structured data from a data source. This class is intended to be
11833 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11836 * @class Roo.data.DataReader
11837 * Base class for reading structured data from a data source. This class is intended to be
11838 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11841 Roo.data.DataReader = function(meta, recordType){
11845 this.recordType = recordType instanceof Array ?
11846 Roo.data.Record.create(recordType) : recordType;
11849 Roo.data.DataReader.prototype = {
11851 * Create an empty record
11852 * @param {Object} data (optional) - overlay some values
11853 * @return {Roo.data.Record} record created.
11855 newRow : function(d) {
11857 this.recordType.prototype.fields.each(function(c) {
11859 case 'int' : da[c.name] = 0; break;
11860 case 'date' : da[c.name] = new Date(); break;
11861 case 'float' : da[c.name] = 0.0; break;
11862 case 'boolean' : da[c.name] = false; break;
11863 default : da[c.name] = ""; break;
11867 return new this.recordType(Roo.apply(da, d));
11872 * Ext JS Library 1.1.1
11873 * Copyright(c) 2006-2007, Ext JS, LLC.
11875 * Originally Released Under LGPL - original licence link has changed is not relivant.
11878 * <script type="text/javascript">
11882 * @class Roo.data.DataProxy
11883 * @extends Roo.data.Observable
11884 * This class is an abstract base class for implementations which provide retrieval of
11885 * unformatted data objects.<br>
11887 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11888 * (of the appropriate type which knows how to parse the data object) to provide a block of
11889 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11891 * Custom implementations must implement the load method as described in
11892 * {@link Roo.data.HttpProxy#load}.
11894 Roo.data.DataProxy = function(){
11897 * @event beforeload
11898 * Fires before a network request is made to retrieve a data object.
11899 * @param {Object} This DataProxy object.
11900 * @param {Object} params The params parameter to the load function.
11905 * Fires before the load method's callback is called.
11906 * @param {Object} This DataProxy object.
11907 * @param {Object} o The data object.
11908 * @param {Object} arg The callback argument object passed to the load function.
11912 * @event loadexception
11913 * Fires if an Exception occurs during data retrieval.
11914 * @param {Object} This DataProxy object.
11915 * @param {Object} o The data object.
11916 * @param {Object} arg The callback argument object passed to the load function.
11917 * @param {Object} e The Exception.
11919 loadexception : true
11921 Roo.data.DataProxy.superclass.constructor.call(this);
11924 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11927 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11931 * Ext JS Library 1.1.1
11932 * Copyright(c) 2006-2007, Ext JS, LLC.
11934 * Originally Released Under LGPL - original licence link has changed is not relivant.
11937 * <script type="text/javascript">
11940 * @class Roo.data.MemoryProxy
11941 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11942 * to the Reader when its load method is called.
11944 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11946 Roo.data.MemoryProxy = function(data){
11950 Roo.data.MemoryProxy.superclass.constructor.call(this);
11954 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11957 * Load data from the requested source (in this case an in-memory
11958 * data object passed to the constructor), read the data object into
11959 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11960 * process that block using the passed callback.
11961 * @param {Object} params This parameter is not used by the MemoryProxy class.
11962 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11963 * object into a block of Roo.data.Records.
11964 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11965 * The function must be passed <ul>
11966 * <li>The Record block object</li>
11967 * <li>The "arg" argument from the load function</li>
11968 * <li>A boolean success indicator</li>
11970 * @param {Object} scope The scope in which to call the callback
11971 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11973 load : function(params, reader, callback, scope, arg){
11974 params = params || {};
11977 result = reader.readRecords(this.data);
11979 this.fireEvent("loadexception", this, arg, null, e);
11980 callback.call(scope, null, arg, false);
11983 callback.call(scope, result, arg, true);
11987 update : function(params, records){
11992 * Ext JS Library 1.1.1
11993 * Copyright(c) 2006-2007, Ext JS, LLC.
11995 * Originally Released Under LGPL - original licence link has changed is not relivant.
11998 * <script type="text/javascript">
12001 * @class Roo.data.HttpProxy
12002 * @extends Roo.data.DataProxy
12003 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12004 * configured to reference a certain URL.<br><br>
12006 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12007 * from which the running page was served.<br><br>
12009 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12011 * Be aware that to enable the browser to parse an XML document, the server must set
12012 * the Content-Type header in the HTTP response to "text/xml".
12014 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12015 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12016 * will be used to make the request.
12018 Roo.data.HttpProxy = function(conn){
12019 Roo.data.HttpProxy.superclass.constructor.call(this);
12020 // is conn a conn config or a real conn?
12022 this.useAjax = !conn || !conn.events;
12026 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12027 // thse are take from connection...
12030 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12033 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12034 * extra parameters to each request made by this object. (defaults to undefined)
12037 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12038 * to each request made by this object. (defaults to undefined)
12041 * @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)
12044 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12047 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12053 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12057 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12058 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12059 * a finer-grained basis than the DataProxy events.
12061 getConnection : function(){
12062 return this.useAjax ? Roo.Ajax : this.conn;
12066 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12067 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12068 * process that block using the passed callback.
12069 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12070 * for the request to the remote server.
12071 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12072 * object into a block of Roo.data.Records.
12073 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12074 * The function must be passed <ul>
12075 * <li>The Record block object</li>
12076 * <li>The "arg" argument from the load function</li>
12077 * <li>A boolean success indicator</li>
12079 * @param {Object} scope The scope in which to call the callback
12080 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12082 load : function(params, reader, callback, scope, arg){
12083 if(this.fireEvent("beforeload", this, params) !== false){
12085 params : params || {},
12087 callback : callback,
12092 callback : this.loadResponse,
12096 Roo.applyIf(o, this.conn);
12097 if(this.activeRequest){
12098 Roo.Ajax.abort(this.activeRequest);
12100 this.activeRequest = Roo.Ajax.request(o);
12102 this.conn.request(o);
12105 callback.call(scope||this, null, arg, false);
12110 loadResponse : function(o, success, response){
12111 delete this.activeRequest;
12113 this.fireEvent("loadexception", this, o, response);
12114 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12119 result = o.reader.read(response);
12121 this.fireEvent("loadexception", this, o, response, e);
12122 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12126 this.fireEvent("load", this, o, o.request.arg);
12127 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12131 update : function(dataSet){
12136 updateResponse : function(dataSet){
12141 * Ext JS Library 1.1.1
12142 * Copyright(c) 2006-2007, Ext JS, LLC.
12144 * Originally Released Under LGPL - original licence link has changed is not relivant.
12147 * <script type="text/javascript">
12151 * @class Roo.data.ScriptTagProxy
12152 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12153 * other than the originating domain of the running page.<br><br>
12155 * <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
12156 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12158 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12159 * source code that is used as the source inside a <script> tag.<br><br>
12161 * In order for the browser to process the returned data, the server must wrap the data object
12162 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12163 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12164 * depending on whether the callback name was passed:
12167 boolean scriptTag = false;
12168 String cb = request.getParameter("callback");
12171 response.setContentType("text/javascript");
12173 response.setContentType("application/x-json");
12175 Writer out = response.getWriter();
12177 out.write(cb + "(");
12179 out.print(dataBlock.toJsonString());
12186 * @param {Object} config A configuration object.
12188 Roo.data.ScriptTagProxy = function(config){
12189 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12190 Roo.apply(this, config);
12191 this.head = document.getElementsByTagName("head")[0];
12194 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12196 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12198 * @cfg {String} url The URL from which to request the data object.
12201 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12205 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12206 * the server the name of the callback function set up by the load call to process the returned data object.
12207 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12208 * javascript output which calls this named function passing the data object as its only parameter.
12210 callbackParam : "callback",
12212 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12213 * name to the request.
12218 * Load data from the configured URL, read the data object into
12219 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12220 * process that block using the passed callback.
12221 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12222 * for the request to the remote server.
12223 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12224 * object into a block of Roo.data.Records.
12225 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12226 * The function must be passed <ul>
12227 * <li>The Record block object</li>
12228 * <li>The "arg" argument from the load function</li>
12229 * <li>A boolean success indicator</li>
12231 * @param {Object} scope The scope in which to call the callback
12232 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12234 load : function(params, reader, callback, scope, arg){
12235 if(this.fireEvent("beforeload", this, params) !== false){
12237 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12239 var url = this.url;
12240 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12242 url += "&_dc=" + (new Date().getTime());
12244 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12247 cb : "stcCallback"+transId,
12248 scriptId : "stcScript"+transId,
12252 callback : callback,
12258 window[trans.cb] = function(o){
12259 conn.handleResponse(o, trans);
12262 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12264 if(this.autoAbort !== false){
12268 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12270 var script = document.createElement("script");
12271 script.setAttribute("src", url);
12272 script.setAttribute("type", "text/javascript");
12273 script.setAttribute("id", trans.scriptId);
12274 this.head.appendChild(script);
12276 this.trans = trans;
12278 callback.call(scope||this, null, arg, false);
12283 isLoading : function(){
12284 return this.trans ? true : false;
12288 * Abort the current server request.
12290 abort : function(){
12291 if(this.isLoading()){
12292 this.destroyTrans(this.trans);
12297 destroyTrans : function(trans, isLoaded){
12298 this.head.removeChild(document.getElementById(trans.scriptId));
12299 clearTimeout(trans.timeoutId);
12301 window[trans.cb] = undefined;
12303 delete window[trans.cb];
12306 // if hasn't been loaded, wait for load to remove it to prevent script error
12307 window[trans.cb] = function(){
12308 window[trans.cb] = undefined;
12310 delete window[trans.cb];
12317 handleResponse : function(o, trans){
12318 this.trans = false;
12319 this.destroyTrans(trans, true);
12322 result = trans.reader.readRecords(o);
12324 this.fireEvent("loadexception", this, o, trans.arg, e);
12325 trans.callback.call(trans.scope||window, null, trans.arg, false);
12328 this.fireEvent("load", this, o, trans.arg);
12329 trans.callback.call(trans.scope||window, result, trans.arg, true);
12333 handleFailure : function(trans){
12334 this.trans = false;
12335 this.destroyTrans(trans, false);
12336 this.fireEvent("loadexception", this, null, trans.arg);
12337 trans.callback.call(trans.scope||window, null, trans.arg, false);
12341 * Ext JS Library 1.1.1
12342 * Copyright(c) 2006-2007, Ext JS, LLC.
12344 * Originally Released Under LGPL - original licence link has changed is not relivant.
12347 * <script type="text/javascript">
12351 * @class Roo.data.JsonReader
12352 * @extends Roo.data.DataReader
12353 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12354 * based on mappings in a provided Roo.data.Record constructor.
12356 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12357 * in the reply previously.
12362 var RecordDef = Roo.data.Record.create([
12363 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12364 {name: 'occupation'} // This field will use "occupation" as the mapping.
12366 var myReader = new Roo.data.JsonReader({
12367 totalProperty: "results", // The property which contains the total dataset size (optional)
12368 root: "rows", // The property which contains an Array of row objects
12369 id: "id" // The property within each row object that provides an ID for the record (optional)
12373 * This would consume a JSON file like this:
12375 { 'results': 2, 'rows': [
12376 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12377 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12380 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12381 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12382 * paged from the remote server.
12383 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12384 * @cfg {String} root name of the property which contains the Array of row objects.
12385 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12386 * @cfg {Array} fields Array of field definition objects
12388 * Create a new JsonReader
12389 * @param {Object} meta Metadata configuration options
12390 * @param {Object} recordType Either an Array of field definition objects,
12391 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12393 Roo.data.JsonReader = function(meta, recordType){
12396 // set some defaults:
12397 Roo.applyIf(meta, {
12398 totalProperty: 'total',
12399 successProperty : 'success',
12404 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12406 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12409 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12410 * Used by Store query builder to append _requestMeta to params.
12413 metaFromRemote : false,
12415 * This method is only used by a DataProxy which has retrieved data from a remote server.
12416 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12417 * @return {Object} data A data block which is used by an Roo.data.Store object as
12418 * a cache of Roo.data.Records.
12420 read : function(response){
12421 var json = response.responseText;
12423 var o = /* eval:var:o */ eval("("+json+")");
12425 throw {message: "JsonReader.read: Json object not found"};
12431 this.metaFromRemote = true;
12432 this.meta = o.metaData;
12433 this.recordType = Roo.data.Record.create(o.metaData.fields);
12434 this.onMetaChange(this.meta, this.recordType, o);
12436 return this.readRecords(o);
12439 // private function a store will implement
12440 onMetaChange : function(meta, recordType, o){
12447 simpleAccess: function(obj, subsc) {
12454 getJsonAccessor: function(){
12456 return function(expr) {
12458 return(re.test(expr))
12459 ? new Function("obj", "return obj." + expr)
12464 return Roo.emptyFn;
12469 * Create a data block containing Roo.data.Records from an XML document.
12470 * @param {Object} o An object which contains an Array of row objects in the property specified
12471 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12472 * which contains the total size of the dataset.
12473 * @return {Object} data A data block which is used by an Roo.data.Store object as
12474 * a cache of Roo.data.Records.
12476 readRecords : function(o){
12478 * After any data loads, the raw JSON data is available for further custom processing.
12482 var s = this.meta, Record = this.recordType,
12483 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12485 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12487 if(s.totalProperty) {
12488 this.getTotal = this.getJsonAccessor(s.totalProperty);
12490 if(s.successProperty) {
12491 this.getSuccess = this.getJsonAccessor(s.successProperty);
12493 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12495 var g = this.getJsonAccessor(s.id);
12496 this.getId = function(rec) {
12498 return (r === undefined || r === "") ? null : r;
12501 this.getId = function(){return null;};
12504 for(var jj = 0; jj < fl; jj++){
12506 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12507 this.ef[jj] = this.getJsonAccessor(map);
12511 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12512 if(s.totalProperty){
12513 var vt = parseInt(this.getTotal(o), 10);
12518 if(s.successProperty){
12519 var vs = this.getSuccess(o);
12520 if(vs === false || vs === 'false'){
12525 for(var i = 0; i < c; i++){
12528 var id = this.getId(n);
12529 for(var j = 0; j < fl; j++){
12531 var v = this.ef[j](n);
12533 Roo.log('missing convert for ' + f.name);
12537 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12539 var record = new Record(values, id);
12541 records[i] = record;
12547 totalRecords : totalRecords
12552 * Ext JS Library 1.1.1
12553 * Copyright(c) 2006-2007, Ext JS, LLC.
12555 * Originally Released Under LGPL - original licence link has changed is not relivant.
12558 * <script type="text/javascript">
12562 * @class Roo.data.ArrayReader
12563 * @extends Roo.data.DataReader
12564 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12565 * Each element of that Array represents a row of data fields. The
12566 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12567 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12571 var RecordDef = Roo.data.Record.create([
12572 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12573 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12575 var myReader = new Roo.data.ArrayReader({
12576 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12580 * This would consume an Array like this:
12582 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12584 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12586 * Create a new JsonReader
12587 * @param {Object} meta Metadata configuration options.
12588 * @param {Object} recordType Either an Array of field definition objects
12589 * as specified to {@link Roo.data.Record#create},
12590 * or an {@link Roo.data.Record} object
12591 * created using {@link Roo.data.Record#create}.
12593 Roo.data.ArrayReader = function(meta, recordType){
12594 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12597 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12599 * Create a data block containing Roo.data.Records from an XML document.
12600 * @param {Object} o An Array of row objects which represents the dataset.
12601 * @return {Object} data A data block which is used by an Roo.data.Store object as
12602 * a cache of Roo.data.Records.
12604 readRecords : function(o){
12605 var sid = this.meta ? this.meta.id : null;
12606 var recordType = this.recordType, fields = recordType.prototype.fields;
12609 for(var i = 0; i < root.length; i++){
12612 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12613 for(var j = 0, jlen = fields.length; j < jlen; j++){
12614 var f = fields.items[j];
12615 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12616 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12618 values[f.name] = v;
12620 var record = new recordType(values, id);
12622 records[records.length] = record;
12626 totalRecords : records.length
12635 * @class Roo.bootstrap.ComboBox
12636 * @extends Roo.bootstrap.TriggerField
12637 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12638 * @cfg {Boolean} append (true|false) default false
12639 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12640 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12641 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12642 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12643 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12644 * @cfg {Boolean} animate default true
12645 * @cfg {Boolean} emptyResultText only for touch device
12646 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12647 * @cfg {String} emptyTitle default ''
12649 * Create a new ComboBox.
12650 * @param {Object} config Configuration options
12652 Roo.bootstrap.ComboBox = function(config){
12653 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12657 * Fires when the dropdown list is expanded
12658 * @param {Roo.bootstrap.ComboBox} combo This combo box
12663 * Fires when the dropdown list is collapsed
12664 * @param {Roo.bootstrap.ComboBox} combo This combo box
12668 * @event beforeselect
12669 * Fires before a list item is selected. Return false to cancel the selection.
12670 * @param {Roo.bootstrap.ComboBox} combo This combo box
12671 * @param {Roo.data.Record} record The data record returned from the underlying store
12672 * @param {Number} index The index of the selected item in the dropdown list
12674 'beforeselect' : true,
12677 * Fires when a list item is selected
12678 * @param {Roo.bootstrap.ComboBox} combo This combo box
12679 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12680 * @param {Number} index The index of the selected item in the dropdown list
12684 * @event beforequery
12685 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12686 * The event object passed has these properties:
12687 * @param {Roo.bootstrap.ComboBox} combo This combo box
12688 * @param {String} query The query
12689 * @param {Boolean} forceAll true to force "all" query
12690 * @param {Boolean} cancel true to cancel the query
12691 * @param {Object} e The query event object
12693 'beforequery': true,
12696 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12697 * @param {Roo.bootstrap.ComboBox} combo This combo box
12702 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12703 * @param {Roo.bootstrap.ComboBox} combo This combo box
12704 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12709 * Fires when the remove value from the combobox array
12710 * @param {Roo.bootstrap.ComboBox} combo This combo box
12714 * @event afterremove
12715 * Fires when the remove value from the combobox array
12716 * @param {Roo.bootstrap.ComboBox} combo This combo box
12718 'afterremove' : true,
12720 * @event specialfilter
12721 * Fires when specialfilter
12722 * @param {Roo.bootstrap.ComboBox} combo This combo box
12724 'specialfilter' : true,
12727 * Fires when tick the element
12728 * @param {Roo.bootstrap.ComboBox} combo This combo box
12732 * @event touchviewdisplay
12733 * Fires when touch view require special display (default is using displayField)
12734 * @param {Roo.bootstrap.ComboBox} combo This combo box
12735 * @param {Object} cfg set html .
12737 'touchviewdisplay' : true
12742 this.tickItems = [];
12744 this.selectedIndex = -1;
12745 if(this.mode == 'local'){
12746 if(config.queryDelay === undefined){
12747 this.queryDelay = 10;
12749 if(config.minChars === undefined){
12755 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12758 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12759 * rendering into an Roo.Editor, defaults to false)
12762 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12763 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12766 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12769 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12770 * the dropdown list (defaults to undefined, with no header element)
12774 * @cfg {String/Roo.Template} tpl The template to use to render the output
12778 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12780 listWidth: undefined,
12782 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12783 * mode = 'remote' or 'text' if mode = 'local')
12785 displayField: undefined,
12788 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12789 * mode = 'remote' or 'value' if mode = 'local').
12790 * Note: use of a valueField requires the user make a selection
12791 * in order for a value to be mapped.
12793 valueField: undefined,
12795 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12800 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12801 * field's data value (defaults to the underlying DOM element's name)
12803 hiddenName: undefined,
12805 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12809 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12811 selectedClass: 'active',
12814 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12818 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12819 * anchor positions (defaults to 'tl-bl')
12821 listAlign: 'tl-bl?',
12823 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12827 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12828 * query specified by the allQuery config option (defaults to 'query')
12830 triggerAction: 'query',
12832 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12833 * (defaults to 4, does not apply if editable = false)
12837 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12838 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12842 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12843 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12847 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12848 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12852 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12853 * when editable = true (defaults to false)
12855 selectOnFocus:false,
12857 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12859 queryParam: 'query',
12861 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12862 * when mode = 'remote' (defaults to 'Loading...')
12864 loadingText: 'Loading...',
12866 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12870 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12874 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12875 * traditional select (defaults to true)
12879 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12883 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12887 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12888 * listWidth has a higher value)
12892 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12893 * allow the user to set arbitrary text into the field (defaults to false)
12895 forceSelection:false,
12897 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12898 * if typeAhead = true (defaults to 250)
12900 typeAheadDelay : 250,
12902 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12903 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12905 valueNotFoundText : undefined,
12907 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12909 blockFocus : false,
12912 * @cfg {Boolean} disableClear Disable showing of clear button.
12914 disableClear : false,
12916 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12918 alwaysQuery : false,
12921 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12926 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12928 invalidClass : "has-warning",
12931 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12933 validClass : "has-success",
12936 * @cfg {Boolean} specialFilter (true|false) special filter default false
12938 specialFilter : false,
12941 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12943 mobileTouchView : true,
12946 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12948 useNativeIOS : false,
12950 ios_options : false,
12962 btnPosition : 'right',
12963 triggerList : true,
12964 showToggleBtn : true,
12966 emptyResultText: 'Empty',
12967 triggerText : 'Select',
12970 // element that contains real text value.. (when hidden is used..)
12972 getAutoCreate : function()
12977 * Render classic select for iso
12980 if(Roo.isIOS && this.useNativeIOS){
12981 cfg = this.getAutoCreateNativeIOS();
12989 if(Roo.isTouch && this.mobileTouchView){
12990 cfg = this.getAutoCreateTouchView();
12997 if(!this.tickable){
12998 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13003 * ComboBox with tickable selections
13006 var align = this.labelAlign || this.parentLabelAlign();
13009 cls : 'form-group roo-combobox-tickable' //input-group
13012 var btn_text_select = '';
13013 var btn_text_done = '';
13014 var btn_text_cancel = '';
13016 if (this.btn_text_show) {
13017 btn_text_select = 'Select';
13018 btn_text_done = 'Done';
13019 btn_text_cancel = 'Cancel';
13024 cls : 'tickable-buttons',
13029 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13030 //html : this.triggerText
13031 html: btn_text_select
13037 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13039 html: btn_text_done
13045 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13047 html: btn_text_cancel
13053 buttons.cn.unshift({
13055 cls: 'roo-select2-search-field-input'
13061 Roo.each(buttons.cn, function(c){
13063 c.cls += ' btn-' + _this.size;
13066 if (_this.disabled) {
13077 cls: 'form-hidden-field'
13081 cls: 'roo-select2-choices',
13085 cls: 'roo-select2-search-field',
13096 cls: 'roo-select2-container input-group roo-select2-container-multi',
13101 // cls: 'typeahead typeahead-long dropdown-menu',
13102 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13107 if(this.hasFeedback && !this.allowBlank){
13111 cls: 'glyphicon form-control-feedback'
13114 combobox.cn.push(feedback);
13118 if (align ==='left' && this.fieldLabel.length) {
13120 cfg.cls += ' roo-form-group-label-left';
13125 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13126 tooltip : 'This field is required'
13131 cls : 'control-label',
13132 html : this.fieldLabel
13144 var labelCfg = cfg.cn[1];
13145 var contentCfg = cfg.cn[2];
13148 if(this.indicatorpos == 'right'){
13154 cls : 'control-label',
13158 html : this.fieldLabel
13162 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13163 tooltip : 'This field is required'
13178 labelCfg = cfg.cn[0];
13179 contentCfg = cfg.cn[1];
13183 if(this.labelWidth > 12){
13184 labelCfg.style = "width: " + this.labelWidth + 'px';
13187 if(this.labelWidth < 13 && this.labelmd == 0){
13188 this.labelmd = this.labelWidth;
13191 if(this.labellg > 0){
13192 labelCfg.cls += ' col-lg-' + this.labellg;
13193 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13196 if(this.labelmd > 0){
13197 labelCfg.cls += ' col-md-' + this.labelmd;
13198 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13201 if(this.labelsm > 0){
13202 labelCfg.cls += ' col-sm-' + this.labelsm;
13203 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13206 if(this.labelxs > 0){
13207 labelCfg.cls += ' col-xs-' + this.labelxs;
13208 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13212 } else if ( this.fieldLabel.length) {
13213 // Roo.log(" label");
13217 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13218 tooltip : 'This field is required'
13222 //cls : 'input-group-addon',
13223 html : this.fieldLabel
13228 if(this.indicatorpos == 'right'){
13232 //cls : 'input-group-addon',
13233 html : this.fieldLabel
13237 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13238 tooltip : 'This field is required'
13247 // Roo.log(" no label && no align");
13254 ['xs','sm','md','lg'].map(function(size){
13255 if (settings[size]) {
13256 cfg.cls += ' col-' + size + '-' + settings[size];
13264 _initEventsCalled : false,
13267 initEvents: function()
13269 if (this._initEventsCalled) { // as we call render... prevent looping...
13272 this._initEventsCalled = true;
13275 throw "can not find store for combo";
13278 this.indicator = this.indicatorEl();
13280 this.store = Roo.factory(this.store, Roo.data);
13281 this.store.parent = this;
13283 // if we are building from html. then this element is so complex, that we can not really
13284 // use the rendered HTML.
13285 // so we have to trash and replace the previous code.
13286 if (Roo.XComponent.build_from_html) {
13287 // remove this element....
13288 var e = this.el.dom, k=0;
13289 while (e ) { e = e.previousSibling; ++k;}
13294 this.rendered = false;
13296 this.render(this.parent().getChildContainer(true), k);
13299 if(Roo.isIOS && this.useNativeIOS){
13300 this.initIOSView();
13308 if(Roo.isTouch && this.mobileTouchView){
13309 this.initTouchView();
13314 this.initTickableEvents();
13318 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13320 if(this.hiddenName){
13322 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13324 this.hiddenField.dom.value =
13325 this.hiddenValue !== undefined ? this.hiddenValue :
13326 this.value !== undefined ? this.value : '';
13328 // prevent input submission
13329 this.el.dom.removeAttribute('name');
13330 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13335 // this.el.dom.setAttribute('autocomplete', 'off');
13338 var cls = 'x-combo-list';
13340 //this.list = new Roo.Layer({
13341 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13347 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13348 _this.list.setWidth(lw);
13351 this.list.on('mouseover', this.onViewOver, this);
13352 this.list.on('mousemove', this.onViewMove, this);
13353 this.list.on('scroll', this.onViewScroll, this);
13356 this.list.swallowEvent('mousewheel');
13357 this.assetHeight = 0;
13360 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13361 this.assetHeight += this.header.getHeight();
13364 this.innerList = this.list.createChild({cls:cls+'-inner'});
13365 this.innerList.on('mouseover', this.onViewOver, this);
13366 this.innerList.on('mousemove', this.onViewMove, this);
13367 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13369 if(this.allowBlank && !this.pageSize && !this.disableClear){
13370 this.footer = this.list.createChild({cls:cls+'-ft'});
13371 this.pageTb = new Roo.Toolbar(this.footer);
13375 this.footer = this.list.createChild({cls:cls+'-ft'});
13376 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13377 {pageSize: this.pageSize});
13381 if (this.pageTb && this.allowBlank && !this.disableClear) {
13383 this.pageTb.add(new Roo.Toolbar.Fill(), {
13384 cls: 'x-btn-icon x-btn-clear',
13386 handler: function()
13389 _this.clearValue();
13390 _this.onSelect(false, -1);
13395 this.assetHeight += this.footer.getHeight();
13400 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13403 this.view = new Roo.View(this.list, this.tpl, {
13404 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13406 //this.view.wrapEl.setDisplayed(false);
13407 this.view.on('click', this.onViewClick, this);
13410 this.store.on('beforeload', this.onBeforeLoad, this);
13411 this.store.on('load', this.onLoad, this);
13412 this.store.on('loadexception', this.onLoadException, this);
13414 if(this.resizable){
13415 this.resizer = new Roo.Resizable(this.list, {
13416 pinned:true, handles:'se'
13418 this.resizer.on('resize', function(r, w, h){
13419 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13420 this.listWidth = w;
13421 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13422 this.restrictHeight();
13424 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13427 if(!this.editable){
13428 this.editable = true;
13429 this.setEditable(false);
13434 if (typeof(this.events.add.listeners) != 'undefined') {
13436 this.addicon = this.wrap.createChild(
13437 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13439 this.addicon.on('click', function(e) {
13440 this.fireEvent('add', this);
13443 if (typeof(this.events.edit.listeners) != 'undefined') {
13445 this.editicon = this.wrap.createChild(
13446 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13447 if (this.addicon) {
13448 this.editicon.setStyle('margin-left', '40px');
13450 this.editicon.on('click', function(e) {
13452 // we fire even if inothing is selected..
13453 this.fireEvent('edit', this, this.lastData );
13459 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13460 "up" : function(e){
13461 this.inKeyMode = true;
13465 "down" : function(e){
13466 if(!this.isExpanded()){
13467 this.onTriggerClick();
13469 this.inKeyMode = true;
13474 "enter" : function(e){
13475 // this.onViewClick();
13479 if(this.fireEvent("specialkey", this, e)){
13480 this.onViewClick(false);
13486 "esc" : function(e){
13490 "tab" : function(e){
13493 if(this.fireEvent("specialkey", this, e)){
13494 this.onViewClick(false);
13502 doRelay : function(foo, bar, hname){
13503 if(hname == 'down' || this.scope.isExpanded()){
13504 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13513 this.queryDelay = Math.max(this.queryDelay || 10,
13514 this.mode == 'local' ? 10 : 250);
13517 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13519 if(this.typeAhead){
13520 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13522 if(this.editable !== false){
13523 this.inputEl().on("keyup", this.onKeyUp, this);
13525 if(this.forceSelection){
13526 this.inputEl().on('blur', this.doForce, this);
13530 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13531 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13535 initTickableEvents: function()
13539 if(this.hiddenName){
13541 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13543 this.hiddenField.dom.value =
13544 this.hiddenValue !== undefined ? this.hiddenValue :
13545 this.value !== undefined ? this.value : '';
13547 // prevent input submission
13548 this.el.dom.removeAttribute('name');
13549 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13554 // this.list = this.el.select('ul.dropdown-menu',true).first();
13556 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13557 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13558 if(this.triggerList){
13559 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13562 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13563 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13565 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13566 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13568 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13569 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13571 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13572 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13573 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13576 this.cancelBtn.hide();
13581 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13582 _this.list.setWidth(lw);
13585 this.list.on('mouseover', this.onViewOver, this);
13586 this.list.on('mousemove', this.onViewMove, this);
13588 this.list.on('scroll', this.onViewScroll, this);
13591 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>';
13594 this.view = new Roo.View(this.list, this.tpl, {
13595 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13598 //this.view.wrapEl.setDisplayed(false);
13599 this.view.on('click', this.onViewClick, this);
13603 this.store.on('beforeload', this.onBeforeLoad, this);
13604 this.store.on('load', this.onLoad, this);
13605 this.store.on('loadexception', this.onLoadException, this);
13608 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13609 "up" : function(e){
13610 this.inKeyMode = true;
13614 "down" : function(e){
13615 this.inKeyMode = true;
13619 "enter" : function(e){
13620 if(this.fireEvent("specialkey", this, e)){
13621 this.onViewClick(false);
13627 "esc" : function(e){
13628 this.onTickableFooterButtonClick(e, false, false);
13631 "tab" : function(e){
13632 this.fireEvent("specialkey", this, e);
13634 this.onTickableFooterButtonClick(e, false, false);
13641 doRelay : function(e, fn, key){
13642 if(this.scope.isExpanded()){
13643 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13652 this.queryDelay = Math.max(this.queryDelay || 10,
13653 this.mode == 'local' ? 10 : 250);
13656 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13658 if(this.typeAhead){
13659 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13662 if(this.editable !== false){
13663 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13666 this.indicator = this.indicatorEl();
13668 if(this.indicator){
13669 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13670 this.indicator.hide();
13675 onDestroy : function(){
13677 this.view.setStore(null);
13678 this.view.el.removeAllListeners();
13679 this.view.el.remove();
13680 this.view.purgeListeners();
13683 this.list.dom.innerHTML = '';
13687 this.store.un('beforeload', this.onBeforeLoad, this);
13688 this.store.un('load', this.onLoad, this);
13689 this.store.un('loadexception', this.onLoadException, this);
13691 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13695 fireKey : function(e){
13696 if(e.isNavKeyPress() && !this.list.isVisible()){
13697 this.fireEvent("specialkey", this, e);
13702 onResize: function(w, h){
13703 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13705 // if(typeof w != 'number'){
13706 // // we do not handle it!?!?
13709 // var tw = this.trigger.getWidth();
13710 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13711 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13713 // this.inputEl().setWidth( this.adjustWidth('input', x));
13715 // //this.trigger.setStyle('left', x+'px');
13717 // if(this.list && this.listWidth === undefined){
13718 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13719 // this.list.setWidth(lw);
13720 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13728 * Allow or prevent the user from directly editing the field text. If false is passed,
13729 * the user will only be able to select from the items defined in the dropdown list. This method
13730 * is the runtime equivalent of setting the 'editable' config option at config time.
13731 * @param {Boolean} value True to allow the user to directly edit the field text
13733 setEditable : function(value){
13734 if(value == this.editable){
13737 this.editable = value;
13739 this.inputEl().dom.setAttribute('readOnly', true);
13740 this.inputEl().on('mousedown', this.onTriggerClick, this);
13741 this.inputEl().addClass('x-combo-noedit');
13743 this.inputEl().dom.setAttribute('readOnly', false);
13744 this.inputEl().un('mousedown', this.onTriggerClick, this);
13745 this.inputEl().removeClass('x-combo-noedit');
13751 onBeforeLoad : function(combo,opts){
13752 if(!this.hasFocus){
13756 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13758 this.restrictHeight();
13759 this.selectedIndex = -1;
13763 onLoad : function(){
13765 this.hasQuery = false;
13767 if(!this.hasFocus){
13771 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13772 this.loading.hide();
13775 if(this.store.getCount() > 0){
13778 this.restrictHeight();
13779 if(this.lastQuery == this.allQuery){
13780 if(this.editable && !this.tickable){
13781 this.inputEl().dom.select();
13785 !this.selectByValue(this.value, true) &&
13788 !this.store.lastOptions ||
13789 typeof(this.store.lastOptions.add) == 'undefined' ||
13790 this.store.lastOptions.add != true
13793 this.select(0, true);
13796 if(this.autoFocus){
13799 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13800 this.taTask.delay(this.typeAheadDelay);
13804 this.onEmptyResults();
13810 onLoadException : function()
13812 this.hasQuery = false;
13814 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13815 this.loading.hide();
13818 if(this.tickable && this.editable){
13823 // only causes errors at present
13824 //Roo.log(this.store.reader.jsonData);
13825 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13827 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13833 onTypeAhead : function(){
13834 if(this.store.getCount() > 0){
13835 var r = this.store.getAt(0);
13836 var newValue = r.data[this.displayField];
13837 var len = newValue.length;
13838 var selStart = this.getRawValue().length;
13840 if(selStart != len){
13841 this.setRawValue(newValue);
13842 this.selectText(selStart, newValue.length);
13848 onSelect : function(record, index){
13850 if(this.fireEvent('beforeselect', this, record, index) !== false){
13852 this.setFromData(index > -1 ? record.data : false);
13855 this.fireEvent('select', this, record, index);
13860 * Returns the currently selected field value or empty string if no value is set.
13861 * @return {String} value The selected value
13863 getValue : function()
13865 if(Roo.isIOS && this.useNativeIOS){
13866 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13870 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13873 if(this.valueField){
13874 return typeof this.value != 'undefined' ? this.value : '';
13876 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13880 getRawValue : function()
13882 if(Roo.isIOS && this.useNativeIOS){
13883 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13886 var v = this.inputEl().getValue();
13892 * Clears any text/value currently set in the field
13894 clearValue : function(){
13896 if(this.hiddenField){
13897 this.hiddenField.dom.value = '';
13900 this.setRawValue('');
13901 this.lastSelectionText = '';
13902 this.lastData = false;
13904 var close = this.closeTriggerEl();
13915 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13916 * will be displayed in the field. If the value does not match the data value of an existing item,
13917 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13918 * Otherwise the field will be blank (although the value will still be set).
13919 * @param {String} value The value to match
13921 setValue : function(v)
13923 if(Roo.isIOS && this.useNativeIOS){
13924 this.setIOSValue(v);
13934 if(this.valueField){
13935 var r = this.findRecord(this.valueField, v);
13937 text = r.data[this.displayField];
13938 }else if(this.valueNotFoundText !== undefined){
13939 text = this.valueNotFoundText;
13942 this.lastSelectionText = text;
13943 if(this.hiddenField){
13944 this.hiddenField.dom.value = v;
13946 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13949 var close = this.closeTriggerEl();
13952 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13958 * @property {Object} the last set data for the element
13963 * Sets the value of the field based on a object which is related to the record format for the store.
13964 * @param {Object} value the value to set as. or false on reset?
13966 setFromData : function(o){
13973 var dv = ''; // display value
13974 var vv = ''; // value value..
13976 if (this.displayField) {
13977 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13979 // this is an error condition!!!
13980 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13983 if(this.valueField){
13984 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13987 var close = this.closeTriggerEl();
13990 if(dv.length || vv * 1 > 0){
13992 this.blockFocus=true;
13998 if(this.hiddenField){
13999 this.hiddenField.dom.value = vv;
14001 this.lastSelectionText = dv;
14002 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14006 // no hidden field.. - we store the value in 'value', but still display
14007 // display field!!!!
14008 this.lastSelectionText = dv;
14009 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14016 reset : function(){
14017 // overridden so that last data is reset..
14024 this.setValue(this.originalValue);
14025 //this.clearInvalid();
14026 this.lastData = false;
14028 this.view.clearSelections();
14034 findRecord : function(prop, value){
14036 if(this.store.getCount() > 0){
14037 this.store.each(function(r){
14038 if(r.data[prop] == value){
14048 getName: function()
14050 // returns hidden if it's set..
14051 if (!this.rendered) {return ''};
14052 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14056 onViewMove : function(e, t){
14057 this.inKeyMode = false;
14061 onViewOver : function(e, t){
14062 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14065 var item = this.view.findItemFromChild(t);
14068 var index = this.view.indexOf(item);
14069 this.select(index, false);
14074 onViewClick : function(view, doFocus, el, e)
14076 var index = this.view.getSelectedIndexes()[0];
14078 var r = this.store.getAt(index);
14082 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14089 Roo.each(this.tickItems, function(v,k){
14091 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14093 _this.tickItems.splice(k, 1);
14095 if(typeof(e) == 'undefined' && view == false){
14096 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14108 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14109 this.tickItems.push(r.data);
14112 if(typeof(e) == 'undefined' && view == false){
14113 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14120 this.onSelect(r, index);
14122 if(doFocus !== false && !this.blockFocus){
14123 this.inputEl().focus();
14128 restrictHeight : function(){
14129 //this.innerList.dom.style.height = '';
14130 //var inner = this.innerList.dom;
14131 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14132 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14133 //this.list.beginUpdate();
14134 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14135 this.list.alignTo(this.inputEl(), this.listAlign);
14136 this.list.alignTo(this.inputEl(), this.listAlign);
14137 //this.list.endUpdate();
14141 onEmptyResults : function(){
14143 if(this.tickable && this.editable){
14144 this.hasFocus = false;
14145 this.restrictHeight();
14153 * Returns true if the dropdown list is expanded, else false.
14155 isExpanded : function(){
14156 return this.list.isVisible();
14160 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14161 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14162 * @param {String} value The data value of the item to select
14163 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14164 * selected item if it is not currently in view (defaults to true)
14165 * @return {Boolean} True if the value matched an item in the list, else false
14167 selectByValue : function(v, scrollIntoView){
14168 if(v !== undefined && v !== null){
14169 var r = this.findRecord(this.valueField || this.displayField, v);
14171 this.select(this.store.indexOf(r), scrollIntoView);
14179 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14180 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14181 * @param {Number} index The zero-based index of the list item to select
14182 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14183 * selected item if it is not currently in view (defaults to true)
14185 select : function(index, scrollIntoView){
14186 this.selectedIndex = index;
14187 this.view.select(index);
14188 if(scrollIntoView !== false){
14189 var el = this.view.getNode(index);
14191 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14194 this.list.scrollChildIntoView(el, false);
14200 selectNext : function(){
14201 var ct = this.store.getCount();
14203 if(this.selectedIndex == -1){
14205 }else if(this.selectedIndex < ct-1){
14206 this.select(this.selectedIndex+1);
14212 selectPrev : function(){
14213 var ct = this.store.getCount();
14215 if(this.selectedIndex == -1){
14217 }else if(this.selectedIndex != 0){
14218 this.select(this.selectedIndex-1);
14224 onKeyUp : function(e){
14225 if(this.editable !== false && !e.isSpecialKey()){
14226 this.lastKey = e.getKey();
14227 this.dqTask.delay(this.queryDelay);
14232 validateBlur : function(){
14233 return !this.list || !this.list.isVisible();
14237 initQuery : function(){
14239 var v = this.getRawValue();
14241 if(this.tickable && this.editable){
14242 v = this.tickableInputEl().getValue();
14249 doForce : function(){
14250 if(this.inputEl().dom.value.length > 0){
14251 this.inputEl().dom.value =
14252 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14258 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14259 * query allowing the query action to be canceled if needed.
14260 * @param {String} query The SQL query to execute
14261 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14262 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14263 * saved in the current store (defaults to false)
14265 doQuery : function(q, forceAll){
14267 if(q === undefined || q === null){
14272 forceAll: forceAll,
14276 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14281 forceAll = qe.forceAll;
14282 if(forceAll === true || (q.length >= this.minChars)){
14284 this.hasQuery = true;
14286 if(this.lastQuery != q || this.alwaysQuery){
14287 this.lastQuery = q;
14288 if(this.mode == 'local'){
14289 this.selectedIndex = -1;
14291 this.store.clearFilter();
14294 if(this.specialFilter){
14295 this.fireEvent('specialfilter', this);
14300 this.store.filter(this.displayField, q);
14303 this.store.fireEvent("datachanged", this.store);
14310 this.store.baseParams[this.queryParam] = q;
14312 var options = {params : this.getParams(q)};
14315 options.add = true;
14316 options.params.start = this.page * this.pageSize;
14319 this.store.load(options);
14322 * this code will make the page width larger, at the beginning, the list not align correctly,
14323 * we should expand the list on onLoad
14324 * so command out it
14329 this.selectedIndex = -1;
14334 this.loadNext = false;
14338 getParams : function(q){
14340 //p[this.queryParam] = q;
14344 p.limit = this.pageSize;
14350 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14352 collapse : function(){
14353 if(!this.isExpanded()){
14359 this.hasFocus = false;
14363 this.cancelBtn.hide();
14364 this.trigger.show();
14367 this.tickableInputEl().dom.value = '';
14368 this.tickableInputEl().blur();
14373 Roo.get(document).un('mousedown', this.collapseIf, this);
14374 Roo.get(document).un('mousewheel', this.collapseIf, this);
14375 if (!this.editable) {
14376 Roo.get(document).un('keydown', this.listKeyPress, this);
14378 this.fireEvent('collapse', this);
14384 collapseIf : function(e){
14385 var in_combo = e.within(this.el);
14386 var in_list = e.within(this.list);
14387 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14389 if (in_combo || in_list || is_list) {
14390 //e.stopPropagation();
14395 this.onTickableFooterButtonClick(e, false, false);
14403 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14405 expand : function(){
14407 if(this.isExpanded() || !this.hasFocus){
14411 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14412 this.list.setWidth(lw);
14418 this.restrictHeight();
14422 this.tickItems = Roo.apply([], this.item);
14425 this.cancelBtn.show();
14426 this.trigger.hide();
14429 this.tickableInputEl().focus();
14434 Roo.get(document).on('mousedown', this.collapseIf, this);
14435 Roo.get(document).on('mousewheel', this.collapseIf, this);
14436 if (!this.editable) {
14437 Roo.get(document).on('keydown', this.listKeyPress, this);
14440 this.fireEvent('expand', this);
14444 // Implements the default empty TriggerField.onTriggerClick function
14445 onTriggerClick : function(e)
14447 Roo.log('trigger click');
14449 if(this.disabled || !this.triggerList){
14454 this.loadNext = false;
14456 if(this.isExpanded()){
14458 if (!this.blockFocus) {
14459 this.inputEl().focus();
14463 this.hasFocus = true;
14464 if(this.triggerAction == 'all') {
14465 this.doQuery(this.allQuery, true);
14467 this.doQuery(this.getRawValue());
14469 if (!this.blockFocus) {
14470 this.inputEl().focus();
14475 onTickableTriggerClick : function(e)
14482 this.loadNext = false;
14483 this.hasFocus = true;
14485 if(this.triggerAction == 'all') {
14486 this.doQuery(this.allQuery, true);
14488 this.doQuery(this.getRawValue());
14492 onSearchFieldClick : function(e)
14494 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14495 this.onTickableFooterButtonClick(e, false, false);
14499 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14504 this.loadNext = false;
14505 this.hasFocus = true;
14507 if(this.triggerAction == 'all') {
14508 this.doQuery(this.allQuery, true);
14510 this.doQuery(this.getRawValue());
14514 listKeyPress : function(e)
14516 //Roo.log('listkeypress');
14517 // scroll to first matching element based on key pres..
14518 if (e.isSpecialKey()) {
14521 var k = String.fromCharCode(e.getKey()).toUpperCase();
14524 var csel = this.view.getSelectedNodes();
14525 var cselitem = false;
14527 var ix = this.view.indexOf(csel[0]);
14528 cselitem = this.store.getAt(ix);
14529 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14535 this.store.each(function(v) {
14537 // start at existing selection.
14538 if (cselitem.id == v.id) {
14544 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14545 match = this.store.indexOf(v);
14551 if (match === false) {
14552 return true; // no more action?
14555 this.view.select(match);
14556 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14557 sn.scrollIntoView(sn.dom.parentNode, false);
14560 onViewScroll : function(e, t){
14562 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){
14566 this.hasQuery = true;
14568 this.loading = this.list.select('.loading', true).first();
14570 if(this.loading === null){
14571 this.list.createChild({
14573 cls: 'loading roo-select2-more-results roo-select2-active',
14574 html: 'Loading more results...'
14577 this.loading = this.list.select('.loading', true).first();
14579 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14581 this.loading.hide();
14584 this.loading.show();
14589 this.loadNext = true;
14591 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14596 addItem : function(o)
14598 var dv = ''; // display value
14600 if (this.displayField) {
14601 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14603 // this is an error condition!!!
14604 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14611 var choice = this.choices.createChild({
14613 cls: 'roo-select2-search-choice',
14622 cls: 'roo-select2-search-choice-close fa fa-times',
14627 }, this.searchField);
14629 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14631 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14639 this.inputEl().dom.value = '';
14644 onRemoveItem : function(e, _self, o)
14646 e.preventDefault();
14648 this.lastItem = Roo.apply([], this.item);
14650 var index = this.item.indexOf(o.data) * 1;
14653 Roo.log('not this item?!');
14657 this.item.splice(index, 1);
14662 this.fireEvent('remove', this, e);
14668 syncValue : function()
14670 if(!this.item.length){
14677 Roo.each(this.item, function(i){
14678 if(_this.valueField){
14679 value.push(i[_this.valueField]);
14686 this.value = value.join(',');
14688 if(this.hiddenField){
14689 this.hiddenField.dom.value = this.value;
14692 this.store.fireEvent("datachanged", this.store);
14697 clearItem : function()
14699 if(!this.multiple){
14705 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14713 if(this.tickable && !Roo.isTouch){
14714 this.view.refresh();
14718 inputEl: function ()
14720 if(Roo.isIOS && this.useNativeIOS){
14721 return this.el.select('select.roo-ios-select', true).first();
14724 if(Roo.isTouch && this.mobileTouchView){
14725 return this.el.select('input.form-control',true).first();
14729 return this.searchField;
14732 return this.el.select('input.form-control',true).first();
14735 onTickableFooterButtonClick : function(e, btn, el)
14737 e.preventDefault();
14739 this.lastItem = Roo.apply([], this.item);
14741 if(btn && btn.name == 'cancel'){
14742 this.tickItems = Roo.apply([], this.item);
14751 Roo.each(this.tickItems, function(o){
14759 validate : function()
14761 if(this.getVisibilityEl().hasClass('hidden')){
14765 var v = this.getRawValue();
14768 v = this.getValue();
14771 if(this.disabled || this.allowBlank || v.length){
14776 this.markInvalid();
14780 tickableInputEl : function()
14782 if(!this.tickable || !this.editable){
14783 return this.inputEl();
14786 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14790 getAutoCreateTouchView : function()
14795 cls: 'form-group' //input-group
14801 type : this.inputType,
14802 cls : 'form-control x-combo-noedit',
14803 autocomplete: 'new-password',
14804 placeholder : this.placeholder || '',
14809 input.name = this.name;
14813 input.cls += ' input-' + this.size;
14816 if (this.disabled) {
14817 input.disabled = true;
14828 inputblock.cls += ' input-group';
14830 inputblock.cn.unshift({
14832 cls : 'input-group-addon',
14837 if(this.removable && !this.multiple){
14838 inputblock.cls += ' roo-removable';
14840 inputblock.cn.push({
14843 cls : 'roo-combo-removable-btn close'
14847 if(this.hasFeedback && !this.allowBlank){
14849 inputblock.cls += ' has-feedback';
14851 inputblock.cn.push({
14853 cls: 'glyphicon form-control-feedback'
14860 inputblock.cls += (this.before) ? '' : ' input-group';
14862 inputblock.cn.push({
14864 cls : 'input-group-addon',
14875 cls: 'form-hidden-field'
14889 cls: 'form-hidden-field'
14893 cls: 'roo-select2-choices',
14897 cls: 'roo-select2-search-field',
14910 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14916 if(!this.multiple && this.showToggleBtn){
14923 if (this.caret != false) {
14926 cls: 'fa fa-' + this.caret
14933 cls : 'input-group-addon btn dropdown-toggle',
14938 cls: 'combobox-clear',
14952 combobox.cls += ' roo-select2-container-multi';
14955 var align = this.labelAlign || this.parentLabelAlign();
14957 if (align ==='left' && this.fieldLabel.length) {
14962 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14963 tooltip : 'This field is required'
14967 cls : 'control-label',
14968 html : this.fieldLabel
14979 var labelCfg = cfg.cn[1];
14980 var contentCfg = cfg.cn[2];
14983 if(this.indicatorpos == 'right'){
14988 cls : 'control-label',
14992 html : this.fieldLabel
14996 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14997 tooltip : 'This field is required'
15010 labelCfg = cfg.cn[0];
15011 contentCfg = cfg.cn[1];
15016 if(this.labelWidth > 12){
15017 labelCfg.style = "width: " + this.labelWidth + 'px';
15020 if(this.labelWidth < 13 && this.labelmd == 0){
15021 this.labelmd = this.labelWidth;
15024 if(this.labellg > 0){
15025 labelCfg.cls += ' col-lg-' + this.labellg;
15026 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15029 if(this.labelmd > 0){
15030 labelCfg.cls += ' col-md-' + this.labelmd;
15031 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15034 if(this.labelsm > 0){
15035 labelCfg.cls += ' col-sm-' + this.labelsm;
15036 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15039 if(this.labelxs > 0){
15040 labelCfg.cls += ' col-xs-' + this.labelxs;
15041 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15045 } else if ( this.fieldLabel.length) {
15049 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15050 tooltip : 'This field is required'
15054 cls : 'control-label',
15055 html : this.fieldLabel
15066 if(this.indicatorpos == 'right'){
15070 cls : 'control-label',
15071 html : this.fieldLabel,
15075 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15076 tooltip : 'This field is required'
15093 var settings = this;
15095 ['xs','sm','md','lg'].map(function(size){
15096 if (settings[size]) {
15097 cfg.cls += ' col-' + size + '-' + settings[size];
15104 initTouchView : function()
15106 this.renderTouchView();
15108 this.touchViewEl.on('scroll', function(){
15109 this.el.dom.scrollTop = 0;
15112 this.originalValue = this.getValue();
15114 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15116 this.inputEl().on("click", this.showTouchView, this);
15117 if (this.triggerEl) {
15118 this.triggerEl.on("click", this.showTouchView, this);
15122 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15123 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15125 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15127 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15128 this.store.on('load', this.onTouchViewLoad, this);
15129 this.store.on('loadexception', this.onTouchViewLoadException, this);
15131 if(this.hiddenName){
15133 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15135 this.hiddenField.dom.value =
15136 this.hiddenValue !== undefined ? this.hiddenValue :
15137 this.value !== undefined ? this.value : '';
15139 this.el.dom.removeAttribute('name');
15140 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15144 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15145 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15148 if(this.removable && !this.multiple){
15149 var close = this.closeTriggerEl();
15151 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15152 close.on('click', this.removeBtnClick, this, close);
15156 * fix the bug in Safari iOS8
15158 this.inputEl().on("focus", function(e){
15159 document.activeElement.blur();
15167 renderTouchView : function()
15169 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15170 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15172 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15173 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15175 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15176 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15177 this.touchViewBodyEl.setStyle('overflow', 'auto');
15179 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15180 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15182 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15183 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15187 showTouchView : function()
15193 this.touchViewHeaderEl.hide();
15195 if(this.modalTitle.length){
15196 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15197 this.touchViewHeaderEl.show();
15200 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15201 this.touchViewEl.show();
15203 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15205 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15206 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15208 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15210 if(this.modalTitle.length){
15211 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15214 this.touchViewBodyEl.setHeight(bodyHeight);
15218 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15220 this.touchViewEl.addClass('in');
15223 this.doTouchViewQuery();
15227 hideTouchView : function()
15229 this.touchViewEl.removeClass('in');
15233 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15235 this.touchViewEl.setStyle('display', 'none');
15240 setTouchViewValue : function()
15247 Roo.each(this.tickItems, function(o){
15252 this.hideTouchView();
15255 doTouchViewQuery : function()
15264 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15268 if(!this.alwaysQuery || this.mode == 'local'){
15269 this.onTouchViewLoad();
15276 onTouchViewBeforeLoad : function(combo,opts)
15282 onTouchViewLoad : function()
15284 if(this.store.getCount() < 1){
15285 this.onTouchViewEmptyResults();
15289 this.clearTouchView();
15291 var rawValue = this.getRawValue();
15293 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15295 this.tickItems = [];
15297 this.store.data.each(function(d, rowIndex){
15298 var row = this.touchViewListGroup.createChild(template);
15300 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15301 row.addClass(d.data.cls);
15304 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15307 html : d.data[this.displayField]
15310 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15311 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15314 row.removeClass('selected');
15315 if(!this.multiple && this.valueField &&
15316 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15319 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15320 row.addClass('selected');
15323 if(this.multiple && this.valueField &&
15324 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15328 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15329 this.tickItems.push(d.data);
15332 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15336 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15338 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15340 if(this.modalTitle.length){
15341 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15344 var listHeight = this.touchViewListGroup.getHeight();
15348 if(firstChecked && listHeight > bodyHeight){
15349 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15354 onTouchViewLoadException : function()
15356 this.hideTouchView();
15359 onTouchViewEmptyResults : function()
15361 this.clearTouchView();
15363 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15365 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15369 clearTouchView : function()
15371 this.touchViewListGroup.dom.innerHTML = '';
15374 onTouchViewClick : function(e, el, o)
15376 e.preventDefault();
15379 var rowIndex = o.rowIndex;
15381 var r = this.store.getAt(rowIndex);
15383 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15385 if(!this.multiple){
15386 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15387 c.dom.removeAttribute('checked');
15390 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15392 this.setFromData(r.data);
15394 var close = this.closeTriggerEl();
15400 this.hideTouchView();
15402 this.fireEvent('select', this, r, rowIndex);
15407 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15408 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15409 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15413 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15414 this.addItem(r.data);
15415 this.tickItems.push(r.data);
15419 getAutoCreateNativeIOS : function()
15422 cls: 'form-group' //input-group,
15427 cls : 'roo-ios-select'
15431 combobox.name = this.name;
15434 if (this.disabled) {
15435 combobox.disabled = true;
15438 var settings = this;
15440 ['xs','sm','md','lg'].map(function(size){
15441 if (settings[size]) {
15442 cfg.cls += ' col-' + size + '-' + settings[size];
15452 initIOSView : function()
15454 this.store.on('load', this.onIOSViewLoad, this);
15459 onIOSViewLoad : function()
15461 if(this.store.getCount() < 1){
15465 this.clearIOSView();
15467 if(this.allowBlank) {
15469 var default_text = '-- SELECT --';
15471 if(this.placeholder.length){
15472 default_text = this.placeholder;
15475 if(this.emptyTitle.length){
15476 default_text += ' - ' + this.emptyTitle + ' -';
15479 var opt = this.inputEl().createChild({
15482 html : default_text
15486 o[this.valueField] = 0;
15487 o[this.displayField] = default_text;
15489 this.ios_options.push({
15496 this.store.data.each(function(d, rowIndex){
15500 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15501 html = d.data[this.displayField];
15506 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15507 value = d.data[this.valueField];
15516 if(this.value == d.data[this.valueField]){
15517 option['selected'] = true;
15520 var opt = this.inputEl().createChild(option);
15522 this.ios_options.push({
15529 this.inputEl().on('change', function(){
15530 this.fireEvent('select', this);
15535 clearIOSView: function()
15537 this.inputEl().dom.innerHTML = '';
15539 this.ios_options = [];
15542 setIOSValue: function(v)
15546 if(!this.ios_options){
15550 Roo.each(this.ios_options, function(opts){
15552 opts.el.dom.removeAttribute('selected');
15554 if(opts.data[this.valueField] != v){
15558 opts.el.dom.setAttribute('selected', true);
15564 * @cfg {Boolean} grow
15568 * @cfg {Number} growMin
15572 * @cfg {Number} growMax
15581 Roo.apply(Roo.bootstrap.ComboBox, {
15585 cls: 'modal-header',
15607 cls: 'list-group-item',
15611 cls: 'roo-combobox-list-group-item-value'
15615 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15629 listItemCheckbox : {
15631 cls: 'list-group-item',
15635 cls: 'roo-combobox-list-group-item-value'
15639 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15655 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15660 cls: 'modal-footer',
15668 cls: 'col-xs-6 text-left',
15671 cls: 'btn btn-danger roo-touch-view-cancel',
15677 cls: 'col-xs-6 text-right',
15680 cls: 'btn btn-success roo-touch-view-ok',
15691 Roo.apply(Roo.bootstrap.ComboBox, {
15693 touchViewTemplate : {
15695 cls: 'modal fade roo-combobox-touch-view',
15699 cls: 'modal-dialog',
15700 style : 'position:fixed', // we have to fix position....
15704 cls: 'modal-content',
15706 Roo.bootstrap.ComboBox.header,
15707 Roo.bootstrap.ComboBox.body,
15708 Roo.bootstrap.ComboBox.footer
15717 * Ext JS Library 1.1.1
15718 * Copyright(c) 2006-2007, Ext JS, LLC.
15720 * Originally Released Under LGPL - original licence link has changed is not relivant.
15723 * <script type="text/javascript">
15728 * @extends Roo.util.Observable
15729 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15730 * This class also supports single and multi selection modes. <br>
15731 * Create a data model bound view:
15733 var store = new Roo.data.Store(...);
15735 var view = new Roo.View({
15737 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15739 singleSelect: true,
15740 selectedClass: "ydataview-selected",
15744 // listen for node click?
15745 view.on("click", function(vw, index, node, e){
15746 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15750 dataModel.load("foobar.xml");
15752 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15754 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15755 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15757 * Note: old style constructor is still suported (container, template, config)
15760 * Create a new View
15761 * @param {Object} config The config object
15764 Roo.View = function(config, depreciated_tpl, depreciated_config){
15766 this.parent = false;
15768 if (typeof(depreciated_tpl) == 'undefined') {
15769 // new way.. - universal constructor.
15770 Roo.apply(this, config);
15771 this.el = Roo.get(this.el);
15774 this.el = Roo.get(config);
15775 this.tpl = depreciated_tpl;
15776 Roo.apply(this, depreciated_config);
15778 this.wrapEl = this.el.wrap().wrap();
15779 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15782 if(typeof(this.tpl) == "string"){
15783 this.tpl = new Roo.Template(this.tpl);
15785 // support xtype ctors..
15786 this.tpl = new Roo.factory(this.tpl, Roo);
15790 this.tpl.compile();
15795 * @event beforeclick
15796 * Fires before a click is processed. Returns false to cancel the default action.
15797 * @param {Roo.View} this
15798 * @param {Number} index The index of the target node
15799 * @param {HTMLElement} node The target node
15800 * @param {Roo.EventObject} e The raw event object
15802 "beforeclick" : true,
15805 * Fires when a template node is clicked.
15806 * @param {Roo.View} this
15807 * @param {Number} index The index of the target node
15808 * @param {HTMLElement} node The target node
15809 * @param {Roo.EventObject} e The raw event object
15814 * Fires when a template node is double clicked.
15815 * @param {Roo.View} this
15816 * @param {Number} index The index of the target node
15817 * @param {HTMLElement} node The target node
15818 * @param {Roo.EventObject} e The raw event object
15822 * @event contextmenu
15823 * Fires when a template node is right clicked.
15824 * @param {Roo.View} this
15825 * @param {Number} index The index of the target node
15826 * @param {HTMLElement} node The target node
15827 * @param {Roo.EventObject} e The raw event object
15829 "contextmenu" : true,
15831 * @event selectionchange
15832 * Fires when the selected nodes change.
15833 * @param {Roo.View} this
15834 * @param {Array} selections Array of the selected nodes
15836 "selectionchange" : true,
15839 * @event beforeselect
15840 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15841 * @param {Roo.View} this
15842 * @param {HTMLElement} node The node to be selected
15843 * @param {Array} selections Array of currently selected nodes
15845 "beforeselect" : true,
15847 * @event preparedata
15848 * Fires on every row to render, to allow you to change the data.
15849 * @param {Roo.View} this
15850 * @param {Object} data to be rendered (change this)
15852 "preparedata" : true
15860 "click": this.onClick,
15861 "dblclick": this.onDblClick,
15862 "contextmenu": this.onContextMenu,
15866 this.selections = [];
15868 this.cmp = new Roo.CompositeElementLite([]);
15870 this.store = Roo.factory(this.store, Roo.data);
15871 this.setStore(this.store, true);
15874 if ( this.footer && this.footer.xtype) {
15876 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15878 this.footer.dataSource = this.store;
15879 this.footer.container = fctr;
15880 this.footer = Roo.factory(this.footer, Roo);
15881 fctr.insertFirst(this.el);
15883 // this is a bit insane - as the paging toolbar seems to detach the el..
15884 // dom.parentNode.parentNode.parentNode
15885 // they get detached?
15889 Roo.View.superclass.constructor.call(this);
15894 Roo.extend(Roo.View, Roo.util.Observable, {
15897 * @cfg {Roo.data.Store} store Data store to load data from.
15902 * @cfg {String|Roo.Element} el The container element.
15907 * @cfg {String|Roo.Template} tpl The template used by this View
15911 * @cfg {String} dataName the named area of the template to use as the data area
15912 * Works with domtemplates roo-name="name"
15916 * @cfg {String} selectedClass The css class to add to selected nodes
15918 selectedClass : "x-view-selected",
15920 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15925 * @cfg {String} text to display on mask (default Loading)
15929 * @cfg {Boolean} multiSelect Allow multiple selection
15931 multiSelect : false,
15933 * @cfg {Boolean} singleSelect Allow single selection
15935 singleSelect: false,
15938 * @cfg {Boolean} toggleSelect - selecting
15940 toggleSelect : false,
15943 * @cfg {Boolean} tickable - selecting
15948 * Returns the element this view is bound to.
15949 * @return {Roo.Element}
15951 getEl : function(){
15952 return this.wrapEl;
15958 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15960 refresh : function(){
15961 //Roo.log('refresh');
15964 // if we are using something like 'domtemplate', then
15965 // the what gets used is:
15966 // t.applySubtemplate(NAME, data, wrapping data..)
15967 // the outer template then get' applied with
15968 // the store 'extra data'
15969 // and the body get's added to the
15970 // roo-name="data" node?
15971 // <span class='roo-tpl-{name}'></span> ?????
15975 this.clearSelections();
15976 this.el.update("");
15978 var records = this.store.getRange();
15979 if(records.length < 1) {
15981 // is this valid?? = should it render a template??
15983 this.el.update(this.emptyText);
15987 if (this.dataName) {
15988 this.el.update(t.apply(this.store.meta)); //????
15989 el = this.el.child('.roo-tpl-' + this.dataName);
15992 for(var i = 0, len = records.length; i < len; i++){
15993 var data = this.prepareData(records[i].data, i, records[i]);
15994 this.fireEvent("preparedata", this, data, i, records[i]);
15996 var d = Roo.apply({}, data);
15999 Roo.apply(d, {'roo-id' : Roo.id()});
16003 Roo.each(this.parent.item, function(item){
16004 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16007 Roo.apply(d, {'roo-data-checked' : 'checked'});
16011 html[html.length] = Roo.util.Format.trim(
16013 t.applySubtemplate(this.dataName, d, this.store.meta) :
16020 el.update(html.join(""));
16021 this.nodes = el.dom.childNodes;
16022 this.updateIndexes(0);
16027 * Function to override to reformat the data that is sent to
16028 * the template for each node.
16029 * DEPRICATED - use the preparedata event handler.
16030 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16031 * a JSON object for an UpdateManager bound view).
16033 prepareData : function(data, index, record)
16035 this.fireEvent("preparedata", this, data, index, record);
16039 onUpdate : function(ds, record){
16040 // Roo.log('on update');
16041 this.clearSelections();
16042 var index = this.store.indexOf(record);
16043 var n = this.nodes[index];
16044 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16045 n.parentNode.removeChild(n);
16046 this.updateIndexes(index, index);
16052 onAdd : function(ds, records, index)
16054 //Roo.log(['on Add', ds, records, index] );
16055 this.clearSelections();
16056 if(this.nodes.length == 0){
16060 var n = this.nodes[index];
16061 for(var i = 0, len = records.length; i < len; i++){
16062 var d = this.prepareData(records[i].data, i, records[i]);
16064 this.tpl.insertBefore(n, d);
16067 this.tpl.append(this.el, d);
16070 this.updateIndexes(index);
16073 onRemove : function(ds, record, index){
16074 // Roo.log('onRemove');
16075 this.clearSelections();
16076 var el = this.dataName ?
16077 this.el.child('.roo-tpl-' + this.dataName) :
16080 el.dom.removeChild(this.nodes[index]);
16081 this.updateIndexes(index);
16085 * Refresh an individual node.
16086 * @param {Number} index
16088 refreshNode : function(index){
16089 this.onUpdate(this.store, this.store.getAt(index));
16092 updateIndexes : function(startIndex, endIndex){
16093 var ns = this.nodes;
16094 startIndex = startIndex || 0;
16095 endIndex = endIndex || ns.length - 1;
16096 for(var i = startIndex; i <= endIndex; i++){
16097 ns[i].nodeIndex = i;
16102 * Changes the data store this view uses and refresh the view.
16103 * @param {Store} store
16105 setStore : function(store, initial){
16106 if(!initial && this.store){
16107 this.store.un("datachanged", this.refresh);
16108 this.store.un("add", this.onAdd);
16109 this.store.un("remove", this.onRemove);
16110 this.store.un("update", this.onUpdate);
16111 this.store.un("clear", this.refresh);
16112 this.store.un("beforeload", this.onBeforeLoad);
16113 this.store.un("load", this.onLoad);
16114 this.store.un("loadexception", this.onLoad);
16118 store.on("datachanged", this.refresh, this);
16119 store.on("add", this.onAdd, this);
16120 store.on("remove", this.onRemove, this);
16121 store.on("update", this.onUpdate, this);
16122 store.on("clear", this.refresh, this);
16123 store.on("beforeload", this.onBeforeLoad, this);
16124 store.on("load", this.onLoad, this);
16125 store.on("loadexception", this.onLoad, this);
16133 * onbeforeLoad - masks the loading area.
16136 onBeforeLoad : function(store,opts)
16138 //Roo.log('onBeforeLoad');
16140 this.el.update("");
16142 this.el.mask(this.mask ? this.mask : "Loading" );
16144 onLoad : function ()
16151 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16152 * @param {HTMLElement} node
16153 * @return {HTMLElement} The template node
16155 findItemFromChild : function(node){
16156 var el = this.dataName ?
16157 this.el.child('.roo-tpl-' + this.dataName,true) :
16160 if(!node || node.parentNode == el){
16163 var p = node.parentNode;
16164 while(p && p != el){
16165 if(p.parentNode == el){
16174 onClick : function(e){
16175 var item = this.findItemFromChild(e.getTarget());
16177 var index = this.indexOf(item);
16178 if(this.onItemClick(item, index, e) !== false){
16179 this.fireEvent("click", this, index, item, e);
16182 this.clearSelections();
16187 onContextMenu : function(e){
16188 var item = this.findItemFromChild(e.getTarget());
16190 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16195 onDblClick : function(e){
16196 var item = this.findItemFromChild(e.getTarget());
16198 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16202 onItemClick : function(item, index, e)
16204 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16207 if (this.toggleSelect) {
16208 var m = this.isSelected(item) ? 'unselect' : 'select';
16211 _t[m](item, true, false);
16214 if(this.multiSelect || this.singleSelect){
16215 if(this.multiSelect && e.shiftKey && this.lastSelection){
16216 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16218 this.select(item, this.multiSelect && e.ctrlKey);
16219 this.lastSelection = item;
16222 if(!this.tickable){
16223 e.preventDefault();
16231 * Get the number of selected nodes.
16234 getSelectionCount : function(){
16235 return this.selections.length;
16239 * Get the currently selected nodes.
16240 * @return {Array} An array of HTMLElements
16242 getSelectedNodes : function(){
16243 return this.selections;
16247 * Get the indexes of the selected nodes.
16250 getSelectedIndexes : function(){
16251 var indexes = [], s = this.selections;
16252 for(var i = 0, len = s.length; i < len; i++){
16253 indexes.push(s[i].nodeIndex);
16259 * Clear all selections
16260 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16262 clearSelections : function(suppressEvent){
16263 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16264 this.cmp.elements = this.selections;
16265 this.cmp.removeClass(this.selectedClass);
16266 this.selections = [];
16267 if(!suppressEvent){
16268 this.fireEvent("selectionchange", this, this.selections);
16274 * Returns true if the passed node is selected
16275 * @param {HTMLElement/Number} node The node or node index
16276 * @return {Boolean}
16278 isSelected : function(node){
16279 var s = this.selections;
16283 node = this.getNode(node);
16284 return s.indexOf(node) !== -1;
16289 * @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
16290 * @param {Boolean} keepExisting (optional) true to keep existing selections
16291 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16293 select : function(nodeInfo, keepExisting, suppressEvent){
16294 if(nodeInfo instanceof Array){
16296 this.clearSelections(true);
16298 for(var i = 0, len = nodeInfo.length; i < len; i++){
16299 this.select(nodeInfo[i], true, true);
16303 var node = this.getNode(nodeInfo);
16304 if(!node || this.isSelected(node)){
16305 return; // already selected.
16308 this.clearSelections(true);
16311 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16312 Roo.fly(node).addClass(this.selectedClass);
16313 this.selections.push(node);
16314 if(!suppressEvent){
16315 this.fireEvent("selectionchange", this, this.selections);
16323 * @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
16324 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16325 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16327 unselect : function(nodeInfo, keepExisting, suppressEvent)
16329 if(nodeInfo instanceof Array){
16330 Roo.each(this.selections, function(s) {
16331 this.unselect(s, nodeInfo);
16335 var node = this.getNode(nodeInfo);
16336 if(!node || !this.isSelected(node)){
16337 //Roo.log("not selected");
16338 return; // not selected.
16342 Roo.each(this.selections, function(s) {
16344 Roo.fly(node).removeClass(this.selectedClass);
16351 this.selections= ns;
16352 this.fireEvent("selectionchange", this, this.selections);
16356 * Gets a template node.
16357 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16358 * @return {HTMLElement} The node or null if it wasn't found
16360 getNode : function(nodeInfo){
16361 if(typeof nodeInfo == "string"){
16362 return document.getElementById(nodeInfo);
16363 }else if(typeof nodeInfo == "number"){
16364 return this.nodes[nodeInfo];
16370 * Gets a range template nodes.
16371 * @param {Number} startIndex
16372 * @param {Number} endIndex
16373 * @return {Array} An array of nodes
16375 getNodes : function(start, end){
16376 var ns = this.nodes;
16377 start = start || 0;
16378 end = typeof end == "undefined" ? ns.length - 1 : end;
16381 for(var i = start; i <= end; i++){
16385 for(var i = start; i >= end; i--){
16393 * Finds the index of the passed node
16394 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16395 * @return {Number} The index of the node or -1
16397 indexOf : function(node){
16398 node = this.getNode(node);
16399 if(typeof node.nodeIndex == "number"){
16400 return node.nodeIndex;
16402 var ns = this.nodes;
16403 for(var i = 0, len = ns.length; i < len; i++){
16414 * based on jquery fullcalendar
16418 Roo.bootstrap = Roo.bootstrap || {};
16420 * @class Roo.bootstrap.Calendar
16421 * @extends Roo.bootstrap.Component
16422 * Bootstrap Calendar class
16423 * @cfg {Boolean} loadMask (true|false) default false
16424 * @cfg {Object} header generate the user specific header of the calendar, default false
16427 * Create a new Container
16428 * @param {Object} config The config object
16433 Roo.bootstrap.Calendar = function(config){
16434 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16438 * Fires when a date is selected
16439 * @param {DatePicker} this
16440 * @param {Date} date The selected date
16444 * @event monthchange
16445 * Fires when the displayed month changes
16446 * @param {DatePicker} this
16447 * @param {Date} date The selected month
16449 'monthchange': true,
16451 * @event evententer
16452 * Fires when mouse over an event
16453 * @param {Calendar} this
16454 * @param {event} Event
16456 'evententer': true,
16458 * @event eventleave
16459 * Fires when the mouse leaves an
16460 * @param {Calendar} this
16463 'eventleave': true,
16465 * @event eventclick
16466 * Fires when the mouse click an
16467 * @param {Calendar} this
16476 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16479 * @cfg {Number} startDay
16480 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16488 getAutoCreate : function(){
16491 var fc_button = function(name, corner, style, content ) {
16492 return Roo.apply({},{
16494 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16496 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16499 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16510 style : 'width:100%',
16517 cls : 'fc-header-left',
16519 fc_button('prev', 'left', 'arrow', '‹' ),
16520 fc_button('next', 'right', 'arrow', '›' ),
16521 { tag: 'span', cls: 'fc-header-space' },
16522 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16530 cls : 'fc-header-center',
16534 cls: 'fc-header-title',
16537 html : 'month / year'
16545 cls : 'fc-header-right',
16547 /* fc_button('month', 'left', '', 'month' ),
16548 fc_button('week', '', '', 'week' ),
16549 fc_button('day', 'right', '', 'day' )
16561 header = this.header;
16564 var cal_heads = function() {
16566 // fixme - handle this.
16568 for (var i =0; i < Date.dayNames.length; i++) {
16569 var d = Date.dayNames[i];
16572 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16573 html : d.substring(0,3)
16577 ret[0].cls += ' fc-first';
16578 ret[6].cls += ' fc-last';
16581 var cal_cell = function(n) {
16584 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16589 cls: 'fc-day-number',
16593 cls: 'fc-day-content',
16597 style: 'position: relative;' // height: 17px;
16609 var cal_rows = function() {
16612 for (var r = 0; r < 6; r++) {
16619 for (var i =0; i < Date.dayNames.length; i++) {
16620 var d = Date.dayNames[i];
16621 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16624 row.cn[0].cls+=' fc-first';
16625 row.cn[0].cn[0].style = 'min-height:90px';
16626 row.cn[6].cls+=' fc-last';
16630 ret[0].cls += ' fc-first';
16631 ret[4].cls += ' fc-prev-last';
16632 ret[5].cls += ' fc-last';
16639 cls: 'fc-border-separate',
16640 style : 'width:100%',
16648 cls : 'fc-first fc-last',
16666 cls : 'fc-content',
16667 style : "position: relative;",
16670 cls : 'fc-view fc-view-month fc-grid',
16671 style : 'position: relative',
16672 unselectable : 'on',
16675 cls : 'fc-event-container',
16676 style : 'position:absolute;z-index:8;top:0;left:0;'
16694 initEvents : function()
16697 throw "can not find store for calendar";
16703 style: "text-align:center",
16707 style: "background-color:white;width:50%;margin:250 auto",
16711 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16722 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16724 var size = this.el.select('.fc-content', true).first().getSize();
16725 this.maskEl.setSize(size.width, size.height);
16726 this.maskEl.enableDisplayMode("block");
16727 if(!this.loadMask){
16728 this.maskEl.hide();
16731 this.store = Roo.factory(this.store, Roo.data);
16732 this.store.on('load', this.onLoad, this);
16733 this.store.on('beforeload', this.onBeforeLoad, this);
16737 this.cells = this.el.select('.fc-day',true);
16738 //Roo.log(this.cells);
16739 this.textNodes = this.el.query('.fc-day-number');
16740 this.cells.addClassOnOver('fc-state-hover');
16742 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16743 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16744 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16745 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16747 this.on('monthchange', this.onMonthChange, this);
16749 this.update(new Date().clearTime());
16752 resize : function() {
16753 var sz = this.el.getSize();
16755 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16756 this.el.select('.fc-day-content div',true).setHeight(34);
16761 showPrevMonth : function(e){
16762 this.update(this.activeDate.add("mo", -1));
16764 showToday : function(e){
16765 this.update(new Date().clearTime());
16768 showNextMonth : function(e){
16769 this.update(this.activeDate.add("mo", 1));
16773 showPrevYear : function(){
16774 this.update(this.activeDate.add("y", -1));
16778 showNextYear : function(){
16779 this.update(this.activeDate.add("y", 1));
16784 update : function(date)
16786 var vd = this.activeDate;
16787 this.activeDate = date;
16788 // if(vd && this.el){
16789 // var t = date.getTime();
16790 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16791 // Roo.log('using add remove');
16793 // this.fireEvent('monthchange', this, date);
16795 // this.cells.removeClass("fc-state-highlight");
16796 // this.cells.each(function(c){
16797 // if(c.dateValue == t){
16798 // c.addClass("fc-state-highlight");
16799 // setTimeout(function(){
16800 // try{c.dom.firstChild.focus();}catch(e){}
16810 var days = date.getDaysInMonth();
16812 var firstOfMonth = date.getFirstDateOfMonth();
16813 var startingPos = firstOfMonth.getDay()-this.startDay;
16815 if(startingPos < this.startDay){
16819 var pm = date.add(Date.MONTH, -1);
16820 var prevStart = pm.getDaysInMonth()-startingPos;
16822 this.cells = this.el.select('.fc-day',true);
16823 this.textNodes = this.el.query('.fc-day-number');
16824 this.cells.addClassOnOver('fc-state-hover');
16826 var cells = this.cells.elements;
16827 var textEls = this.textNodes;
16829 Roo.each(cells, function(cell){
16830 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16833 days += startingPos;
16835 // convert everything to numbers so it's fast
16836 var day = 86400000;
16837 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16840 //Roo.log(prevStart);
16842 var today = new Date().clearTime().getTime();
16843 var sel = date.clearTime().getTime();
16844 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16845 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16846 var ddMatch = this.disabledDatesRE;
16847 var ddText = this.disabledDatesText;
16848 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16849 var ddaysText = this.disabledDaysText;
16850 var format = this.format;
16852 var setCellClass = function(cal, cell){
16856 //Roo.log('set Cell Class');
16858 var t = d.getTime();
16862 cell.dateValue = t;
16864 cell.className += " fc-today";
16865 cell.className += " fc-state-highlight";
16866 cell.title = cal.todayText;
16869 // disable highlight in other month..
16870 //cell.className += " fc-state-highlight";
16875 cell.className = " fc-state-disabled";
16876 cell.title = cal.minText;
16880 cell.className = " fc-state-disabled";
16881 cell.title = cal.maxText;
16885 if(ddays.indexOf(d.getDay()) != -1){
16886 cell.title = ddaysText;
16887 cell.className = " fc-state-disabled";
16890 if(ddMatch && format){
16891 var fvalue = d.dateFormat(format);
16892 if(ddMatch.test(fvalue)){
16893 cell.title = ddText.replace("%0", fvalue);
16894 cell.className = " fc-state-disabled";
16898 if (!cell.initialClassName) {
16899 cell.initialClassName = cell.dom.className;
16902 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16907 for(; i < startingPos; i++) {
16908 textEls[i].innerHTML = (++prevStart);
16909 d.setDate(d.getDate()+1);
16911 cells[i].className = "fc-past fc-other-month";
16912 setCellClass(this, cells[i]);
16917 for(; i < days; i++){
16918 intDay = i - startingPos + 1;
16919 textEls[i].innerHTML = (intDay);
16920 d.setDate(d.getDate()+1);
16922 cells[i].className = ''; // "x-date-active";
16923 setCellClass(this, cells[i]);
16927 for(; i < 42; i++) {
16928 textEls[i].innerHTML = (++extraDays);
16929 d.setDate(d.getDate()+1);
16931 cells[i].className = "fc-future fc-other-month";
16932 setCellClass(this, cells[i]);
16935 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16937 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16939 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16940 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16942 if(totalRows != 6){
16943 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16944 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16947 this.fireEvent('monthchange', this, date);
16951 if(!this.internalRender){
16952 var main = this.el.dom.firstChild;
16953 var w = main.offsetWidth;
16954 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16955 Roo.fly(main).setWidth(w);
16956 this.internalRender = true;
16957 // opera does not respect the auto grow header center column
16958 // then, after it gets a width opera refuses to recalculate
16959 // without a second pass
16960 if(Roo.isOpera && !this.secondPass){
16961 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16962 this.secondPass = true;
16963 this.update.defer(10, this, [date]);
16970 findCell : function(dt) {
16971 dt = dt.clearTime().getTime();
16973 this.cells.each(function(c){
16974 //Roo.log("check " +c.dateValue + '?=' + dt);
16975 if(c.dateValue == dt){
16985 findCells : function(ev) {
16986 var s = ev.start.clone().clearTime().getTime();
16988 var e= ev.end.clone().clearTime().getTime();
16991 this.cells.each(function(c){
16992 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16994 if(c.dateValue > e){
16997 if(c.dateValue < s){
17006 // findBestRow: function(cells)
17010 // for (var i =0 ; i < cells.length;i++) {
17011 // ret = Math.max(cells[i].rows || 0,ret);
17018 addItem : function(ev)
17020 // look for vertical location slot in
17021 var cells = this.findCells(ev);
17023 // ev.row = this.findBestRow(cells);
17025 // work out the location.
17029 for(var i =0; i < cells.length; i++) {
17031 cells[i].row = cells[0].row;
17034 cells[i].row = cells[i].row + 1;
17044 if (crow.start.getY() == cells[i].getY()) {
17046 crow.end = cells[i];
17063 cells[0].events.push(ev);
17065 this.calevents.push(ev);
17068 clearEvents: function() {
17070 if(!this.calevents){
17074 Roo.each(this.cells.elements, function(c){
17080 Roo.each(this.calevents, function(e) {
17081 Roo.each(e.els, function(el) {
17082 el.un('mouseenter' ,this.onEventEnter, this);
17083 el.un('mouseleave' ,this.onEventLeave, this);
17088 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17094 renderEvents: function()
17098 this.cells.each(function(c) {
17107 if(c.row != c.events.length){
17108 r = 4 - (4 - (c.row - c.events.length));
17111 c.events = ev.slice(0, r);
17112 c.more = ev.slice(r);
17114 if(c.more.length && c.more.length == 1){
17115 c.events.push(c.more.pop());
17118 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17122 this.cells.each(function(c) {
17124 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17127 for (var e = 0; e < c.events.length; e++){
17128 var ev = c.events[e];
17129 var rows = ev.rows;
17131 for(var i = 0; i < rows.length; i++) {
17133 // how many rows should it span..
17136 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17137 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17139 unselectable : "on",
17142 cls: 'fc-event-inner',
17146 // cls: 'fc-event-time',
17147 // html : cells.length > 1 ? '' : ev.time
17151 cls: 'fc-event-title',
17152 html : String.format('{0}', ev.title)
17159 cls: 'ui-resizable-handle ui-resizable-e',
17160 html : '  '
17167 cfg.cls += ' fc-event-start';
17169 if ((i+1) == rows.length) {
17170 cfg.cls += ' fc-event-end';
17173 var ctr = _this.el.select('.fc-event-container',true).first();
17174 var cg = ctr.createChild(cfg);
17176 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17177 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17179 var r = (c.more.length) ? 1 : 0;
17180 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17181 cg.setWidth(ebox.right - sbox.x -2);
17183 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17184 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17185 cg.on('click', _this.onEventClick, _this, ev);
17196 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17197 style : 'position: absolute',
17198 unselectable : "on",
17201 cls: 'fc-event-inner',
17205 cls: 'fc-event-title',
17213 cls: 'ui-resizable-handle ui-resizable-e',
17214 html : '  '
17220 var ctr = _this.el.select('.fc-event-container',true).first();
17221 var cg = ctr.createChild(cfg);
17223 var sbox = c.select('.fc-day-content',true).first().getBox();
17224 var ebox = c.select('.fc-day-content',true).first().getBox();
17226 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17227 cg.setWidth(ebox.right - sbox.x -2);
17229 cg.on('click', _this.onMoreEventClick, _this, c.more);
17239 onEventEnter: function (e, el,event,d) {
17240 this.fireEvent('evententer', this, el, event);
17243 onEventLeave: function (e, el,event,d) {
17244 this.fireEvent('eventleave', this, el, event);
17247 onEventClick: function (e, el,event,d) {
17248 this.fireEvent('eventclick', this, el, event);
17251 onMonthChange: function () {
17255 onMoreEventClick: function(e, el, more)
17259 this.calpopover.placement = 'right';
17260 this.calpopover.setTitle('More');
17262 this.calpopover.setContent('');
17264 var ctr = this.calpopover.el.select('.popover-content', true).first();
17266 Roo.each(more, function(m){
17268 cls : 'fc-event-hori fc-event-draggable',
17271 var cg = ctr.createChild(cfg);
17273 cg.on('click', _this.onEventClick, _this, m);
17276 this.calpopover.show(el);
17281 onLoad: function ()
17283 this.calevents = [];
17286 if(this.store.getCount() > 0){
17287 this.store.data.each(function(d){
17290 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17291 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17292 time : d.data.start_time,
17293 title : d.data.title,
17294 description : d.data.description,
17295 venue : d.data.venue
17300 this.renderEvents();
17302 if(this.calevents.length && this.loadMask){
17303 this.maskEl.hide();
17307 onBeforeLoad: function()
17309 this.clearEvents();
17311 this.maskEl.show();
17325 * @class Roo.bootstrap.Popover
17326 * @extends Roo.bootstrap.Component
17327 * Bootstrap Popover class
17328 * @cfg {String} html contents of the popover (or false to use children..)
17329 * @cfg {String} title of popover (or false to hide)
17330 * @cfg {String} placement how it is placed
17331 * @cfg {String} trigger click || hover (or false to trigger manually)
17332 * @cfg {String} over what (parent or false to trigger manually.)
17333 * @cfg {Number} delay - delay before showing
17336 * Create a new Popover
17337 * @param {Object} config The config object
17340 Roo.bootstrap.Popover = function(config){
17341 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17347 * After the popover show
17349 * @param {Roo.bootstrap.Popover} this
17354 * After the popover hide
17356 * @param {Roo.bootstrap.Popover} this
17362 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17364 title: 'Fill in a title',
17367 placement : 'right',
17368 trigger : 'hover', // hover
17374 can_build_overlaid : false,
17376 getChildContainer : function()
17378 return this.el.select('.popover-content',true).first();
17381 getAutoCreate : function(){
17384 cls : 'popover roo-dynamic',
17385 style: 'display:block',
17391 cls : 'popover-inner',
17395 cls: 'popover-title',
17399 cls : 'popover-content',
17410 setTitle: function(str)
17413 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17415 setContent: function(str)
17418 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17420 // as it get's added to the bottom of the page.
17421 onRender : function(ct, position)
17423 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17425 var cfg = Roo.apply({}, this.getAutoCreate());
17429 cfg.cls += ' ' + this.cls;
17432 cfg.style = this.style;
17434 //Roo.log("adding to ");
17435 this.el = Roo.get(document.body).createChild(cfg, position);
17436 // Roo.log(this.el);
17441 initEvents : function()
17443 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17444 this.el.enableDisplayMode('block');
17446 if (this.over === false) {
17449 if (this.triggers === false) {
17452 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17453 var triggers = this.trigger ? this.trigger.split(' ') : [];
17454 Roo.each(triggers, function(trigger) {
17456 if (trigger == 'click') {
17457 on_el.on('click', this.toggle, this);
17458 } else if (trigger != 'manual') {
17459 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17460 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17462 on_el.on(eventIn ,this.enter, this);
17463 on_el.on(eventOut, this.leave, this);
17474 toggle : function () {
17475 this.hoverState == 'in' ? this.leave() : this.enter();
17478 enter : function () {
17480 clearTimeout(this.timeout);
17482 this.hoverState = 'in';
17484 if (!this.delay || !this.delay.show) {
17489 this.timeout = setTimeout(function () {
17490 if (_t.hoverState == 'in') {
17493 }, this.delay.show)
17496 leave : function() {
17497 clearTimeout(this.timeout);
17499 this.hoverState = 'out';
17501 if (!this.delay || !this.delay.hide) {
17506 this.timeout = setTimeout(function () {
17507 if (_t.hoverState == 'out') {
17510 }, this.delay.hide)
17513 show : function (on_el)
17516 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17520 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17521 if (this.html !== false) {
17522 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17524 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17525 if (!this.title.length) {
17526 this.el.select('.popover-title',true).hide();
17529 var placement = typeof this.placement == 'function' ?
17530 this.placement.call(this, this.el, on_el) :
17533 var autoToken = /\s?auto?\s?/i;
17534 var autoPlace = autoToken.test(placement);
17536 placement = placement.replace(autoToken, '') || 'top';
17540 //this.el.setXY([0,0]);
17542 this.el.dom.style.display='block';
17543 this.el.addClass(placement);
17545 //this.el.appendTo(on_el);
17547 var p = this.getPosition();
17548 var box = this.el.getBox();
17553 var align = Roo.bootstrap.Popover.alignment[placement];
17556 this.el.alignTo(on_el, align[0],align[1]);
17557 //var arrow = this.el.select('.arrow',true).first();
17558 //arrow.set(align[2],
17560 this.el.addClass('in');
17563 if (this.el.hasClass('fade')) {
17567 this.hoverState = 'in';
17569 this.fireEvent('show', this);
17574 this.el.setXY([0,0]);
17575 this.el.removeClass('in');
17577 this.hoverState = null;
17579 this.fireEvent('hide', this);
17584 Roo.bootstrap.Popover.alignment = {
17585 'left' : ['r-l', [-10,0], 'right'],
17586 'right' : ['l-r', [10,0], 'left'],
17587 'bottom' : ['t-b', [0,10], 'top'],
17588 'top' : [ 'b-t', [0,-10], 'bottom']
17599 * @class Roo.bootstrap.Progress
17600 * @extends Roo.bootstrap.Component
17601 * Bootstrap Progress class
17602 * @cfg {Boolean} striped striped of the progress bar
17603 * @cfg {Boolean} active animated of the progress bar
17607 * Create a new Progress
17608 * @param {Object} config The config object
17611 Roo.bootstrap.Progress = function(config){
17612 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17615 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17620 getAutoCreate : function(){
17628 cfg.cls += ' progress-striped';
17632 cfg.cls += ' active';
17651 * @class Roo.bootstrap.ProgressBar
17652 * @extends Roo.bootstrap.Component
17653 * Bootstrap ProgressBar class
17654 * @cfg {Number} aria_valuenow aria-value now
17655 * @cfg {Number} aria_valuemin aria-value min
17656 * @cfg {Number} aria_valuemax aria-value max
17657 * @cfg {String} label label for the progress bar
17658 * @cfg {String} panel (success | info | warning | danger )
17659 * @cfg {String} role role of the progress bar
17660 * @cfg {String} sr_only text
17664 * Create a new ProgressBar
17665 * @param {Object} config The config object
17668 Roo.bootstrap.ProgressBar = function(config){
17669 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17672 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17676 aria_valuemax : 100,
17682 getAutoCreate : function()
17687 cls: 'progress-bar',
17688 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17700 cfg.role = this.role;
17703 if(this.aria_valuenow){
17704 cfg['aria-valuenow'] = this.aria_valuenow;
17707 if(this.aria_valuemin){
17708 cfg['aria-valuemin'] = this.aria_valuemin;
17711 if(this.aria_valuemax){
17712 cfg['aria-valuemax'] = this.aria_valuemax;
17715 if(this.label && !this.sr_only){
17716 cfg.html = this.label;
17720 cfg.cls += ' progress-bar-' + this.panel;
17726 update : function(aria_valuenow)
17728 this.aria_valuenow = aria_valuenow;
17730 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17745 * @class Roo.bootstrap.TabGroup
17746 * @extends Roo.bootstrap.Column
17747 * Bootstrap Column class
17748 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17749 * @cfg {Boolean} carousel true to make the group behave like a carousel
17750 * @cfg {Boolean} bullets show bullets for the panels
17751 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17752 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17753 * @cfg {Boolean} showarrow (true|false) show arrow default true
17756 * Create a new TabGroup
17757 * @param {Object} config The config object
17760 Roo.bootstrap.TabGroup = function(config){
17761 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17763 this.navId = Roo.id();
17766 Roo.bootstrap.TabGroup.register(this);
17770 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17773 transition : false,
17778 slideOnTouch : false,
17781 getAutoCreate : function()
17783 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17785 cfg.cls += ' tab-content';
17787 if (this.carousel) {
17788 cfg.cls += ' carousel slide';
17791 cls : 'carousel-inner',
17795 if(this.bullets && !Roo.isTouch){
17798 cls : 'carousel-bullets',
17802 if(this.bullets_cls){
17803 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17810 cfg.cn[0].cn.push(bullets);
17813 if(this.showarrow){
17814 cfg.cn[0].cn.push({
17816 class : 'carousel-arrow',
17820 class : 'carousel-prev',
17824 class : 'fa fa-chevron-left'
17830 class : 'carousel-next',
17834 class : 'fa fa-chevron-right'
17847 initEvents: function()
17849 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17850 // this.el.on("touchstart", this.onTouchStart, this);
17853 if(this.autoslide){
17856 this.slideFn = window.setInterval(function() {
17857 _this.showPanelNext();
17861 if(this.showarrow){
17862 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17863 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17869 // onTouchStart : function(e, el, o)
17871 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17875 // this.showPanelNext();
17879 getChildContainer : function()
17881 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17885 * register a Navigation item
17886 * @param {Roo.bootstrap.NavItem} the navitem to add
17888 register : function(item)
17890 this.tabs.push( item);
17891 item.navId = this.navId; // not really needed..
17896 getActivePanel : function()
17899 Roo.each(this.tabs, function(t) {
17909 getPanelByName : function(n)
17912 Roo.each(this.tabs, function(t) {
17913 if (t.tabId == n) {
17921 indexOfPanel : function(p)
17924 Roo.each(this.tabs, function(t,i) {
17925 if (t.tabId == p.tabId) {
17934 * show a specific panel
17935 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17936 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17938 showPanel : function (pan)
17940 if(this.transition || typeof(pan) == 'undefined'){
17941 Roo.log("waiting for the transitionend");
17945 if (typeof(pan) == 'number') {
17946 pan = this.tabs[pan];
17949 if (typeof(pan) == 'string') {
17950 pan = this.getPanelByName(pan);
17953 var cur = this.getActivePanel();
17956 Roo.log('pan or acitve pan is undefined');
17960 if (pan.tabId == this.getActivePanel().tabId) {
17964 if (false === cur.fireEvent('beforedeactivate')) {
17968 if(this.bullets > 0 && !Roo.isTouch){
17969 this.setActiveBullet(this.indexOfPanel(pan));
17972 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17974 this.transition = true;
17975 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17976 var lr = dir == 'next' ? 'left' : 'right';
17977 pan.el.addClass(dir); // or prev
17978 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17979 cur.el.addClass(lr); // or right
17980 pan.el.addClass(lr);
17983 cur.el.on('transitionend', function() {
17984 Roo.log("trans end?");
17986 pan.el.removeClass([lr,dir]);
17987 pan.setActive(true);
17989 cur.el.removeClass([lr]);
17990 cur.setActive(false);
17992 _this.transition = false;
17994 }, this, { single: true } );
17999 cur.setActive(false);
18000 pan.setActive(true);
18005 showPanelNext : function()
18007 var i = this.indexOfPanel(this.getActivePanel());
18009 if (i >= this.tabs.length - 1 && !this.autoslide) {
18013 if (i >= this.tabs.length - 1 && this.autoslide) {
18017 this.showPanel(this.tabs[i+1]);
18020 showPanelPrev : function()
18022 var i = this.indexOfPanel(this.getActivePanel());
18024 if (i < 1 && !this.autoslide) {
18028 if (i < 1 && this.autoslide) {
18029 i = this.tabs.length;
18032 this.showPanel(this.tabs[i-1]);
18036 addBullet: function()
18038 if(!this.bullets || Roo.isTouch){
18041 var ctr = this.el.select('.carousel-bullets',true).first();
18042 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18043 var bullet = ctr.createChild({
18044 cls : 'bullet bullet-' + i
18045 },ctr.dom.lastChild);
18050 bullet.on('click', (function(e, el, o, ii, t){
18052 e.preventDefault();
18054 this.showPanel(ii);
18056 if(this.autoslide && this.slideFn){
18057 clearInterval(this.slideFn);
18058 this.slideFn = window.setInterval(function() {
18059 _this.showPanelNext();
18063 }).createDelegate(this, [i, bullet], true));
18068 setActiveBullet : function(i)
18074 Roo.each(this.el.select('.bullet', true).elements, function(el){
18075 el.removeClass('selected');
18078 var bullet = this.el.select('.bullet-' + i, true).first();
18084 bullet.addClass('selected');
18095 Roo.apply(Roo.bootstrap.TabGroup, {
18099 * register a Navigation Group
18100 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18102 register : function(navgrp)
18104 this.groups[navgrp.navId] = navgrp;
18108 * fetch a Navigation Group based on the navigation ID
18109 * if one does not exist , it will get created.
18110 * @param {string} the navgroup to add
18111 * @returns {Roo.bootstrap.NavGroup} the navgroup
18113 get: function(navId) {
18114 if (typeof(this.groups[navId]) == 'undefined') {
18115 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18117 return this.groups[navId] ;
18132 * @class Roo.bootstrap.TabPanel
18133 * @extends Roo.bootstrap.Component
18134 * Bootstrap TabPanel class
18135 * @cfg {Boolean} active panel active
18136 * @cfg {String} html panel content
18137 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18138 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18139 * @cfg {String} href click to link..
18143 * Create a new TabPanel
18144 * @param {Object} config The config object
18147 Roo.bootstrap.TabPanel = function(config){
18148 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18152 * Fires when the active status changes
18153 * @param {Roo.bootstrap.TabPanel} this
18154 * @param {Boolean} state the new state
18159 * @event beforedeactivate
18160 * Fires before a tab is de-activated - can be used to do validation on a form.
18161 * @param {Roo.bootstrap.TabPanel} this
18162 * @return {Boolean} false if there is an error
18165 'beforedeactivate': true
18168 this.tabId = this.tabId || Roo.id();
18172 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18180 getAutoCreate : function(){
18183 // item is needed for carousel - not sure if it has any effect otherwise
18184 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18185 html: this.html || ''
18189 cfg.cls += ' active';
18193 cfg.tabId = this.tabId;
18200 initEvents: function()
18202 var p = this.parent();
18204 this.navId = this.navId || p.navId;
18206 if (typeof(this.navId) != 'undefined') {
18207 // not really needed.. but just in case.. parent should be a NavGroup.
18208 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18212 var i = tg.tabs.length - 1;
18214 if(this.active && tg.bullets > 0 && i < tg.bullets){
18215 tg.setActiveBullet(i);
18219 this.el.on('click', this.onClick, this);
18222 this.el.on("touchstart", this.onTouchStart, this);
18223 this.el.on("touchmove", this.onTouchMove, this);
18224 this.el.on("touchend", this.onTouchEnd, this);
18229 onRender : function(ct, position)
18231 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18234 setActive : function(state)
18236 Roo.log("panel - set active " + this.tabId + "=" + state);
18238 this.active = state;
18240 this.el.removeClass('active');
18242 } else if (!this.el.hasClass('active')) {
18243 this.el.addClass('active');
18246 this.fireEvent('changed', this, state);
18249 onClick : function(e)
18251 e.preventDefault();
18253 if(!this.href.length){
18257 window.location.href = this.href;
18266 onTouchStart : function(e)
18268 this.swiping = false;
18270 this.startX = e.browserEvent.touches[0].clientX;
18271 this.startY = e.browserEvent.touches[0].clientY;
18274 onTouchMove : function(e)
18276 this.swiping = true;
18278 this.endX = e.browserEvent.touches[0].clientX;
18279 this.endY = e.browserEvent.touches[0].clientY;
18282 onTouchEnd : function(e)
18289 var tabGroup = this.parent();
18291 if(this.endX > this.startX){ // swiping right
18292 tabGroup.showPanelPrev();
18296 if(this.startX > this.endX){ // swiping left
18297 tabGroup.showPanelNext();
18316 * @class Roo.bootstrap.DateField
18317 * @extends Roo.bootstrap.Input
18318 * Bootstrap DateField class
18319 * @cfg {Number} weekStart default 0
18320 * @cfg {String} viewMode default empty, (months|years)
18321 * @cfg {String} minViewMode default empty, (months|years)
18322 * @cfg {Number} startDate default -Infinity
18323 * @cfg {Number} endDate default Infinity
18324 * @cfg {Boolean} todayHighlight default false
18325 * @cfg {Boolean} todayBtn default false
18326 * @cfg {Boolean} calendarWeeks default false
18327 * @cfg {Object} daysOfWeekDisabled default empty
18328 * @cfg {Boolean} singleMode default false (true | false)
18330 * @cfg {Boolean} keyboardNavigation default true
18331 * @cfg {String} language default en
18334 * Create a new DateField
18335 * @param {Object} config The config object
18338 Roo.bootstrap.DateField = function(config){
18339 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18343 * Fires when this field show.
18344 * @param {Roo.bootstrap.DateField} this
18345 * @param {Mixed} date The date value
18350 * Fires when this field hide.
18351 * @param {Roo.bootstrap.DateField} this
18352 * @param {Mixed} date The date value
18357 * Fires when select a date.
18358 * @param {Roo.bootstrap.DateField} this
18359 * @param {Mixed} date The date value
18363 * @event beforeselect
18364 * Fires when before select a date.
18365 * @param {Roo.bootstrap.DateField} this
18366 * @param {Mixed} date The date value
18368 beforeselect : true
18372 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18375 * @cfg {String} format
18376 * The default date format string which can be overriden for localization support. The format must be
18377 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18381 * @cfg {String} altFormats
18382 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18383 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18385 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18393 todayHighlight : false,
18399 keyboardNavigation: true,
18401 calendarWeeks: false,
18403 startDate: -Infinity,
18407 daysOfWeekDisabled: [],
18411 singleMode : false,
18413 UTCDate: function()
18415 return new Date(Date.UTC.apply(Date, arguments));
18418 UTCToday: function()
18420 var today = new Date();
18421 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18424 getDate: function() {
18425 var d = this.getUTCDate();
18426 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18429 getUTCDate: function() {
18433 setDate: function(d) {
18434 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18437 setUTCDate: function(d) {
18439 this.setValue(this.formatDate(this.date));
18442 onRender: function(ct, position)
18445 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18447 this.language = this.language || 'en';
18448 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18449 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18451 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18452 this.format = this.format || 'm/d/y';
18453 this.isInline = false;
18454 this.isInput = true;
18455 this.component = this.el.select('.add-on', true).first() || false;
18456 this.component = (this.component && this.component.length === 0) ? false : this.component;
18457 this.hasInput = this.component && this.inputEl().length;
18459 if (typeof(this.minViewMode === 'string')) {
18460 switch (this.minViewMode) {
18462 this.minViewMode = 1;
18465 this.minViewMode = 2;
18468 this.minViewMode = 0;
18473 if (typeof(this.viewMode === 'string')) {
18474 switch (this.viewMode) {
18487 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18489 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18491 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18493 this.picker().on('mousedown', this.onMousedown, this);
18494 this.picker().on('click', this.onClick, this);
18496 this.picker().addClass('datepicker-dropdown');
18498 this.startViewMode = this.viewMode;
18500 if(this.singleMode){
18501 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18502 v.setVisibilityMode(Roo.Element.DISPLAY);
18506 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18507 v.setStyle('width', '189px');
18511 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18512 if(!this.calendarWeeks){
18517 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18518 v.attr('colspan', function(i, val){
18519 return parseInt(val) + 1;
18524 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18526 this.setStartDate(this.startDate);
18527 this.setEndDate(this.endDate);
18529 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18536 if(this.isInline) {
18541 picker : function()
18543 return this.pickerEl;
18544 // return this.el.select('.datepicker', true).first();
18547 fillDow: function()
18549 var dowCnt = this.weekStart;
18558 if(this.calendarWeeks){
18566 while (dowCnt < this.weekStart + 7) {
18570 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18574 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18577 fillMonths: function()
18580 var months = this.picker().select('>.datepicker-months td', true).first();
18582 months.dom.innerHTML = '';
18588 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18591 months.createChild(month);
18598 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;
18600 if (this.date < this.startDate) {
18601 this.viewDate = new Date(this.startDate);
18602 } else if (this.date > this.endDate) {
18603 this.viewDate = new Date(this.endDate);
18605 this.viewDate = new Date(this.date);
18613 var d = new Date(this.viewDate),
18614 year = d.getUTCFullYear(),
18615 month = d.getUTCMonth(),
18616 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18617 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18618 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18619 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18620 currentDate = this.date && this.date.valueOf(),
18621 today = this.UTCToday();
18623 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18625 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18627 // this.picker.select('>tfoot th.today').
18628 // .text(dates[this.language].today)
18629 // .toggle(this.todayBtn !== false);
18631 this.updateNavArrows();
18634 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18636 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18638 prevMonth.setUTCDate(day);
18640 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18642 var nextMonth = new Date(prevMonth);
18644 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18646 nextMonth = nextMonth.valueOf();
18648 var fillMonths = false;
18650 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18652 while(prevMonth.valueOf() < nextMonth) {
18655 if (prevMonth.getUTCDay() === this.weekStart) {
18657 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18665 if(this.calendarWeeks){
18666 // ISO 8601: First week contains first thursday.
18667 // ISO also states week starts on Monday, but we can be more abstract here.
18669 // Start of current week: based on weekstart/current date
18670 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18671 // Thursday of this week
18672 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18673 // First Thursday of year, year from thursday
18674 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18675 // Calendar week: ms between thursdays, div ms per day, div 7 days
18676 calWeek = (th - yth) / 864e5 / 7 + 1;
18678 fillMonths.cn.push({
18686 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18688 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18691 if (this.todayHighlight &&
18692 prevMonth.getUTCFullYear() == today.getFullYear() &&
18693 prevMonth.getUTCMonth() == today.getMonth() &&
18694 prevMonth.getUTCDate() == today.getDate()) {
18695 clsName += ' today';
18698 if (currentDate && prevMonth.valueOf() === currentDate) {
18699 clsName += ' active';
18702 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18703 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18704 clsName += ' disabled';
18707 fillMonths.cn.push({
18709 cls: 'day ' + clsName,
18710 html: prevMonth.getDate()
18713 prevMonth.setDate(prevMonth.getDate()+1);
18716 var currentYear = this.date && this.date.getUTCFullYear();
18717 var currentMonth = this.date && this.date.getUTCMonth();
18719 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18721 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18722 v.removeClass('active');
18724 if(currentYear === year && k === currentMonth){
18725 v.addClass('active');
18728 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18729 v.addClass('disabled');
18735 year = parseInt(year/10, 10) * 10;
18737 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18739 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18742 for (var i = -1; i < 11; i++) {
18743 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18745 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18753 showMode: function(dir)
18756 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18759 Roo.each(this.picker().select('>div',true).elements, function(v){
18760 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18763 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18768 if(this.isInline) {
18772 this.picker().removeClass(['bottom', 'top']);
18774 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18776 * place to the top of element!
18780 this.picker().addClass('top');
18781 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18786 this.picker().addClass('bottom');
18788 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18791 parseDate : function(value)
18793 if(!value || value instanceof Date){
18796 var v = Date.parseDate(value, this.format);
18797 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18798 v = Date.parseDate(value, 'Y-m-d');
18800 if(!v && this.altFormats){
18801 if(!this.altFormatsArray){
18802 this.altFormatsArray = this.altFormats.split("|");
18804 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18805 v = Date.parseDate(value, this.altFormatsArray[i]);
18811 formatDate : function(date, fmt)
18813 return (!date || !(date instanceof Date)) ?
18814 date : date.dateFormat(fmt || this.format);
18817 onFocus : function()
18819 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18823 onBlur : function()
18825 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18827 var d = this.inputEl().getValue();
18836 this.picker().show();
18840 this.fireEvent('show', this, this.date);
18845 if(this.isInline) {
18848 this.picker().hide();
18849 this.viewMode = this.startViewMode;
18852 this.fireEvent('hide', this, this.date);
18856 onMousedown: function(e)
18858 e.stopPropagation();
18859 e.preventDefault();
18864 Roo.bootstrap.DateField.superclass.keyup.call(this);
18868 setValue: function(v)
18870 if(this.fireEvent('beforeselect', this, v) !== false){
18871 var d = new Date(this.parseDate(v) ).clearTime();
18873 if(isNaN(d.getTime())){
18874 this.date = this.viewDate = '';
18875 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18879 v = this.formatDate(d);
18881 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18883 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18887 this.fireEvent('select', this, this.date);
18891 getValue: function()
18893 return this.formatDate(this.date);
18896 fireKey: function(e)
18898 if (!this.picker().isVisible()){
18899 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18905 var dateChanged = false,
18907 newDate, newViewDate;
18912 e.preventDefault();
18916 if (!this.keyboardNavigation) {
18919 dir = e.keyCode == 37 ? -1 : 1;
18922 newDate = this.moveYear(this.date, dir);
18923 newViewDate = this.moveYear(this.viewDate, dir);
18924 } else if (e.shiftKey){
18925 newDate = this.moveMonth(this.date, dir);
18926 newViewDate = this.moveMonth(this.viewDate, dir);
18928 newDate = new Date(this.date);
18929 newDate.setUTCDate(this.date.getUTCDate() + dir);
18930 newViewDate = new Date(this.viewDate);
18931 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18933 if (this.dateWithinRange(newDate)){
18934 this.date = newDate;
18935 this.viewDate = newViewDate;
18936 this.setValue(this.formatDate(this.date));
18938 e.preventDefault();
18939 dateChanged = true;
18944 if (!this.keyboardNavigation) {
18947 dir = e.keyCode == 38 ? -1 : 1;
18949 newDate = this.moveYear(this.date, dir);
18950 newViewDate = this.moveYear(this.viewDate, dir);
18951 } else if (e.shiftKey){
18952 newDate = this.moveMonth(this.date, dir);
18953 newViewDate = this.moveMonth(this.viewDate, dir);
18955 newDate = new Date(this.date);
18956 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18957 newViewDate = new Date(this.viewDate);
18958 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18960 if (this.dateWithinRange(newDate)){
18961 this.date = newDate;
18962 this.viewDate = newViewDate;
18963 this.setValue(this.formatDate(this.date));
18965 e.preventDefault();
18966 dateChanged = true;
18970 this.setValue(this.formatDate(this.date));
18972 e.preventDefault();
18975 this.setValue(this.formatDate(this.date));
18989 onClick: function(e)
18991 e.stopPropagation();
18992 e.preventDefault();
18994 var target = e.getTarget();
18996 if(target.nodeName.toLowerCase() === 'i'){
18997 target = Roo.get(target).dom.parentNode;
19000 var nodeName = target.nodeName;
19001 var className = target.className;
19002 var html = target.innerHTML;
19003 //Roo.log(nodeName);
19005 switch(nodeName.toLowerCase()) {
19007 switch(className) {
19013 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19014 switch(this.viewMode){
19016 this.viewDate = this.moveMonth(this.viewDate, dir);
19020 this.viewDate = this.moveYear(this.viewDate, dir);
19026 var date = new Date();
19027 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19029 this.setValue(this.formatDate(this.date));
19036 if (className.indexOf('disabled') < 0) {
19037 this.viewDate.setUTCDate(1);
19038 if (className.indexOf('month') > -1) {
19039 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19041 var year = parseInt(html, 10) || 0;
19042 this.viewDate.setUTCFullYear(year);
19046 if(this.singleMode){
19047 this.setValue(this.formatDate(this.viewDate));
19058 //Roo.log(className);
19059 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19060 var day = parseInt(html, 10) || 1;
19061 var year = this.viewDate.getUTCFullYear(),
19062 month = this.viewDate.getUTCMonth();
19064 if (className.indexOf('old') > -1) {
19071 } else if (className.indexOf('new') > -1) {
19079 //Roo.log([year,month,day]);
19080 this.date = this.UTCDate(year, month, day,0,0,0,0);
19081 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19083 //Roo.log(this.formatDate(this.date));
19084 this.setValue(this.formatDate(this.date));
19091 setStartDate: function(startDate)
19093 this.startDate = startDate || -Infinity;
19094 if (this.startDate !== -Infinity) {
19095 this.startDate = this.parseDate(this.startDate);
19098 this.updateNavArrows();
19101 setEndDate: function(endDate)
19103 this.endDate = endDate || Infinity;
19104 if (this.endDate !== Infinity) {
19105 this.endDate = this.parseDate(this.endDate);
19108 this.updateNavArrows();
19111 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19113 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19114 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19115 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19117 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19118 return parseInt(d, 10);
19121 this.updateNavArrows();
19124 updateNavArrows: function()
19126 if(this.singleMode){
19130 var d = new Date(this.viewDate),
19131 year = d.getUTCFullYear(),
19132 month = d.getUTCMonth();
19134 Roo.each(this.picker().select('.prev', true).elements, function(v){
19136 switch (this.viewMode) {
19139 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19145 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19152 Roo.each(this.picker().select('.next', true).elements, function(v){
19154 switch (this.viewMode) {
19157 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19163 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19171 moveMonth: function(date, dir)
19176 var new_date = new Date(date.valueOf()),
19177 day = new_date.getUTCDate(),
19178 month = new_date.getUTCMonth(),
19179 mag = Math.abs(dir),
19181 dir = dir > 0 ? 1 : -1;
19184 // If going back one month, make sure month is not current month
19185 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19187 return new_date.getUTCMonth() == month;
19189 // If going forward one month, make sure month is as expected
19190 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19192 return new_date.getUTCMonth() != new_month;
19194 new_month = month + dir;
19195 new_date.setUTCMonth(new_month);
19196 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19197 if (new_month < 0 || new_month > 11) {
19198 new_month = (new_month + 12) % 12;
19201 // For magnitudes >1, move one month at a time...
19202 for (var i=0; i<mag; i++) {
19203 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19204 new_date = this.moveMonth(new_date, dir);
19206 // ...then reset the day, keeping it in the new month
19207 new_month = new_date.getUTCMonth();
19208 new_date.setUTCDate(day);
19210 return new_month != new_date.getUTCMonth();
19213 // Common date-resetting loop -- if date is beyond end of month, make it
19216 new_date.setUTCDate(--day);
19217 new_date.setUTCMonth(new_month);
19222 moveYear: function(date, dir)
19224 return this.moveMonth(date, dir*12);
19227 dateWithinRange: function(date)
19229 return date >= this.startDate && date <= this.endDate;
19235 this.picker().remove();
19238 validateValue : function(value)
19240 if(this.getVisibilityEl().hasClass('hidden')){
19244 if(value.length < 1) {
19245 if(this.allowBlank){
19251 if(value.length < this.minLength){
19254 if(value.length > this.maxLength){
19258 var vt = Roo.form.VTypes;
19259 if(!vt[this.vtype](value, this)){
19263 if(typeof this.validator == "function"){
19264 var msg = this.validator(value);
19270 if(this.regex && !this.regex.test(value)){
19274 if(typeof(this.parseDate(value)) == 'undefined'){
19278 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19282 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19290 setVisible : function(visible)
19296 this.getEl().removeClass('hidden');
19302 this.getEl().addClass('hidden');
19307 Roo.apply(Roo.bootstrap.DateField, {
19318 html: '<i class="fa fa-arrow-left"/>'
19328 html: '<i class="fa fa-arrow-right"/>'
19370 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19371 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19372 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19373 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19374 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19387 navFnc: 'FullYear',
19392 navFnc: 'FullYear',
19397 Roo.apply(Roo.bootstrap.DateField, {
19401 cls: 'datepicker dropdown-menu roo-dynamic',
19405 cls: 'datepicker-days',
19409 cls: 'table-condensed',
19411 Roo.bootstrap.DateField.head,
19415 Roo.bootstrap.DateField.footer
19422 cls: 'datepicker-months',
19426 cls: 'table-condensed',
19428 Roo.bootstrap.DateField.head,
19429 Roo.bootstrap.DateField.content,
19430 Roo.bootstrap.DateField.footer
19437 cls: 'datepicker-years',
19441 cls: 'table-condensed',
19443 Roo.bootstrap.DateField.head,
19444 Roo.bootstrap.DateField.content,
19445 Roo.bootstrap.DateField.footer
19464 * @class Roo.bootstrap.TimeField
19465 * @extends Roo.bootstrap.Input
19466 * Bootstrap DateField class
19470 * Create a new TimeField
19471 * @param {Object} config The config object
19474 Roo.bootstrap.TimeField = function(config){
19475 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19479 * Fires when this field show.
19480 * @param {Roo.bootstrap.DateField} thisthis
19481 * @param {Mixed} date The date value
19486 * Fires when this field hide.
19487 * @param {Roo.bootstrap.DateField} this
19488 * @param {Mixed} date The date value
19493 * Fires when select a date.
19494 * @param {Roo.bootstrap.DateField} this
19495 * @param {Mixed} date The date value
19501 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19504 * @cfg {String} format
19505 * The default time format string which can be overriden for localization support. The format must be
19506 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19510 onRender: function(ct, position)
19513 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19515 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19517 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19519 this.pop = this.picker().select('>.datepicker-time',true).first();
19520 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19522 this.picker().on('mousedown', this.onMousedown, this);
19523 this.picker().on('click', this.onClick, this);
19525 this.picker().addClass('datepicker-dropdown');
19530 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19531 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19532 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19533 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19534 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19535 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19539 fireKey: function(e){
19540 if (!this.picker().isVisible()){
19541 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19547 e.preventDefault();
19555 this.onTogglePeriod();
19558 this.onIncrementMinutes();
19561 this.onDecrementMinutes();
19570 onClick: function(e) {
19571 e.stopPropagation();
19572 e.preventDefault();
19575 picker : function()
19577 return this.el.select('.datepicker', true).first();
19580 fillTime: function()
19582 var time = this.pop.select('tbody', true).first();
19584 time.dom.innerHTML = '';
19599 cls: 'hours-up glyphicon glyphicon-chevron-up'
19619 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19640 cls: 'timepicker-hour',
19655 cls: 'timepicker-minute',
19670 cls: 'btn btn-primary period',
19692 cls: 'hours-down glyphicon glyphicon-chevron-down'
19712 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19730 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19737 var hours = this.time.getHours();
19738 var minutes = this.time.getMinutes();
19751 hours = hours - 12;
19755 hours = '0' + hours;
19759 minutes = '0' + minutes;
19762 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19763 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19764 this.pop.select('button', true).first().dom.innerHTML = period;
19770 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19772 var cls = ['bottom'];
19774 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19781 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19786 this.picker().addClass(cls.join('-'));
19790 Roo.each(cls, function(c){
19792 _this.picker().setTop(_this.inputEl().getHeight());
19796 _this.picker().setTop(0 - _this.picker().getHeight());
19801 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19805 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19812 onFocus : function()
19814 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19818 onBlur : function()
19820 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19826 this.picker().show();
19831 this.fireEvent('show', this, this.date);
19836 this.picker().hide();
19839 this.fireEvent('hide', this, this.date);
19842 setTime : function()
19845 this.setValue(this.time.format(this.format));
19847 this.fireEvent('select', this, this.date);
19852 onMousedown: function(e){
19853 e.stopPropagation();
19854 e.preventDefault();
19857 onIncrementHours: function()
19859 Roo.log('onIncrementHours');
19860 this.time = this.time.add(Date.HOUR, 1);
19865 onDecrementHours: function()
19867 Roo.log('onDecrementHours');
19868 this.time = this.time.add(Date.HOUR, -1);
19872 onIncrementMinutes: function()
19874 Roo.log('onIncrementMinutes');
19875 this.time = this.time.add(Date.MINUTE, 1);
19879 onDecrementMinutes: function()
19881 Roo.log('onDecrementMinutes');
19882 this.time = this.time.add(Date.MINUTE, -1);
19886 onTogglePeriod: function()
19888 Roo.log('onTogglePeriod');
19889 this.time = this.time.add(Date.HOUR, 12);
19896 Roo.apply(Roo.bootstrap.TimeField, {
19926 cls: 'btn btn-info ok',
19938 Roo.apply(Roo.bootstrap.TimeField, {
19942 cls: 'datepicker dropdown-menu',
19946 cls: 'datepicker-time',
19950 cls: 'table-condensed',
19952 Roo.bootstrap.TimeField.content,
19953 Roo.bootstrap.TimeField.footer
19972 * @class Roo.bootstrap.MonthField
19973 * @extends Roo.bootstrap.Input
19974 * Bootstrap MonthField class
19976 * @cfg {String} language default en
19979 * Create a new MonthField
19980 * @param {Object} config The config object
19983 Roo.bootstrap.MonthField = function(config){
19984 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19989 * Fires when this field show.
19990 * @param {Roo.bootstrap.MonthField} this
19991 * @param {Mixed} date The date value
19996 * Fires when this field hide.
19997 * @param {Roo.bootstrap.MonthField} this
19998 * @param {Mixed} date The date value
20003 * Fires when select a date.
20004 * @param {Roo.bootstrap.MonthField} this
20005 * @param {String} oldvalue The old value
20006 * @param {String} newvalue The new value
20012 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20014 onRender: function(ct, position)
20017 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20019 this.language = this.language || 'en';
20020 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20021 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20023 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20024 this.isInline = false;
20025 this.isInput = true;
20026 this.component = this.el.select('.add-on', true).first() || false;
20027 this.component = (this.component && this.component.length === 0) ? false : this.component;
20028 this.hasInput = this.component && this.inputEL().length;
20030 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20032 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20034 this.picker().on('mousedown', this.onMousedown, this);
20035 this.picker().on('click', this.onClick, this);
20037 this.picker().addClass('datepicker-dropdown');
20039 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20040 v.setStyle('width', '189px');
20047 if(this.isInline) {
20053 setValue: function(v, suppressEvent)
20055 var o = this.getValue();
20057 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20061 if(suppressEvent !== true){
20062 this.fireEvent('select', this, o, v);
20067 getValue: function()
20072 onClick: function(e)
20074 e.stopPropagation();
20075 e.preventDefault();
20077 var target = e.getTarget();
20079 if(target.nodeName.toLowerCase() === 'i'){
20080 target = Roo.get(target).dom.parentNode;
20083 var nodeName = target.nodeName;
20084 var className = target.className;
20085 var html = target.innerHTML;
20087 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20091 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20093 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20099 picker : function()
20101 return this.pickerEl;
20104 fillMonths: function()
20107 var months = this.picker().select('>.datepicker-months td', true).first();
20109 months.dom.innerHTML = '';
20115 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20118 months.createChild(month);
20127 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20128 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20131 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20132 e.removeClass('active');
20134 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20135 e.addClass('active');
20142 if(this.isInline) {
20146 this.picker().removeClass(['bottom', 'top']);
20148 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20150 * place to the top of element!
20154 this.picker().addClass('top');
20155 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20160 this.picker().addClass('bottom');
20162 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20165 onFocus : function()
20167 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20171 onBlur : function()
20173 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20175 var d = this.inputEl().getValue();
20184 this.picker().show();
20185 this.picker().select('>.datepicker-months', true).first().show();
20189 this.fireEvent('show', this, this.date);
20194 if(this.isInline) {
20197 this.picker().hide();
20198 this.fireEvent('hide', this, this.date);
20202 onMousedown: function(e)
20204 e.stopPropagation();
20205 e.preventDefault();
20210 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20214 fireKey: function(e)
20216 if (!this.picker().isVisible()){
20217 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20228 e.preventDefault();
20232 dir = e.keyCode == 37 ? -1 : 1;
20234 this.vIndex = this.vIndex + dir;
20236 if(this.vIndex < 0){
20240 if(this.vIndex > 11){
20244 if(isNaN(this.vIndex)){
20248 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20254 dir = e.keyCode == 38 ? -1 : 1;
20256 this.vIndex = this.vIndex + dir * 4;
20258 if(this.vIndex < 0){
20262 if(this.vIndex > 11){
20266 if(isNaN(this.vIndex)){
20270 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20275 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20276 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20280 e.preventDefault();
20283 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20284 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20300 this.picker().remove();
20305 Roo.apply(Roo.bootstrap.MonthField, {
20324 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20325 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20330 Roo.apply(Roo.bootstrap.MonthField, {
20334 cls: 'datepicker dropdown-menu roo-dynamic',
20338 cls: 'datepicker-months',
20342 cls: 'table-condensed',
20344 Roo.bootstrap.DateField.content
20364 * @class Roo.bootstrap.CheckBox
20365 * @extends Roo.bootstrap.Input
20366 * Bootstrap CheckBox class
20368 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20369 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20370 * @cfg {String} boxLabel The text that appears beside the checkbox
20371 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20372 * @cfg {Boolean} checked initnal the element
20373 * @cfg {Boolean} inline inline the element (default false)
20374 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20375 * @cfg {String} tooltip label tooltip
20378 * Create a new CheckBox
20379 * @param {Object} config The config object
20382 Roo.bootstrap.CheckBox = function(config){
20383 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20388 * Fires when the element is checked or unchecked.
20389 * @param {Roo.bootstrap.CheckBox} this This input
20390 * @param {Boolean} checked The new checked value
20395 * Fires when the element is click.
20396 * @param {Roo.bootstrap.CheckBox} this This input
20403 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20405 inputType: 'checkbox',
20414 getAutoCreate : function()
20416 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20422 cfg.cls = 'form-group ' + this.inputType; //input-group
20425 cfg.cls += ' ' + this.inputType + '-inline';
20431 type : this.inputType,
20432 value : this.inputValue,
20433 cls : 'roo-' + this.inputType, //'form-box',
20434 placeholder : this.placeholder || ''
20438 if(this.inputType != 'radio'){
20442 cls : 'roo-hidden-value',
20443 value : this.checked ? this.inputValue : this.valueOff
20448 if (this.weight) { // Validity check?
20449 cfg.cls += " " + this.inputType + "-" + this.weight;
20452 if (this.disabled) {
20453 input.disabled=true;
20457 input.checked = this.checked;
20462 input.name = this.name;
20464 if(this.inputType != 'radio'){
20465 hidden.name = this.name;
20466 input.name = '_hidden_' + this.name;
20471 input.cls += ' input-' + this.size;
20476 ['xs','sm','md','lg'].map(function(size){
20477 if (settings[size]) {
20478 cfg.cls += ' col-' + size + '-' + settings[size];
20482 var inputblock = input;
20484 if (this.before || this.after) {
20487 cls : 'input-group',
20492 inputblock.cn.push({
20494 cls : 'input-group-addon',
20499 inputblock.cn.push(input);
20501 if(this.inputType != 'radio'){
20502 inputblock.cn.push(hidden);
20506 inputblock.cn.push({
20508 cls : 'input-group-addon',
20515 if (align ==='left' && this.fieldLabel.length) {
20516 // Roo.log("left and has label");
20521 cls : 'control-label',
20522 html : this.fieldLabel
20532 if(this.labelWidth > 12){
20533 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20536 if(this.labelWidth < 13 && this.labelmd == 0){
20537 this.labelmd = this.labelWidth;
20540 if(this.labellg > 0){
20541 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20542 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20545 if(this.labelmd > 0){
20546 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20547 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20550 if(this.labelsm > 0){
20551 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20552 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20555 if(this.labelxs > 0){
20556 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20557 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20560 } else if ( this.fieldLabel.length) {
20561 // Roo.log(" label");
20565 tag: this.boxLabel ? 'span' : 'label',
20567 cls: 'control-label box-input-label',
20568 //cls : 'input-group-addon',
20569 html : this.fieldLabel
20578 // Roo.log(" no label && no align");
20579 cfg.cn = [ inputblock ] ;
20585 var boxLabelCfg = {
20587 //'for': id, // box label is handled by onclick - so no for...
20589 html: this.boxLabel
20593 boxLabelCfg.tooltip = this.tooltip;
20596 cfg.cn.push(boxLabelCfg);
20599 if(this.inputType != 'radio'){
20600 cfg.cn.push(hidden);
20608 * return the real input element.
20610 inputEl: function ()
20612 return this.el.select('input.roo-' + this.inputType,true).first();
20614 hiddenEl: function ()
20616 return this.el.select('input.roo-hidden-value',true).first();
20619 labelEl: function()
20621 return this.el.select('label.control-label',true).first();
20623 /* depricated... */
20627 return this.labelEl();
20630 boxLabelEl: function()
20632 return this.el.select('label.box-label',true).first();
20635 initEvents : function()
20637 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20639 this.inputEl().on('click', this.onClick, this);
20641 if (this.boxLabel) {
20642 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20645 this.startValue = this.getValue();
20648 Roo.bootstrap.CheckBox.register(this);
20652 onClick : function(e)
20654 if(this.fireEvent('click', this, e) !== false){
20655 this.setChecked(!this.checked);
20660 setChecked : function(state,suppressEvent)
20662 this.startValue = this.getValue();
20664 if(this.inputType == 'radio'){
20666 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20667 e.dom.checked = false;
20670 this.inputEl().dom.checked = true;
20672 this.inputEl().dom.value = this.inputValue;
20674 if(suppressEvent !== true){
20675 this.fireEvent('check', this, true);
20683 this.checked = state;
20685 this.inputEl().dom.checked = state;
20688 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20690 if(suppressEvent !== true){
20691 this.fireEvent('check', this, state);
20697 getValue : function()
20699 if(this.inputType == 'radio'){
20700 return this.getGroupValue();
20703 return this.hiddenEl().dom.value;
20707 getGroupValue : function()
20709 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20713 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20716 setValue : function(v,suppressEvent)
20718 if(this.inputType == 'radio'){
20719 this.setGroupValue(v, suppressEvent);
20723 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20728 setGroupValue : function(v, suppressEvent)
20730 this.startValue = this.getValue();
20732 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20733 e.dom.checked = false;
20735 if(e.dom.value == v){
20736 e.dom.checked = true;
20740 if(suppressEvent !== true){
20741 this.fireEvent('check', this, true);
20749 validate : function()
20751 if(this.getVisibilityEl().hasClass('hidden')){
20757 (this.inputType == 'radio' && this.validateRadio()) ||
20758 (this.inputType == 'checkbox' && this.validateCheckbox())
20764 this.markInvalid();
20768 validateRadio : function()
20770 if(this.getVisibilityEl().hasClass('hidden')){
20774 if(this.allowBlank){
20780 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20781 if(!e.dom.checked){
20793 validateCheckbox : function()
20796 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20797 //return (this.getValue() == this.inputValue) ? true : false;
20800 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20808 for(var i in group){
20809 if(group[i].el.isVisible(true)){
20817 for(var i in group){
20822 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20829 * Mark this field as valid
20831 markValid : function()
20835 this.fireEvent('valid', this);
20837 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20840 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20847 if(this.inputType == 'radio'){
20848 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20849 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20850 e.findParent('.form-group', false, true).addClass(_this.validClass);
20857 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20858 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20862 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20868 for(var i in group){
20869 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20870 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20875 * Mark this field as invalid
20876 * @param {String} msg The validation message
20878 markInvalid : function(msg)
20880 if(this.allowBlank){
20886 this.fireEvent('invalid', this, msg);
20888 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20891 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20895 label.markInvalid();
20898 if(this.inputType == 'radio'){
20899 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20900 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20901 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20908 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20909 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20913 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20919 for(var i in group){
20920 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20921 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20926 clearInvalid : function()
20928 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20930 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20932 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20934 if (label && label.iconEl) {
20935 label.iconEl.removeClass(label.validClass);
20936 label.iconEl.removeClass(label.invalidClass);
20940 disable : function()
20942 if(this.inputType != 'radio'){
20943 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20950 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20951 _this.getActionEl().addClass(this.disabledClass);
20952 e.dom.disabled = true;
20956 this.disabled = true;
20957 this.fireEvent("disable", this);
20961 enable : function()
20963 if(this.inputType != 'radio'){
20964 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20971 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20972 _this.getActionEl().removeClass(this.disabledClass);
20973 e.dom.disabled = false;
20977 this.disabled = false;
20978 this.fireEvent("enable", this);
20982 setBoxLabel : function(v)
20987 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20993 Roo.apply(Roo.bootstrap.CheckBox, {
20998 * register a CheckBox Group
20999 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21001 register : function(checkbox)
21003 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21004 this.groups[checkbox.groupId] = {};
21007 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21011 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21015 * fetch a CheckBox Group based on the group ID
21016 * @param {string} the group ID
21017 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21019 get: function(groupId) {
21020 if (typeof(this.groups[groupId]) == 'undefined') {
21024 return this.groups[groupId] ;
21037 * @class Roo.bootstrap.Radio
21038 * @extends Roo.bootstrap.Component
21039 * Bootstrap Radio class
21040 * @cfg {String} boxLabel - the label associated
21041 * @cfg {String} value - the value of radio
21044 * Create a new Radio
21045 * @param {Object} config The config object
21047 Roo.bootstrap.Radio = function(config){
21048 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21052 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21058 getAutoCreate : function()
21062 cls : 'form-group radio',
21067 html : this.boxLabel
21075 initEvents : function()
21077 this.parent().register(this);
21079 this.el.on('click', this.onClick, this);
21083 onClick : function(e)
21085 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21086 this.setChecked(true);
21090 setChecked : function(state, suppressEvent)
21092 this.parent().setValue(this.value, suppressEvent);
21096 setBoxLabel : function(v)
21101 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21116 * @class Roo.bootstrap.SecurePass
21117 * @extends Roo.bootstrap.Input
21118 * Bootstrap SecurePass class
21122 * Create a new SecurePass
21123 * @param {Object} config The config object
21126 Roo.bootstrap.SecurePass = function (config) {
21127 // these go here, so the translation tool can replace them..
21129 PwdEmpty: "Please type a password, and then retype it to confirm.",
21130 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21131 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21132 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21133 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21134 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21135 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21136 TooWeak: "Your password is Too Weak."
21138 this.meterLabel = "Password strength:";
21139 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21140 this.meterClass = [
21141 "roo-password-meter-tooweak",
21142 "roo-password-meter-weak",
21143 "roo-password-meter-medium",
21144 "roo-password-meter-strong",
21145 "roo-password-meter-grey"
21150 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21153 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21155 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21157 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21158 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21159 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21160 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21161 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21162 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21163 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21173 * @cfg {String/Object} Label for the strength meter (defaults to
21174 * 'Password strength:')
21179 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21180 * ['Weak', 'Medium', 'Strong'])
21183 pwdStrengths: false,
21196 initEvents: function ()
21198 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21200 if (this.el.is('input[type=password]') && Roo.isSafari) {
21201 this.el.on('keydown', this.SafariOnKeyDown, this);
21204 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21207 onRender: function (ct, position)
21209 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21210 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21211 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21213 this.trigger.createChild({
21218 cls: 'roo-password-meter-grey col-xs-12',
21221 //width: this.meterWidth + 'px'
21225 cls: 'roo-password-meter-text'
21231 if (this.hideTrigger) {
21232 this.trigger.setDisplayed(false);
21234 this.setSize(this.width || '', this.height || '');
21237 onDestroy: function ()
21239 if (this.trigger) {
21240 this.trigger.removeAllListeners();
21241 this.trigger.remove();
21244 this.wrap.remove();
21246 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21249 checkStrength: function ()
21251 var pwd = this.inputEl().getValue();
21252 if (pwd == this._lastPwd) {
21257 if (this.ClientSideStrongPassword(pwd)) {
21259 } else if (this.ClientSideMediumPassword(pwd)) {
21261 } else if (this.ClientSideWeakPassword(pwd)) {
21267 Roo.log('strength1: ' + strength);
21269 //var pm = this.trigger.child('div/div/div').dom;
21270 var pm = this.trigger.child('div/div');
21271 pm.removeClass(this.meterClass);
21272 pm.addClass(this.meterClass[strength]);
21275 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21277 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21279 this._lastPwd = pwd;
21283 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21285 this._lastPwd = '';
21287 var pm = this.trigger.child('div/div');
21288 pm.removeClass(this.meterClass);
21289 pm.addClass('roo-password-meter-grey');
21292 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21295 this.inputEl().dom.type='password';
21298 validateValue: function (value)
21301 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21304 if (value.length == 0) {
21305 if (this.allowBlank) {
21306 this.clearInvalid();
21310 this.markInvalid(this.errors.PwdEmpty);
21311 this.errorMsg = this.errors.PwdEmpty;
21319 if ('[\x21-\x7e]*'.match(value)) {
21320 this.markInvalid(this.errors.PwdBadChar);
21321 this.errorMsg = this.errors.PwdBadChar;
21324 if (value.length < 6) {
21325 this.markInvalid(this.errors.PwdShort);
21326 this.errorMsg = this.errors.PwdShort;
21329 if (value.length > 16) {
21330 this.markInvalid(this.errors.PwdLong);
21331 this.errorMsg = this.errors.PwdLong;
21335 if (this.ClientSideStrongPassword(value)) {
21337 } else if (this.ClientSideMediumPassword(value)) {
21339 } else if (this.ClientSideWeakPassword(value)) {
21346 if (strength < 2) {
21347 //this.markInvalid(this.errors.TooWeak);
21348 this.errorMsg = this.errors.TooWeak;
21353 console.log('strength2: ' + strength);
21355 //var pm = this.trigger.child('div/div/div').dom;
21357 var pm = this.trigger.child('div/div');
21358 pm.removeClass(this.meterClass);
21359 pm.addClass(this.meterClass[strength]);
21361 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21363 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21365 this.errorMsg = '';
21369 CharacterSetChecks: function (type)
21372 this.fResult = false;
21375 isctype: function (character, type)
21378 case this.kCapitalLetter:
21379 if (character >= 'A' && character <= 'Z') {
21384 case this.kSmallLetter:
21385 if (character >= 'a' && character <= 'z') {
21391 if (character >= '0' && character <= '9') {
21396 case this.kPunctuation:
21397 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21408 IsLongEnough: function (pwd, size)
21410 return !(pwd == null || isNaN(size) || pwd.length < size);
21413 SpansEnoughCharacterSets: function (word, nb)
21415 if (!this.IsLongEnough(word, nb))
21420 var characterSetChecks = new Array(
21421 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21422 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21425 for (var index = 0; index < word.length; ++index) {
21426 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21427 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21428 characterSetChecks[nCharSet].fResult = true;
21435 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21436 if (characterSetChecks[nCharSet].fResult) {
21441 if (nCharSets < nb) {
21447 ClientSideStrongPassword: function (pwd)
21449 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21452 ClientSideMediumPassword: function (pwd)
21454 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21457 ClientSideWeakPassword: function (pwd)
21459 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21462 })//<script type="text/javascript">
21465 * Based Ext JS Library 1.1.1
21466 * Copyright(c) 2006-2007, Ext JS, LLC.
21472 * @class Roo.HtmlEditorCore
21473 * @extends Roo.Component
21474 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21476 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21479 Roo.HtmlEditorCore = function(config){
21482 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21487 * @event initialize
21488 * Fires when the editor is fully initialized (including the iframe)
21489 * @param {Roo.HtmlEditorCore} this
21494 * Fires when the editor is first receives the focus. Any insertion must wait
21495 * until after this event.
21496 * @param {Roo.HtmlEditorCore} this
21500 * @event beforesync
21501 * Fires before the textarea is updated with content from the editor iframe. Return false
21502 * to cancel the sync.
21503 * @param {Roo.HtmlEditorCore} this
21504 * @param {String} html
21508 * @event beforepush
21509 * Fires before the iframe editor is updated with content from the textarea. Return false
21510 * to cancel the push.
21511 * @param {Roo.HtmlEditorCore} this
21512 * @param {String} html
21517 * Fires when the textarea is updated with content from the editor iframe.
21518 * @param {Roo.HtmlEditorCore} this
21519 * @param {String} html
21524 * Fires when the iframe editor is updated with content from the textarea.
21525 * @param {Roo.HtmlEditorCore} this
21526 * @param {String} html
21531 * @event editorevent
21532 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21533 * @param {Roo.HtmlEditorCore} this
21539 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21541 // defaults : white / black...
21542 this.applyBlacklists();
21549 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21553 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21559 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21564 * @cfg {Number} height (in pixels)
21568 * @cfg {Number} width (in pixels)
21573 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21576 stylesheets: false,
21581 // private properties
21582 validationEvent : false,
21584 initialized : false,
21586 sourceEditMode : false,
21587 onFocus : Roo.emptyFn,
21589 hideMode:'offsets',
21593 // blacklist + whitelisted elements..
21600 * Protected method that will not generally be called directly. It
21601 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21602 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21604 getDocMarkup : function(){
21608 // inherit styels from page...??
21609 if (this.stylesheets === false) {
21611 Roo.get(document.head).select('style').each(function(node) {
21612 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21615 Roo.get(document.head).select('link').each(function(node) {
21616 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21619 } else if (!this.stylesheets.length) {
21621 st = '<style type="text/css">' +
21622 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21625 st = '<style type="text/css">' +
21630 st += '<style type="text/css">' +
21631 'IMG { cursor: pointer } ' +
21634 var cls = 'roo-htmleditor-body';
21636 if(this.bodyCls.length){
21637 cls += ' ' + this.bodyCls;
21640 return '<html><head>' + st +
21641 //<style type="text/css">' +
21642 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21644 ' </head><body class="' + cls + '"></body></html>';
21648 onRender : function(ct, position)
21651 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21652 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21655 this.el.dom.style.border = '0 none';
21656 this.el.dom.setAttribute('tabIndex', -1);
21657 this.el.addClass('x-hidden hide');
21661 if(Roo.isIE){ // fix IE 1px bogus margin
21662 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21666 this.frameId = Roo.id();
21670 var iframe = this.owner.wrap.createChild({
21672 cls: 'form-control', // bootstrap..
21674 name: this.frameId,
21675 frameBorder : 'no',
21676 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21681 this.iframe = iframe.dom;
21683 this.assignDocWin();
21685 this.doc.designMode = 'on';
21688 this.doc.write(this.getDocMarkup());
21692 var task = { // must defer to wait for browser to be ready
21694 //console.log("run task?" + this.doc.readyState);
21695 this.assignDocWin();
21696 if(this.doc.body || this.doc.readyState == 'complete'){
21698 this.doc.designMode="on";
21702 Roo.TaskMgr.stop(task);
21703 this.initEditor.defer(10, this);
21710 Roo.TaskMgr.start(task);
21715 onResize : function(w, h)
21717 Roo.log('resize: ' +w + ',' + h );
21718 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21722 if(typeof w == 'number'){
21724 this.iframe.style.width = w + 'px';
21726 if(typeof h == 'number'){
21728 this.iframe.style.height = h + 'px';
21730 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21737 * Toggles the editor between standard and source edit mode.
21738 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21740 toggleSourceEdit : function(sourceEditMode){
21742 this.sourceEditMode = sourceEditMode === true;
21744 if(this.sourceEditMode){
21746 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21749 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21750 //this.iframe.className = '';
21753 //this.setSize(this.owner.wrap.getSize());
21754 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21761 * Protected method that will not generally be called directly. If you need/want
21762 * custom HTML cleanup, this is the method you should override.
21763 * @param {String} html The HTML to be cleaned
21764 * return {String} The cleaned HTML
21766 cleanHtml : function(html){
21767 html = String(html);
21768 if(html.length > 5){
21769 if(Roo.isSafari){ // strip safari nonsense
21770 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21773 if(html == ' '){
21780 * HTML Editor -> Textarea
21781 * Protected method that will not generally be called directly. Syncs the contents
21782 * of the editor iframe with the textarea.
21784 syncValue : function(){
21785 if(this.initialized){
21786 var bd = (this.doc.body || this.doc.documentElement);
21787 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21788 var html = bd.innerHTML;
21790 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21791 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21793 html = '<div style="'+m[0]+'">' + html + '</div>';
21796 html = this.cleanHtml(html);
21797 // fix up the special chars.. normaly like back quotes in word...
21798 // however we do not want to do this with chinese..
21799 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21800 var cc = b.charCodeAt();
21802 (cc >= 0x4E00 && cc < 0xA000 ) ||
21803 (cc >= 0x3400 && cc < 0x4E00 ) ||
21804 (cc >= 0xf900 && cc < 0xfb00 )
21810 if(this.owner.fireEvent('beforesync', this, html) !== false){
21811 this.el.dom.value = html;
21812 this.owner.fireEvent('sync', this, html);
21818 * Protected method that will not generally be called directly. Pushes the value of the textarea
21819 * into the iframe editor.
21821 pushValue : function(){
21822 if(this.initialized){
21823 var v = this.el.dom.value.trim();
21825 // if(v.length < 1){
21829 if(this.owner.fireEvent('beforepush', this, v) !== false){
21830 var d = (this.doc.body || this.doc.documentElement);
21832 this.cleanUpPaste();
21833 this.el.dom.value = d.innerHTML;
21834 this.owner.fireEvent('push', this, v);
21840 deferFocus : function(){
21841 this.focus.defer(10, this);
21845 focus : function(){
21846 if(this.win && !this.sourceEditMode){
21853 assignDocWin: function()
21855 var iframe = this.iframe;
21858 this.doc = iframe.contentWindow.document;
21859 this.win = iframe.contentWindow;
21861 // if (!Roo.get(this.frameId)) {
21864 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21865 // this.win = Roo.get(this.frameId).dom.contentWindow;
21867 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21871 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21872 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21877 initEditor : function(){
21878 //console.log("INIT EDITOR");
21879 this.assignDocWin();
21883 this.doc.designMode="on";
21885 this.doc.write(this.getDocMarkup());
21888 var dbody = (this.doc.body || this.doc.documentElement);
21889 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21890 // this copies styles from the containing element into thsi one..
21891 // not sure why we need all of this..
21892 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21894 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21895 //ss['background-attachment'] = 'fixed'; // w3c
21896 dbody.bgProperties = 'fixed'; // ie
21897 //Roo.DomHelper.applyStyles(dbody, ss);
21898 Roo.EventManager.on(this.doc, {
21899 //'mousedown': this.onEditorEvent,
21900 'mouseup': this.onEditorEvent,
21901 'dblclick': this.onEditorEvent,
21902 'click': this.onEditorEvent,
21903 'keyup': this.onEditorEvent,
21908 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21910 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21911 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21913 this.initialized = true;
21915 this.owner.fireEvent('initialize', this);
21920 onDestroy : function(){
21926 //for (var i =0; i < this.toolbars.length;i++) {
21927 // // fixme - ask toolbars for heights?
21928 // this.toolbars[i].onDestroy();
21931 //this.wrap.dom.innerHTML = '';
21932 //this.wrap.remove();
21937 onFirstFocus : function(){
21939 this.assignDocWin();
21942 this.activated = true;
21945 if(Roo.isGecko){ // prevent silly gecko errors
21947 var s = this.win.getSelection();
21948 if(!s.focusNode || s.focusNode.nodeType != 3){
21949 var r = s.getRangeAt(0);
21950 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21955 this.execCmd('useCSS', true);
21956 this.execCmd('styleWithCSS', false);
21959 this.owner.fireEvent('activate', this);
21963 adjustFont: function(btn){
21964 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21965 //if(Roo.isSafari){ // safari
21968 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21969 if(Roo.isSafari){ // safari
21970 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21971 v = (v < 10) ? 10 : v;
21972 v = (v > 48) ? 48 : v;
21973 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21978 v = Math.max(1, v+adjust);
21980 this.execCmd('FontSize', v );
21983 onEditorEvent : function(e)
21985 this.owner.fireEvent('editorevent', this, e);
21986 // this.updateToolbar();
21987 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21990 insertTag : function(tg)
21992 // could be a bit smarter... -> wrap the current selected tRoo..
21993 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21995 range = this.createRange(this.getSelection());
21996 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21997 wrappingNode.appendChild(range.extractContents());
21998 range.insertNode(wrappingNode);
22005 this.execCmd("formatblock", tg);
22009 insertText : function(txt)
22013 var range = this.createRange();
22014 range.deleteContents();
22015 //alert(Sender.getAttribute('label'));
22017 range.insertNode(this.doc.createTextNode(txt));
22023 * Executes a Midas editor command on the editor document and performs necessary focus and
22024 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22025 * @param {String} cmd The Midas command
22026 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22028 relayCmd : function(cmd, value){
22030 this.execCmd(cmd, value);
22031 this.owner.fireEvent('editorevent', this);
22032 //this.updateToolbar();
22033 this.owner.deferFocus();
22037 * Executes a Midas editor command directly on the editor document.
22038 * For visual commands, you should use {@link #relayCmd} instead.
22039 * <b>This should only be called after the editor is initialized.</b>
22040 * @param {String} cmd The Midas command
22041 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22043 execCmd : function(cmd, value){
22044 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22051 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22053 * @param {String} text | dom node..
22055 insertAtCursor : function(text)
22058 if(!this.activated){
22064 var r = this.doc.selection.createRange();
22075 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22079 // from jquery ui (MIT licenced)
22081 var win = this.win;
22083 if (win.getSelection && win.getSelection().getRangeAt) {
22084 range = win.getSelection().getRangeAt(0);
22085 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22086 range.insertNode(node);
22087 } else if (win.document.selection && win.document.selection.createRange) {
22088 // no firefox support
22089 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22090 win.document.selection.createRange().pasteHTML(txt);
22092 // no firefox support
22093 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22094 this.execCmd('InsertHTML', txt);
22103 mozKeyPress : function(e){
22105 var c = e.getCharCode(), cmd;
22108 c = String.fromCharCode(c).toLowerCase();
22122 this.cleanUpPaste.defer(100, this);
22130 e.preventDefault();
22138 fixKeys : function(){ // load time branching for fastest keydown performance
22140 return function(e){
22141 var k = e.getKey(), r;
22144 r = this.doc.selection.createRange();
22147 r.pasteHTML('    ');
22154 r = this.doc.selection.createRange();
22156 var target = r.parentElement();
22157 if(!target || target.tagName.toLowerCase() != 'li'){
22159 r.pasteHTML('<br />');
22165 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22166 this.cleanUpPaste.defer(100, this);
22172 }else if(Roo.isOpera){
22173 return function(e){
22174 var k = e.getKey();
22178 this.execCmd('InsertHTML','    ');
22181 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22182 this.cleanUpPaste.defer(100, this);
22187 }else if(Roo.isSafari){
22188 return function(e){
22189 var k = e.getKey();
22193 this.execCmd('InsertText','\t');
22197 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22198 this.cleanUpPaste.defer(100, this);
22206 getAllAncestors: function()
22208 var p = this.getSelectedNode();
22211 a.push(p); // push blank onto stack..
22212 p = this.getParentElement();
22216 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22220 a.push(this.doc.body);
22224 lastSelNode : false,
22227 getSelection : function()
22229 this.assignDocWin();
22230 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22233 getSelectedNode: function()
22235 // this may only work on Gecko!!!
22237 // should we cache this!!!!
22242 var range = this.createRange(this.getSelection()).cloneRange();
22245 var parent = range.parentElement();
22247 var testRange = range.duplicate();
22248 testRange.moveToElementText(parent);
22249 if (testRange.inRange(range)) {
22252 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22255 parent = parent.parentElement;
22260 // is ancestor a text element.
22261 var ac = range.commonAncestorContainer;
22262 if (ac.nodeType == 3) {
22263 ac = ac.parentNode;
22266 var ar = ac.childNodes;
22269 var other_nodes = [];
22270 var has_other_nodes = false;
22271 for (var i=0;i<ar.length;i++) {
22272 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22275 // fullly contained node.
22277 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22282 // probably selected..
22283 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22284 other_nodes.push(ar[i]);
22288 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22293 has_other_nodes = true;
22295 if (!nodes.length && other_nodes.length) {
22296 nodes= other_nodes;
22298 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22304 createRange: function(sel)
22306 // this has strange effects when using with
22307 // top toolbar - not sure if it's a great idea.
22308 //this.editor.contentWindow.focus();
22309 if (typeof sel != "undefined") {
22311 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22313 return this.doc.createRange();
22316 return this.doc.createRange();
22319 getParentElement: function()
22322 this.assignDocWin();
22323 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22325 var range = this.createRange(sel);
22328 var p = range.commonAncestorContainer;
22329 while (p.nodeType == 3) { // text node
22340 * Range intersection.. the hard stuff...
22344 * [ -- selected range --- ]
22348 * if end is before start or hits it. fail.
22349 * if start is after end or hits it fail.
22351 * if either hits (but other is outside. - then it's not
22357 // @see http://www.thismuchiknow.co.uk/?p=64.
22358 rangeIntersectsNode : function(range, node)
22360 var nodeRange = node.ownerDocument.createRange();
22362 nodeRange.selectNode(node);
22364 nodeRange.selectNodeContents(node);
22367 var rangeStartRange = range.cloneRange();
22368 rangeStartRange.collapse(true);
22370 var rangeEndRange = range.cloneRange();
22371 rangeEndRange.collapse(false);
22373 var nodeStartRange = nodeRange.cloneRange();
22374 nodeStartRange.collapse(true);
22376 var nodeEndRange = nodeRange.cloneRange();
22377 nodeEndRange.collapse(false);
22379 return rangeStartRange.compareBoundaryPoints(
22380 Range.START_TO_START, nodeEndRange) == -1 &&
22381 rangeEndRange.compareBoundaryPoints(
22382 Range.START_TO_START, nodeStartRange) == 1;
22386 rangeCompareNode : function(range, node)
22388 var nodeRange = node.ownerDocument.createRange();
22390 nodeRange.selectNode(node);
22392 nodeRange.selectNodeContents(node);
22396 range.collapse(true);
22398 nodeRange.collapse(true);
22400 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22401 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22403 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22405 var nodeIsBefore = ss == 1;
22406 var nodeIsAfter = ee == -1;
22408 if (nodeIsBefore && nodeIsAfter) {
22411 if (!nodeIsBefore && nodeIsAfter) {
22412 return 1; //right trailed.
22415 if (nodeIsBefore && !nodeIsAfter) {
22416 return 2; // left trailed.
22422 // private? - in a new class?
22423 cleanUpPaste : function()
22425 // cleans up the whole document..
22426 Roo.log('cleanuppaste');
22428 this.cleanUpChildren(this.doc.body);
22429 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22430 if (clean != this.doc.body.innerHTML) {
22431 this.doc.body.innerHTML = clean;
22436 cleanWordChars : function(input) {// change the chars to hex code
22437 var he = Roo.HtmlEditorCore;
22439 var output = input;
22440 Roo.each(he.swapCodes, function(sw) {
22441 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22443 output = output.replace(swapper, sw[1]);
22450 cleanUpChildren : function (n)
22452 if (!n.childNodes.length) {
22455 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22456 this.cleanUpChild(n.childNodes[i]);
22463 cleanUpChild : function (node)
22466 //console.log(node);
22467 if (node.nodeName == "#text") {
22468 // clean up silly Windows -- stuff?
22471 if (node.nodeName == "#comment") {
22472 node.parentNode.removeChild(node);
22473 // clean up silly Windows -- stuff?
22476 var lcname = node.tagName.toLowerCase();
22477 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22478 // whitelist of tags..
22480 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22482 node.parentNode.removeChild(node);
22487 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22489 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22490 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22492 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22493 // remove_keep_children = true;
22496 if (remove_keep_children) {
22497 this.cleanUpChildren(node);
22498 // inserts everything just before this node...
22499 while (node.childNodes.length) {
22500 var cn = node.childNodes[0];
22501 node.removeChild(cn);
22502 node.parentNode.insertBefore(cn, node);
22504 node.parentNode.removeChild(node);
22508 if (!node.attributes || !node.attributes.length) {
22509 this.cleanUpChildren(node);
22513 function cleanAttr(n,v)
22516 if (v.match(/^\./) || v.match(/^\//)) {
22519 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22522 if (v.match(/^#/)) {
22525 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22526 node.removeAttribute(n);
22530 var cwhite = this.cwhite;
22531 var cblack = this.cblack;
22533 function cleanStyle(n,v)
22535 if (v.match(/expression/)) { //XSS?? should we even bother..
22536 node.removeAttribute(n);
22540 var parts = v.split(/;/);
22543 Roo.each(parts, function(p) {
22544 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22548 var l = p.split(':').shift().replace(/\s+/g,'');
22549 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22551 if ( cwhite.length && cblack.indexOf(l) > -1) {
22552 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22553 //node.removeAttribute(n);
22557 // only allow 'c whitelisted system attributes'
22558 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22559 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22560 //node.removeAttribute(n);
22570 if (clean.length) {
22571 node.setAttribute(n, clean.join(';'));
22573 node.removeAttribute(n);
22579 for (var i = node.attributes.length-1; i > -1 ; i--) {
22580 var a = node.attributes[i];
22583 if (a.name.toLowerCase().substr(0,2)=='on') {
22584 node.removeAttribute(a.name);
22587 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22588 node.removeAttribute(a.name);
22591 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22592 cleanAttr(a.name,a.value); // fixme..
22595 if (a.name == 'style') {
22596 cleanStyle(a.name,a.value);
22599 /// clean up MS crap..
22600 // tecnically this should be a list of valid class'es..
22603 if (a.name == 'class') {
22604 if (a.value.match(/^Mso/)) {
22605 node.className = '';
22608 if (a.value.match(/^body$/)) {
22609 node.className = '';
22620 this.cleanUpChildren(node);
22626 * Clean up MS wordisms...
22628 cleanWord : function(node)
22633 this.cleanWord(this.doc.body);
22636 if (node.nodeName == "#text") {
22637 // clean up silly Windows -- stuff?
22640 if (node.nodeName == "#comment") {
22641 node.parentNode.removeChild(node);
22642 // clean up silly Windows -- stuff?
22646 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22647 node.parentNode.removeChild(node);
22651 // remove - but keep children..
22652 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22653 while (node.childNodes.length) {
22654 var cn = node.childNodes[0];
22655 node.removeChild(cn);
22656 node.parentNode.insertBefore(cn, node);
22658 node.parentNode.removeChild(node);
22659 this.iterateChildren(node, this.cleanWord);
22663 if (node.className.length) {
22665 var cn = node.className.split(/\W+/);
22667 Roo.each(cn, function(cls) {
22668 if (cls.match(/Mso[a-zA-Z]+/)) {
22673 node.className = cna.length ? cna.join(' ') : '';
22675 node.removeAttribute("class");
22679 if (node.hasAttribute("lang")) {
22680 node.removeAttribute("lang");
22683 if (node.hasAttribute("style")) {
22685 var styles = node.getAttribute("style").split(";");
22687 Roo.each(styles, function(s) {
22688 if (!s.match(/:/)) {
22691 var kv = s.split(":");
22692 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22695 // what ever is left... we allow.
22698 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22699 if (!nstyle.length) {
22700 node.removeAttribute('style');
22703 this.iterateChildren(node, this.cleanWord);
22709 * iterateChildren of a Node, calling fn each time, using this as the scole..
22710 * @param {DomNode} node node to iterate children of.
22711 * @param {Function} fn method of this class to call on each item.
22713 iterateChildren : function(node, fn)
22715 if (!node.childNodes.length) {
22718 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22719 fn.call(this, node.childNodes[i])
22725 * cleanTableWidths.
22727 * Quite often pasting from word etc.. results in tables with column and widths.
22728 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22731 cleanTableWidths : function(node)
22736 this.cleanTableWidths(this.doc.body);
22741 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22744 Roo.log(node.tagName);
22745 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22746 this.iterateChildren(node, this.cleanTableWidths);
22749 if (node.hasAttribute('width')) {
22750 node.removeAttribute('width');
22754 if (node.hasAttribute("style")) {
22757 var styles = node.getAttribute("style").split(";");
22759 Roo.each(styles, function(s) {
22760 if (!s.match(/:/)) {
22763 var kv = s.split(":");
22764 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22767 // what ever is left... we allow.
22770 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22771 if (!nstyle.length) {
22772 node.removeAttribute('style');
22776 this.iterateChildren(node, this.cleanTableWidths);
22784 domToHTML : function(currentElement, depth, nopadtext) {
22786 depth = depth || 0;
22787 nopadtext = nopadtext || false;
22789 if (!currentElement) {
22790 return this.domToHTML(this.doc.body);
22793 //Roo.log(currentElement);
22795 var allText = false;
22796 var nodeName = currentElement.nodeName;
22797 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22799 if (nodeName == '#text') {
22801 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22806 if (nodeName != 'BODY') {
22809 // Prints the node tagName, such as <A>, <IMG>, etc
22812 for(i = 0; i < currentElement.attributes.length;i++) {
22814 var aname = currentElement.attributes.item(i).name;
22815 if (!currentElement.attributes.item(i).value.length) {
22818 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22821 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22830 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22833 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22838 // Traverse the tree
22840 var currentElementChild = currentElement.childNodes.item(i);
22841 var allText = true;
22842 var innerHTML = '';
22844 while (currentElementChild) {
22845 // Formatting code (indent the tree so it looks nice on the screen)
22846 var nopad = nopadtext;
22847 if (lastnode == 'SPAN') {
22851 if (currentElementChild.nodeName == '#text') {
22852 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22853 toadd = nopadtext ? toadd : toadd.trim();
22854 if (!nopad && toadd.length > 80) {
22855 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22857 innerHTML += toadd;
22860 currentElementChild = currentElement.childNodes.item(i);
22866 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22868 // Recursively traverse the tree structure of the child node
22869 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22870 lastnode = currentElementChild.nodeName;
22872 currentElementChild=currentElement.childNodes.item(i);
22878 // The remaining code is mostly for formatting the tree
22879 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22884 ret+= "</"+tagName+">";
22890 applyBlacklists : function()
22892 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22893 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22897 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22898 if (b.indexOf(tag) > -1) {
22901 this.white.push(tag);
22905 Roo.each(w, function(tag) {
22906 if (b.indexOf(tag) > -1) {
22909 if (this.white.indexOf(tag) > -1) {
22912 this.white.push(tag);
22917 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22918 if (w.indexOf(tag) > -1) {
22921 this.black.push(tag);
22925 Roo.each(b, function(tag) {
22926 if (w.indexOf(tag) > -1) {
22929 if (this.black.indexOf(tag) > -1) {
22932 this.black.push(tag);
22937 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22938 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22942 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22943 if (b.indexOf(tag) > -1) {
22946 this.cwhite.push(tag);
22950 Roo.each(w, function(tag) {
22951 if (b.indexOf(tag) > -1) {
22954 if (this.cwhite.indexOf(tag) > -1) {
22957 this.cwhite.push(tag);
22962 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22963 if (w.indexOf(tag) > -1) {
22966 this.cblack.push(tag);
22970 Roo.each(b, function(tag) {
22971 if (w.indexOf(tag) > -1) {
22974 if (this.cblack.indexOf(tag) > -1) {
22977 this.cblack.push(tag);
22982 setStylesheets : function(stylesheets)
22984 if(typeof(stylesheets) == 'string'){
22985 Roo.get(this.iframe.contentDocument.head).createChild({
22987 rel : 'stylesheet',
22996 Roo.each(stylesheets, function(s) {
23001 Roo.get(_this.iframe.contentDocument.head).createChild({
23003 rel : 'stylesheet',
23012 removeStylesheets : function()
23016 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23021 setStyle : function(style)
23023 Roo.get(this.iframe.contentDocument.head).createChild({
23032 // hide stuff that is not compatible
23046 * @event specialkey
23050 * @cfg {String} fieldClass @hide
23053 * @cfg {String} focusClass @hide
23056 * @cfg {String} autoCreate @hide
23059 * @cfg {String} inputType @hide
23062 * @cfg {String} invalidClass @hide
23065 * @cfg {String} invalidText @hide
23068 * @cfg {String} msgFx @hide
23071 * @cfg {String} validateOnBlur @hide
23075 Roo.HtmlEditorCore.white = [
23076 'area', 'br', 'img', 'input', 'hr', 'wbr',
23078 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23079 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23080 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23081 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23082 'table', 'ul', 'xmp',
23084 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23087 'dir', 'menu', 'ol', 'ul', 'dl',
23093 Roo.HtmlEditorCore.black = [
23094 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23096 'base', 'basefont', 'bgsound', 'blink', 'body',
23097 'frame', 'frameset', 'head', 'html', 'ilayer',
23098 'iframe', 'layer', 'link', 'meta', 'object',
23099 'script', 'style' ,'title', 'xml' // clean later..
23101 Roo.HtmlEditorCore.clean = [
23102 'script', 'style', 'title', 'xml'
23104 Roo.HtmlEditorCore.remove = [
23109 Roo.HtmlEditorCore.ablack = [
23113 Roo.HtmlEditorCore.aclean = [
23114 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23118 Roo.HtmlEditorCore.pwhite= [
23119 'http', 'https', 'mailto'
23122 // white listed style attributes.
23123 Roo.HtmlEditorCore.cwhite= [
23124 // 'text-align', /// default is to allow most things..
23130 // black listed style attributes.
23131 Roo.HtmlEditorCore.cblack= [
23132 // 'font-size' -- this can be set by the project
23136 Roo.HtmlEditorCore.swapCodes =[
23155 * @class Roo.bootstrap.HtmlEditor
23156 * @extends Roo.bootstrap.TextArea
23157 * Bootstrap HtmlEditor class
23160 * Create a new HtmlEditor
23161 * @param {Object} config The config object
23164 Roo.bootstrap.HtmlEditor = function(config){
23165 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23166 if (!this.toolbars) {
23167 this.toolbars = [];
23170 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23173 * @event initialize
23174 * Fires when the editor is fully initialized (including the iframe)
23175 * @param {HtmlEditor} this
23180 * Fires when the editor is first receives the focus. Any insertion must wait
23181 * until after this event.
23182 * @param {HtmlEditor} this
23186 * @event beforesync
23187 * Fires before the textarea is updated with content from the editor iframe. Return false
23188 * to cancel the sync.
23189 * @param {HtmlEditor} this
23190 * @param {String} html
23194 * @event beforepush
23195 * Fires before the iframe editor is updated with content from the textarea. Return false
23196 * to cancel the push.
23197 * @param {HtmlEditor} this
23198 * @param {String} html
23203 * Fires when the textarea is updated with content from the editor iframe.
23204 * @param {HtmlEditor} this
23205 * @param {String} html
23210 * Fires when the iframe editor is updated with content from the textarea.
23211 * @param {HtmlEditor} this
23212 * @param {String} html
23216 * @event editmodechange
23217 * Fires when the editor switches edit modes
23218 * @param {HtmlEditor} this
23219 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23221 editmodechange: true,
23223 * @event editorevent
23224 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23225 * @param {HtmlEditor} this
23229 * @event firstfocus
23230 * Fires when on first focus - needed by toolbars..
23231 * @param {HtmlEditor} this
23236 * Auto save the htmlEditor value as a file into Events
23237 * @param {HtmlEditor} this
23241 * @event savedpreview
23242 * preview the saved version of htmlEditor
23243 * @param {HtmlEditor} this
23250 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23254 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23259 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23264 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23269 * @cfg {Number} height (in pixels)
23273 * @cfg {Number} width (in pixels)
23278 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23281 stylesheets: false,
23286 // private properties
23287 validationEvent : false,
23289 initialized : false,
23292 onFocus : Roo.emptyFn,
23294 hideMode:'offsets',
23296 tbContainer : false,
23300 toolbarContainer :function() {
23301 return this.wrap.select('.x-html-editor-tb',true).first();
23305 * Protected method that will not generally be called directly. It
23306 * is called when the editor creates its toolbar. Override this method if you need to
23307 * add custom toolbar buttons.
23308 * @param {HtmlEditor} editor
23310 createToolbar : function(){
23311 Roo.log('renewing');
23312 Roo.log("create toolbars");
23314 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23315 this.toolbars[0].render(this.toolbarContainer());
23319 // if (!editor.toolbars || !editor.toolbars.length) {
23320 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23323 // for (var i =0 ; i < editor.toolbars.length;i++) {
23324 // editor.toolbars[i] = Roo.factory(
23325 // typeof(editor.toolbars[i]) == 'string' ?
23326 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23327 // Roo.bootstrap.HtmlEditor);
23328 // editor.toolbars[i].init(editor);
23334 onRender : function(ct, position)
23336 // Roo.log("Call onRender: " + this.xtype);
23338 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23340 this.wrap = this.inputEl().wrap({
23341 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23344 this.editorcore.onRender(ct, position);
23346 if (this.resizable) {
23347 this.resizeEl = new Roo.Resizable(this.wrap, {
23351 minHeight : this.height,
23352 height: this.height,
23353 handles : this.resizable,
23356 resize : function(r, w, h) {
23357 _t.onResize(w,h); // -something
23363 this.createToolbar(this);
23366 if(!this.width && this.resizable){
23367 this.setSize(this.wrap.getSize());
23369 if (this.resizeEl) {
23370 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23371 // should trigger onReize..
23377 onResize : function(w, h)
23379 Roo.log('resize: ' +w + ',' + h );
23380 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23384 if(this.inputEl() ){
23385 if(typeof w == 'number'){
23386 var aw = w - this.wrap.getFrameWidth('lr');
23387 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23390 if(typeof h == 'number'){
23391 var tbh = -11; // fixme it needs to tool bar size!
23392 for (var i =0; i < this.toolbars.length;i++) {
23393 // fixme - ask toolbars for heights?
23394 tbh += this.toolbars[i].el.getHeight();
23395 //if (this.toolbars[i].footer) {
23396 // tbh += this.toolbars[i].footer.el.getHeight();
23404 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23405 ah -= 5; // knock a few pixes off for look..
23406 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23410 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23411 this.editorcore.onResize(ew,eh);
23416 * Toggles the editor between standard and source edit mode.
23417 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23419 toggleSourceEdit : function(sourceEditMode)
23421 this.editorcore.toggleSourceEdit(sourceEditMode);
23423 if(this.editorcore.sourceEditMode){
23424 Roo.log('editor - showing textarea');
23427 // Roo.log(this.syncValue());
23429 this.inputEl().removeClass(['hide', 'x-hidden']);
23430 this.inputEl().dom.removeAttribute('tabIndex');
23431 this.inputEl().focus();
23433 Roo.log('editor - hiding textarea');
23435 // Roo.log(this.pushValue());
23438 this.inputEl().addClass(['hide', 'x-hidden']);
23439 this.inputEl().dom.setAttribute('tabIndex', -1);
23440 //this.deferFocus();
23443 if(this.resizable){
23444 this.setSize(this.wrap.getSize());
23447 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23450 // private (for BoxComponent)
23451 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23453 // private (for BoxComponent)
23454 getResizeEl : function(){
23458 // private (for BoxComponent)
23459 getPositionEl : function(){
23464 initEvents : function(){
23465 this.originalValue = this.getValue();
23469 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23472 // markInvalid : Roo.emptyFn,
23474 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23477 // clearInvalid : Roo.emptyFn,
23479 setValue : function(v){
23480 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23481 this.editorcore.pushValue();
23486 deferFocus : function(){
23487 this.focus.defer(10, this);
23491 focus : function(){
23492 this.editorcore.focus();
23498 onDestroy : function(){
23504 for (var i =0; i < this.toolbars.length;i++) {
23505 // fixme - ask toolbars for heights?
23506 this.toolbars[i].onDestroy();
23509 this.wrap.dom.innerHTML = '';
23510 this.wrap.remove();
23515 onFirstFocus : function(){
23516 //Roo.log("onFirstFocus");
23517 this.editorcore.onFirstFocus();
23518 for (var i =0; i < this.toolbars.length;i++) {
23519 this.toolbars[i].onFirstFocus();
23525 syncValue : function()
23527 this.editorcore.syncValue();
23530 pushValue : function()
23532 this.editorcore.pushValue();
23536 // hide stuff that is not compatible
23550 * @event specialkey
23554 * @cfg {String} fieldClass @hide
23557 * @cfg {String} focusClass @hide
23560 * @cfg {String} autoCreate @hide
23563 * @cfg {String} inputType @hide
23566 * @cfg {String} invalidClass @hide
23569 * @cfg {String} invalidText @hide
23572 * @cfg {String} msgFx @hide
23575 * @cfg {String} validateOnBlur @hide
23584 Roo.namespace('Roo.bootstrap.htmleditor');
23586 * @class Roo.bootstrap.HtmlEditorToolbar1
23591 new Roo.bootstrap.HtmlEditor({
23594 new Roo.bootstrap.HtmlEditorToolbar1({
23595 disable : { fonts: 1 , format: 1, ..., ... , ...],
23601 * @cfg {Object} disable List of elements to disable..
23602 * @cfg {Array} btns List of additional buttons.
23606 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23609 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23612 Roo.apply(this, config);
23614 // default disabled, based on 'good practice'..
23615 this.disable = this.disable || {};
23616 Roo.applyIf(this.disable, {
23619 specialElements : true
23621 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23623 this.editor = config.editor;
23624 this.editorcore = config.editor.editorcore;
23626 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23628 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23629 // dont call parent... till later.
23631 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23636 editorcore : false,
23641 "h1","h2","h3","h4","h5","h6",
23643 "abbr", "acronym", "address", "cite", "samp", "var",
23647 onRender : function(ct, position)
23649 // Roo.log("Call onRender: " + this.xtype);
23651 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23653 this.el.dom.style.marginBottom = '0';
23655 var editorcore = this.editorcore;
23656 var editor= this.editor;
23659 var btn = function(id,cmd , toggle, handler, html){
23661 var event = toggle ? 'toggle' : 'click';
23666 xns: Roo.bootstrap,
23669 enableToggle:toggle !== false,
23671 pressed : toggle ? false : null,
23674 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23675 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23681 // var cb_box = function...
23686 xns: Roo.bootstrap,
23687 glyphicon : 'font',
23691 xns: Roo.bootstrap,
23695 Roo.each(this.formats, function(f) {
23696 style.menu.items.push({
23698 xns: Roo.bootstrap,
23699 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23704 editorcore.insertTag(this.tagname);
23711 children.push(style);
23713 btn('bold',false,true);
23714 btn('italic',false,true);
23715 btn('align-left', 'justifyleft',true);
23716 btn('align-center', 'justifycenter',true);
23717 btn('align-right' , 'justifyright',true);
23718 btn('link', false, false, function(btn) {
23719 //Roo.log("create link?");
23720 var url = prompt(this.createLinkText, this.defaultLinkValue);
23721 if(url && url != 'http:/'+'/'){
23722 this.editorcore.relayCmd('createlink', url);
23725 btn('list','insertunorderedlist',true);
23726 btn('pencil', false,true, function(btn){
23728 this.toggleSourceEdit(btn.pressed);
23731 if (this.editor.btns.length > 0) {
23732 for (var i = 0; i<this.editor.btns.length; i++) {
23733 children.push(this.editor.btns[i]);
23741 xns: Roo.bootstrap,
23746 xns: Roo.bootstrap,
23751 cog.menu.items.push({
23753 xns: Roo.bootstrap,
23754 html : Clean styles,
23759 editorcore.insertTag(this.tagname);
23768 this.xtype = 'NavSimplebar';
23770 for(var i=0;i< children.length;i++) {
23772 this.buttons.add(this.addxtypeChild(children[i]));
23776 editor.on('editorevent', this.updateToolbar, this);
23778 onBtnClick : function(id)
23780 this.editorcore.relayCmd(id);
23781 this.editorcore.focus();
23785 * Protected method that will not generally be called directly. It triggers
23786 * a toolbar update by reading the markup state of the current selection in the editor.
23788 updateToolbar: function(){
23790 if(!this.editorcore.activated){
23791 this.editor.onFirstFocus(); // is this neeed?
23795 var btns = this.buttons;
23796 var doc = this.editorcore.doc;
23797 btns.get('bold').setActive(doc.queryCommandState('bold'));
23798 btns.get('italic').setActive(doc.queryCommandState('italic'));
23799 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23801 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23802 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23803 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23805 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23806 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23809 var ans = this.editorcore.getAllAncestors();
23810 if (this.formatCombo) {
23813 var store = this.formatCombo.store;
23814 this.formatCombo.setValue("");
23815 for (var i =0; i < ans.length;i++) {
23816 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23818 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23826 // hides menus... - so this cant be on a menu...
23827 Roo.bootstrap.MenuMgr.hideAll();
23829 Roo.bootstrap.MenuMgr.hideAll();
23830 //this.editorsyncValue();
23832 onFirstFocus: function() {
23833 this.buttons.each(function(item){
23837 toggleSourceEdit : function(sourceEditMode){
23840 if(sourceEditMode){
23841 Roo.log("disabling buttons");
23842 this.buttons.each( function(item){
23843 if(item.cmd != 'pencil'){
23849 Roo.log("enabling buttons");
23850 if(this.editorcore.initialized){
23851 this.buttons.each( function(item){
23857 Roo.log("calling toggole on editor");
23858 // tell the editor that it's been pressed..
23859 this.editor.toggleSourceEdit(sourceEditMode);
23869 * @class Roo.bootstrap.Table.AbstractSelectionModel
23870 * @extends Roo.util.Observable
23871 * Abstract base class for grid SelectionModels. It provides the interface that should be
23872 * implemented by descendant classes. This class should not be directly instantiated.
23875 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23876 this.locked = false;
23877 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23881 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23882 /** @ignore Called by the grid automatically. Do not call directly. */
23883 init : function(grid){
23889 * Locks the selections.
23892 this.locked = true;
23896 * Unlocks the selections.
23898 unlock : function(){
23899 this.locked = false;
23903 * Returns true if the selections are locked.
23904 * @return {Boolean}
23906 isLocked : function(){
23907 return this.locked;
23911 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23912 * @class Roo.bootstrap.Table.RowSelectionModel
23913 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23914 * It supports multiple selections and keyboard selection/navigation.
23916 * @param {Object} config
23919 Roo.bootstrap.Table.RowSelectionModel = function(config){
23920 Roo.apply(this, config);
23921 this.selections = new Roo.util.MixedCollection(false, function(o){
23926 this.lastActive = false;
23930 * @event selectionchange
23931 * Fires when the selection changes
23932 * @param {SelectionModel} this
23934 "selectionchange" : true,
23936 * @event afterselectionchange
23937 * Fires after the selection changes (eg. by key press or clicking)
23938 * @param {SelectionModel} this
23940 "afterselectionchange" : true,
23942 * @event beforerowselect
23943 * Fires when a row is selected being selected, return false to cancel.
23944 * @param {SelectionModel} this
23945 * @param {Number} rowIndex The selected index
23946 * @param {Boolean} keepExisting False if other selections will be cleared
23948 "beforerowselect" : true,
23951 * Fires when a row is selected.
23952 * @param {SelectionModel} this
23953 * @param {Number} rowIndex The selected index
23954 * @param {Roo.data.Record} r The record
23956 "rowselect" : true,
23958 * @event rowdeselect
23959 * Fires when a row is deselected.
23960 * @param {SelectionModel} this
23961 * @param {Number} rowIndex The selected index
23963 "rowdeselect" : true
23965 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23966 this.locked = false;
23969 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23971 * @cfg {Boolean} singleSelect
23972 * True to allow selection of only one row at a time (defaults to false)
23974 singleSelect : false,
23977 initEvents : function()
23980 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23981 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23982 //}else{ // allow click to work like normal
23983 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23985 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23986 this.grid.on("rowclick", this.handleMouseDown, this);
23988 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23989 "up" : function(e){
23991 this.selectPrevious(e.shiftKey);
23992 }else if(this.last !== false && this.lastActive !== false){
23993 var last = this.last;
23994 this.selectRange(this.last, this.lastActive-1);
23995 this.grid.getView().focusRow(this.lastActive);
23996 if(last !== false){
24000 this.selectFirstRow();
24002 this.fireEvent("afterselectionchange", this);
24004 "down" : function(e){
24006 this.selectNext(e.shiftKey);
24007 }else if(this.last !== false && this.lastActive !== false){
24008 var last = this.last;
24009 this.selectRange(this.last, this.lastActive+1);
24010 this.grid.getView().focusRow(this.lastActive);
24011 if(last !== false){
24015 this.selectFirstRow();
24017 this.fireEvent("afterselectionchange", this);
24021 this.grid.store.on('load', function(){
24022 this.selections.clear();
24025 var view = this.grid.view;
24026 view.on("refresh", this.onRefresh, this);
24027 view.on("rowupdated", this.onRowUpdated, this);
24028 view.on("rowremoved", this.onRemove, this);
24033 onRefresh : function()
24035 var ds = this.grid.store, i, v = this.grid.view;
24036 var s = this.selections;
24037 s.each(function(r){
24038 if((i = ds.indexOfId(r.id)) != -1){
24047 onRemove : function(v, index, r){
24048 this.selections.remove(r);
24052 onRowUpdated : function(v, index, r){
24053 if(this.isSelected(r)){
24054 v.onRowSelect(index);
24060 * @param {Array} records The records to select
24061 * @param {Boolean} keepExisting (optional) True to keep existing selections
24063 selectRecords : function(records, keepExisting)
24066 this.clearSelections();
24068 var ds = this.grid.store;
24069 for(var i = 0, len = records.length; i < len; i++){
24070 this.selectRow(ds.indexOf(records[i]), true);
24075 * Gets the number of selected rows.
24078 getCount : function(){
24079 return this.selections.length;
24083 * Selects the first row in the grid.
24085 selectFirstRow : function(){
24090 * Select the last row.
24091 * @param {Boolean} keepExisting (optional) True to keep existing selections
24093 selectLastRow : function(keepExisting){
24094 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24095 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24099 * Selects the row immediately following the last selected row.
24100 * @param {Boolean} keepExisting (optional) True to keep existing selections
24102 selectNext : function(keepExisting)
24104 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24105 this.selectRow(this.last+1, keepExisting);
24106 this.grid.getView().focusRow(this.last);
24111 * Selects the row that precedes the last selected row.
24112 * @param {Boolean} keepExisting (optional) True to keep existing selections
24114 selectPrevious : function(keepExisting){
24116 this.selectRow(this.last-1, keepExisting);
24117 this.grid.getView().focusRow(this.last);
24122 * Returns the selected records
24123 * @return {Array} Array of selected records
24125 getSelections : function(){
24126 return [].concat(this.selections.items);
24130 * Returns the first selected record.
24133 getSelected : function(){
24134 return this.selections.itemAt(0);
24139 * Clears all selections.
24141 clearSelections : function(fast)
24147 var ds = this.grid.store;
24148 var s = this.selections;
24149 s.each(function(r){
24150 this.deselectRow(ds.indexOfId(r.id));
24154 this.selections.clear();
24161 * Selects all rows.
24163 selectAll : function(){
24167 this.selections.clear();
24168 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24169 this.selectRow(i, true);
24174 * Returns True if there is a selection.
24175 * @return {Boolean}
24177 hasSelection : function(){
24178 return this.selections.length > 0;
24182 * Returns True if the specified row is selected.
24183 * @param {Number/Record} record The record or index of the record to check
24184 * @return {Boolean}
24186 isSelected : function(index){
24187 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24188 return (r && this.selections.key(r.id) ? true : false);
24192 * Returns True if the specified record id is selected.
24193 * @param {String} id The id of record to check
24194 * @return {Boolean}
24196 isIdSelected : function(id){
24197 return (this.selections.key(id) ? true : false);
24202 handleMouseDBClick : function(e, t){
24206 handleMouseDown : function(e, t)
24208 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24209 if(this.isLocked() || rowIndex < 0 ){
24212 if(e.shiftKey && this.last !== false){
24213 var last = this.last;
24214 this.selectRange(last, rowIndex, e.ctrlKey);
24215 this.last = last; // reset the last
24219 var isSelected = this.isSelected(rowIndex);
24220 //Roo.log("select row:" + rowIndex);
24222 this.deselectRow(rowIndex);
24224 this.selectRow(rowIndex, true);
24228 if(e.button !== 0 && isSelected){
24229 alert('rowIndex 2: ' + rowIndex);
24230 view.focusRow(rowIndex);
24231 }else if(e.ctrlKey && isSelected){
24232 this.deselectRow(rowIndex);
24233 }else if(!isSelected){
24234 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24235 view.focusRow(rowIndex);
24239 this.fireEvent("afterselectionchange", this);
24242 handleDragableRowClick : function(grid, rowIndex, e)
24244 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24245 this.selectRow(rowIndex, false);
24246 grid.view.focusRow(rowIndex);
24247 this.fireEvent("afterselectionchange", this);
24252 * Selects multiple rows.
24253 * @param {Array} rows Array of the indexes of the row to select
24254 * @param {Boolean} keepExisting (optional) True to keep existing selections
24256 selectRows : function(rows, keepExisting){
24258 this.clearSelections();
24260 for(var i = 0, len = rows.length; i < len; i++){
24261 this.selectRow(rows[i], true);
24266 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24267 * @param {Number} startRow The index of the first row in the range
24268 * @param {Number} endRow The index of the last row in the range
24269 * @param {Boolean} keepExisting (optional) True to retain existing selections
24271 selectRange : function(startRow, endRow, keepExisting){
24276 this.clearSelections();
24278 if(startRow <= endRow){
24279 for(var i = startRow; i <= endRow; i++){
24280 this.selectRow(i, true);
24283 for(var i = startRow; i >= endRow; i--){
24284 this.selectRow(i, true);
24290 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24291 * @param {Number} startRow The index of the first row in the range
24292 * @param {Number} endRow The index of the last row in the range
24294 deselectRange : function(startRow, endRow, preventViewNotify){
24298 for(var i = startRow; i <= endRow; i++){
24299 this.deselectRow(i, preventViewNotify);
24305 * @param {Number} row The index of the row to select
24306 * @param {Boolean} keepExisting (optional) True to keep existing selections
24308 selectRow : function(index, keepExisting, preventViewNotify)
24310 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24313 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24314 if(!keepExisting || this.singleSelect){
24315 this.clearSelections();
24318 var r = this.grid.store.getAt(index);
24319 //console.log('selectRow - record id :' + r.id);
24321 this.selections.add(r);
24322 this.last = this.lastActive = index;
24323 if(!preventViewNotify){
24324 var proxy = new Roo.Element(
24325 this.grid.getRowDom(index)
24327 proxy.addClass('bg-info info');
24329 this.fireEvent("rowselect", this, index, r);
24330 this.fireEvent("selectionchange", this);
24336 * @param {Number} row The index of the row to deselect
24338 deselectRow : function(index, preventViewNotify)
24343 if(this.last == index){
24346 if(this.lastActive == index){
24347 this.lastActive = false;
24350 var r = this.grid.store.getAt(index);
24355 this.selections.remove(r);
24356 //.console.log('deselectRow - record id :' + r.id);
24357 if(!preventViewNotify){
24359 var proxy = new Roo.Element(
24360 this.grid.getRowDom(index)
24362 proxy.removeClass('bg-info info');
24364 this.fireEvent("rowdeselect", this, index);
24365 this.fireEvent("selectionchange", this);
24369 restoreLast : function(){
24371 this.last = this._last;
24376 acceptsNav : function(row, col, cm){
24377 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24381 onEditorKey : function(field, e){
24382 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24387 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24389 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24391 }else if(k == e.ENTER && !e.ctrlKey){
24395 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24397 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24399 }else if(k == e.ESC){
24403 g.startEditing(newCell[0], newCell[1]);
24409 * Ext JS Library 1.1.1
24410 * Copyright(c) 2006-2007, Ext JS, LLC.
24412 * Originally Released Under LGPL - original licence link has changed is not relivant.
24415 * <script type="text/javascript">
24419 * @class Roo.bootstrap.PagingToolbar
24420 * @extends Roo.bootstrap.NavSimplebar
24421 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24423 * Create a new PagingToolbar
24424 * @param {Object} config The config object
24425 * @param {Roo.data.Store} store
24427 Roo.bootstrap.PagingToolbar = function(config)
24429 // old args format still supported... - xtype is prefered..
24430 // created from xtype...
24432 this.ds = config.dataSource;
24434 if (config.store && !this.ds) {
24435 this.store= Roo.factory(config.store, Roo.data);
24436 this.ds = this.store;
24437 this.ds.xmodule = this.xmodule || false;
24440 this.toolbarItems = [];
24441 if (config.items) {
24442 this.toolbarItems = config.items;
24445 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24450 this.bind(this.ds);
24453 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24457 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24459 * @cfg {Roo.data.Store} dataSource
24460 * The underlying data store providing the paged data
24463 * @cfg {String/HTMLElement/Element} container
24464 * container The id or element that will contain the toolbar
24467 * @cfg {Boolean} displayInfo
24468 * True to display the displayMsg (defaults to false)
24471 * @cfg {Number} pageSize
24472 * The number of records to display per page (defaults to 20)
24476 * @cfg {String} displayMsg
24477 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24479 displayMsg : 'Displaying {0} - {1} of {2}',
24481 * @cfg {String} emptyMsg
24482 * The message to display when no records are found (defaults to "No data to display")
24484 emptyMsg : 'No data to display',
24486 * Customizable piece of the default paging text (defaults to "Page")
24489 beforePageText : "Page",
24491 * Customizable piece of the default paging text (defaults to "of %0")
24494 afterPageText : "of {0}",
24496 * Customizable piece of the default paging text (defaults to "First Page")
24499 firstText : "First Page",
24501 * Customizable piece of the default paging text (defaults to "Previous Page")
24504 prevText : "Previous Page",
24506 * Customizable piece of the default paging text (defaults to "Next Page")
24509 nextText : "Next Page",
24511 * Customizable piece of the default paging text (defaults to "Last Page")
24514 lastText : "Last Page",
24516 * Customizable piece of the default paging text (defaults to "Refresh")
24519 refreshText : "Refresh",
24523 onRender : function(ct, position)
24525 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24526 this.navgroup.parentId = this.id;
24527 this.navgroup.onRender(this.el, null);
24528 // add the buttons to the navgroup
24530 if(this.displayInfo){
24531 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24532 this.displayEl = this.el.select('.x-paging-info', true).first();
24533 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24534 // this.displayEl = navel.el.select('span',true).first();
24540 Roo.each(_this.buttons, function(e){ // this might need to use render????
24541 Roo.factory(e).render(_this.el);
24545 Roo.each(_this.toolbarItems, function(e) {
24546 _this.navgroup.addItem(e);
24550 this.first = this.navgroup.addItem({
24551 tooltip: this.firstText,
24553 icon : 'fa fa-backward',
24555 preventDefault: true,
24556 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24559 this.prev = this.navgroup.addItem({
24560 tooltip: this.prevText,
24562 icon : 'fa fa-step-backward',
24564 preventDefault: true,
24565 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24567 //this.addSeparator();
24570 var field = this.navgroup.addItem( {
24572 cls : 'x-paging-position',
24574 html : this.beforePageText +
24575 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24576 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24579 this.field = field.el.select('input', true).first();
24580 this.field.on("keydown", this.onPagingKeydown, this);
24581 this.field.on("focus", function(){this.dom.select();});
24584 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24585 //this.field.setHeight(18);
24586 //this.addSeparator();
24587 this.next = this.navgroup.addItem({
24588 tooltip: this.nextText,
24590 html : ' <i class="fa fa-step-forward">',
24592 preventDefault: true,
24593 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24595 this.last = this.navgroup.addItem({
24596 tooltip: this.lastText,
24597 icon : 'fa fa-forward',
24600 preventDefault: true,
24601 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24603 //this.addSeparator();
24604 this.loading = this.navgroup.addItem({
24605 tooltip: this.refreshText,
24606 icon: 'fa fa-refresh',
24607 preventDefault: true,
24608 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24614 updateInfo : function(){
24615 if(this.displayEl){
24616 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24617 var msg = count == 0 ?
24621 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24623 this.displayEl.update(msg);
24628 onLoad : function(ds, r, o)
24630 this.cursor = o.params.start ? o.params.start : 0;
24632 var d = this.getPageData(),
24637 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24638 this.field.dom.value = ap;
24639 this.first.setDisabled(ap == 1);
24640 this.prev.setDisabled(ap == 1);
24641 this.next.setDisabled(ap == ps);
24642 this.last.setDisabled(ap == ps);
24643 this.loading.enable();
24648 getPageData : function(){
24649 var total = this.ds.getTotalCount();
24652 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24653 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24658 onLoadError : function(){
24659 this.loading.enable();
24663 onPagingKeydown : function(e){
24664 var k = e.getKey();
24665 var d = this.getPageData();
24667 var v = this.field.dom.value, pageNum;
24668 if(!v || isNaN(pageNum = parseInt(v, 10))){
24669 this.field.dom.value = d.activePage;
24672 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24673 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24676 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))
24678 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24679 this.field.dom.value = pageNum;
24680 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24683 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24685 var v = this.field.dom.value, pageNum;
24686 var increment = (e.shiftKey) ? 10 : 1;
24687 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24690 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24691 this.field.dom.value = d.activePage;
24694 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24696 this.field.dom.value = parseInt(v, 10) + increment;
24697 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24698 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24705 beforeLoad : function(){
24707 this.loading.disable();
24712 onClick : function(which){
24721 ds.load({params:{start: 0, limit: this.pageSize}});
24724 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24727 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24730 var total = ds.getTotalCount();
24731 var extra = total % this.pageSize;
24732 var lastStart = extra ? (total - extra) : total-this.pageSize;
24733 ds.load({params:{start: lastStart, limit: this.pageSize}});
24736 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24742 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24743 * @param {Roo.data.Store} store The data store to unbind
24745 unbind : function(ds){
24746 ds.un("beforeload", this.beforeLoad, this);
24747 ds.un("load", this.onLoad, this);
24748 ds.un("loadexception", this.onLoadError, this);
24749 ds.un("remove", this.updateInfo, this);
24750 ds.un("add", this.updateInfo, this);
24751 this.ds = undefined;
24755 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24756 * @param {Roo.data.Store} store The data store to bind
24758 bind : function(ds){
24759 ds.on("beforeload", this.beforeLoad, this);
24760 ds.on("load", this.onLoad, this);
24761 ds.on("loadexception", this.onLoadError, this);
24762 ds.on("remove", this.updateInfo, this);
24763 ds.on("add", this.updateInfo, this);
24774 * @class Roo.bootstrap.MessageBar
24775 * @extends Roo.bootstrap.Component
24776 * Bootstrap MessageBar class
24777 * @cfg {String} html contents of the MessageBar
24778 * @cfg {String} weight (info | success | warning | danger) default info
24779 * @cfg {String} beforeClass insert the bar before the given class
24780 * @cfg {Boolean} closable (true | false) default false
24781 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24784 * Create a new Element
24785 * @param {Object} config The config object
24788 Roo.bootstrap.MessageBar = function(config){
24789 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24792 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24798 beforeClass: 'bootstrap-sticky-wrap',
24800 getAutoCreate : function(){
24804 cls: 'alert alert-dismissable alert-' + this.weight,
24809 html: this.html || ''
24815 cfg.cls += ' alert-messages-fixed';
24829 onRender : function(ct, position)
24831 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24834 var cfg = Roo.apply({}, this.getAutoCreate());
24838 cfg.cls += ' ' + this.cls;
24841 cfg.style = this.style;
24843 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24845 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24848 this.el.select('>button.close').on('click', this.hide, this);
24854 if (!this.rendered) {
24860 this.fireEvent('show', this);
24866 if (!this.rendered) {
24872 this.fireEvent('hide', this);
24875 update : function()
24877 // var e = this.el.dom.firstChild;
24879 // if(this.closable){
24880 // e = e.nextSibling;
24883 // e.data = this.html || '';
24885 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24901 * @class Roo.bootstrap.Graph
24902 * @extends Roo.bootstrap.Component
24903 * Bootstrap Graph class
24907 @cfg {String} graphtype bar | vbar | pie
24908 @cfg {number} g_x coodinator | centre x (pie)
24909 @cfg {number} g_y coodinator | centre y (pie)
24910 @cfg {number} g_r radius (pie)
24911 @cfg {number} g_height height of the chart (respected by all elements in the set)
24912 @cfg {number} g_width width of the chart (respected by all elements in the set)
24913 @cfg {Object} title The title of the chart
24916 -opts (object) options for the chart
24918 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24919 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24921 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.
24922 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24924 o stretch (boolean)
24926 -opts (object) options for the pie
24929 o startAngle (number)
24930 o endAngle (number)
24934 * Create a new Input
24935 * @param {Object} config The config object
24938 Roo.bootstrap.Graph = function(config){
24939 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24945 * The img click event for the img.
24946 * @param {Roo.EventObject} e
24952 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24963 //g_colors: this.colors,
24970 getAutoCreate : function(){
24981 onRender : function(ct,position){
24984 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24986 if (typeof(Raphael) == 'undefined') {
24987 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24991 this.raphael = Raphael(this.el.dom);
24993 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24994 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24995 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24996 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24998 r.text(160, 10, "Single Series Chart").attr(txtattr);
24999 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25000 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25001 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25003 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25004 r.barchart(330, 10, 300, 220, data1);
25005 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25006 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25009 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25010 // r.barchart(30, 30, 560, 250, xdata, {
25011 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25012 // axis : "0 0 1 1",
25013 // axisxlabels : xdata
25014 // //yvalues : cols,
25017 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25019 // this.load(null,xdata,{
25020 // axis : "0 0 1 1",
25021 // axisxlabels : xdata
25026 load : function(graphtype,xdata,opts)
25028 this.raphael.clear();
25030 graphtype = this.graphtype;
25035 var r = this.raphael,
25036 fin = function () {
25037 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25039 fout = function () {
25040 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25042 pfin = function() {
25043 this.sector.stop();
25044 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25047 this.label[0].stop();
25048 this.label[0].attr({ r: 7.5 });
25049 this.label[1].attr({ "font-weight": 800 });
25052 pfout = function() {
25053 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25056 this.label[0].animate({ r: 5 }, 500, "bounce");
25057 this.label[1].attr({ "font-weight": 400 });
25063 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25066 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25069 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25070 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25072 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25079 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25084 setTitle: function(o)
25089 initEvents: function() {
25092 this.el.on('click', this.onClick, this);
25096 onClick : function(e)
25098 Roo.log('img onclick');
25099 this.fireEvent('click', this, e);
25111 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25114 * @class Roo.bootstrap.dash.NumberBox
25115 * @extends Roo.bootstrap.Component
25116 * Bootstrap NumberBox class
25117 * @cfg {String} headline Box headline
25118 * @cfg {String} content Box content
25119 * @cfg {String} icon Box icon
25120 * @cfg {String} footer Footer text
25121 * @cfg {String} fhref Footer href
25124 * Create a new NumberBox
25125 * @param {Object} config The config object
25129 Roo.bootstrap.dash.NumberBox = function(config){
25130 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25134 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25143 getAutoCreate : function(){
25147 cls : 'small-box ',
25155 cls : 'roo-headline',
25156 html : this.headline
25160 cls : 'roo-content',
25161 html : this.content
25175 cls : 'ion ' + this.icon
25184 cls : 'small-box-footer',
25185 href : this.fhref || '#',
25189 cfg.cn.push(footer);
25196 onRender : function(ct,position){
25197 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25204 setHeadline: function (value)
25206 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25209 setFooter: function (value, href)
25211 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25214 this.el.select('a.small-box-footer',true).first().attr('href', href);
25219 setContent: function (value)
25221 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25224 initEvents: function()
25238 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25241 * @class Roo.bootstrap.dash.TabBox
25242 * @extends Roo.bootstrap.Component
25243 * Bootstrap TabBox class
25244 * @cfg {String} title Title of the TabBox
25245 * @cfg {String} icon Icon of the TabBox
25246 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25247 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25250 * Create a new TabBox
25251 * @param {Object} config The config object
25255 Roo.bootstrap.dash.TabBox = function(config){
25256 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25261 * When a pane is added
25262 * @param {Roo.bootstrap.dash.TabPane} pane
25266 * @event activatepane
25267 * When a pane is activated
25268 * @param {Roo.bootstrap.dash.TabPane} pane
25270 "activatepane" : true
25278 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25283 tabScrollable : false,
25285 getChildContainer : function()
25287 return this.el.select('.tab-content', true).first();
25290 getAutoCreate : function(){
25294 cls: 'pull-left header',
25302 cls: 'fa ' + this.icon
25308 cls: 'nav nav-tabs pull-right',
25314 if(this.tabScrollable){
25321 cls: 'nav nav-tabs pull-right',
25332 cls: 'nav-tabs-custom',
25337 cls: 'tab-content no-padding',
25345 initEvents : function()
25347 //Roo.log('add add pane handler');
25348 this.on('addpane', this.onAddPane, this);
25351 * Updates the box title
25352 * @param {String} html to set the title to.
25354 setTitle : function(value)
25356 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25358 onAddPane : function(pane)
25360 this.panes.push(pane);
25361 //Roo.log('addpane');
25363 // tabs are rendere left to right..
25364 if(!this.showtabs){
25368 var ctr = this.el.select('.nav-tabs', true).first();
25371 var existing = ctr.select('.nav-tab',true);
25372 var qty = existing.getCount();;
25375 var tab = ctr.createChild({
25377 cls : 'nav-tab' + (qty ? '' : ' active'),
25385 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25388 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25390 pane.el.addClass('active');
25395 onTabClick : function(ev,un,ob,pane)
25397 //Roo.log('tab - prev default');
25398 ev.preventDefault();
25401 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25402 pane.tab.addClass('active');
25403 //Roo.log(pane.title);
25404 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25405 // technically we should have a deactivate event.. but maybe add later.
25406 // and it should not de-activate the selected tab...
25407 this.fireEvent('activatepane', pane);
25408 pane.el.addClass('active');
25409 pane.fireEvent('activate');
25414 getActivePane : function()
25417 Roo.each(this.panes, function(p) {
25418 if(p.el.hasClass('active')){
25439 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25441 * @class Roo.bootstrap.TabPane
25442 * @extends Roo.bootstrap.Component
25443 * Bootstrap TabPane class
25444 * @cfg {Boolean} active (false | true) Default false
25445 * @cfg {String} title title of panel
25449 * Create a new TabPane
25450 * @param {Object} config The config object
25453 Roo.bootstrap.dash.TabPane = function(config){
25454 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25460 * When a pane is activated
25461 * @param {Roo.bootstrap.dash.TabPane} pane
25468 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25473 // the tabBox that this is attached to.
25476 getAutoCreate : function()
25484 cfg.cls += ' active';
25489 initEvents : function()
25491 //Roo.log('trigger add pane handler');
25492 this.parent().fireEvent('addpane', this)
25496 * Updates the tab title
25497 * @param {String} html to set the title to.
25499 setTitle: function(str)
25505 this.tab.select('a', true).first().dom.innerHTML = str;
25522 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25525 * @class Roo.bootstrap.menu.Menu
25526 * @extends Roo.bootstrap.Component
25527 * Bootstrap Menu class - container for Menu
25528 * @cfg {String} html Text of the menu
25529 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25530 * @cfg {String} icon Font awesome icon
25531 * @cfg {String} pos Menu align to (top | bottom) default bottom
25535 * Create a new Menu
25536 * @param {Object} config The config object
25540 Roo.bootstrap.menu.Menu = function(config){
25541 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25545 * @event beforeshow
25546 * Fires before this menu is displayed
25547 * @param {Roo.bootstrap.menu.Menu} this
25551 * @event beforehide
25552 * Fires before this menu is hidden
25553 * @param {Roo.bootstrap.menu.Menu} this
25558 * Fires after this menu is displayed
25559 * @param {Roo.bootstrap.menu.Menu} this
25564 * Fires after this menu is hidden
25565 * @param {Roo.bootstrap.menu.Menu} this
25570 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25571 * @param {Roo.bootstrap.menu.Menu} this
25572 * @param {Roo.EventObject} e
25579 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25583 weight : 'default',
25588 getChildContainer : function() {
25589 if(this.isSubMenu){
25593 return this.el.select('ul.dropdown-menu', true).first();
25596 getAutoCreate : function()
25601 cls : 'roo-menu-text',
25609 cls : 'fa ' + this.icon
25620 cls : 'dropdown-button btn btn-' + this.weight,
25625 cls : 'dropdown-toggle btn btn-' + this.weight,
25635 cls : 'dropdown-menu'
25641 if(this.pos == 'top'){
25642 cfg.cls += ' dropup';
25645 if(this.isSubMenu){
25648 cls : 'dropdown-menu'
25655 onRender : function(ct, position)
25657 this.isSubMenu = ct.hasClass('dropdown-submenu');
25659 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25662 initEvents : function()
25664 if(this.isSubMenu){
25668 this.hidden = true;
25670 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25671 this.triggerEl.on('click', this.onTriggerPress, this);
25673 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25674 this.buttonEl.on('click', this.onClick, this);
25680 if(this.isSubMenu){
25684 return this.el.select('ul.dropdown-menu', true).first();
25687 onClick : function(e)
25689 this.fireEvent("click", this, e);
25692 onTriggerPress : function(e)
25694 if (this.isVisible()) {
25701 isVisible : function(){
25702 return !this.hidden;
25707 this.fireEvent("beforeshow", this);
25709 this.hidden = false;
25710 this.el.addClass('open');
25712 Roo.get(document).on("mouseup", this.onMouseUp, this);
25714 this.fireEvent("show", this);
25721 this.fireEvent("beforehide", this);
25723 this.hidden = true;
25724 this.el.removeClass('open');
25726 Roo.get(document).un("mouseup", this.onMouseUp);
25728 this.fireEvent("hide", this);
25731 onMouseUp : function()
25745 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25748 * @class Roo.bootstrap.menu.Item
25749 * @extends Roo.bootstrap.Component
25750 * Bootstrap MenuItem class
25751 * @cfg {Boolean} submenu (true | false) default false
25752 * @cfg {String} html text of the item
25753 * @cfg {String} href the link
25754 * @cfg {Boolean} disable (true | false) default false
25755 * @cfg {Boolean} preventDefault (true | false) default true
25756 * @cfg {String} icon Font awesome icon
25757 * @cfg {String} pos Submenu align to (left | right) default right
25761 * Create a new Item
25762 * @param {Object} config The config object
25766 Roo.bootstrap.menu.Item = function(config){
25767 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25771 * Fires when the mouse is hovering over this menu
25772 * @param {Roo.bootstrap.menu.Item} this
25773 * @param {Roo.EventObject} e
25778 * Fires when the mouse exits this menu
25779 * @param {Roo.bootstrap.menu.Item} this
25780 * @param {Roo.EventObject} e
25786 * The raw click event for the entire grid.
25787 * @param {Roo.EventObject} e
25793 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25798 preventDefault: true,
25803 getAutoCreate : function()
25808 cls : 'roo-menu-item-text',
25816 cls : 'fa ' + this.icon
25825 href : this.href || '#',
25832 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25836 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25838 if(this.pos == 'left'){
25839 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25846 initEvents : function()
25848 this.el.on('mouseover', this.onMouseOver, this);
25849 this.el.on('mouseout', this.onMouseOut, this);
25851 this.el.select('a', true).first().on('click', this.onClick, this);
25855 onClick : function(e)
25857 if(this.preventDefault){
25858 e.preventDefault();
25861 this.fireEvent("click", this, e);
25864 onMouseOver : function(e)
25866 if(this.submenu && this.pos == 'left'){
25867 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25870 this.fireEvent("mouseover", this, e);
25873 onMouseOut : function(e)
25875 this.fireEvent("mouseout", this, e);
25887 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25890 * @class Roo.bootstrap.menu.Separator
25891 * @extends Roo.bootstrap.Component
25892 * Bootstrap Separator class
25895 * Create a new Separator
25896 * @param {Object} config The config object
25900 Roo.bootstrap.menu.Separator = function(config){
25901 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25904 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25906 getAutoCreate : function(){
25927 * @class Roo.bootstrap.Tooltip
25928 * Bootstrap Tooltip class
25929 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25930 * to determine which dom element triggers the tooltip.
25932 * It needs to add support for additional attributes like tooltip-position
25935 * Create a new Toolti
25936 * @param {Object} config The config object
25939 Roo.bootstrap.Tooltip = function(config){
25940 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25942 this.alignment = Roo.bootstrap.Tooltip.alignment;
25944 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25945 this.alignment = config.alignment;
25950 Roo.apply(Roo.bootstrap.Tooltip, {
25952 * @function init initialize tooltip monitoring.
25956 currentTip : false,
25957 currentRegion : false,
25963 Roo.get(document).on('mouseover', this.enter ,this);
25964 Roo.get(document).on('mouseout', this.leave, this);
25967 this.currentTip = new Roo.bootstrap.Tooltip();
25970 enter : function(ev)
25972 var dom = ev.getTarget();
25974 //Roo.log(['enter',dom]);
25975 var el = Roo.fly(dom);
25976 if (this.currentEl) {
25978 //Roo.log(this.currentEl);
25979 //Roo.log(this.currentEl.contains(dom));
25980 if (this.currentEl == el) {
25983 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25989 if (this.currentTip.el) {
25990 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25994 if(!el || el.dom == document){
26000 // you can not look for children, as if el is the body.. then everythign is the child..
26001 if (!el.attr('tooltip')) { //
26002 if (!el.select("[tooltip]").elements.length) {
26005 // is the mouse over this child...?
26006 bindEl = el.select("[tooltip]").first();
26007 var xy = ev.getXY();
26008 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26009 //Roo.log("not in region.");
26012 //Roo.log("child element over..");
26015 this.currentEl = bindEl;
26016 this.currentTip.bind(bindEl);
26017 this.currentRegion = Roo.lib.Region.getRegion(dom);
26018 this.currentTip.enter();
26021 leave : function(ev)
26023 var dom = ev.getTarget();
26024 //Roo.log(['leave',dom]);
26025 if (!this.currentEl) {
26030 if (dom != this.currentEl.dom) {
26033 var xy = ev.getXY();
26034 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26037 // only activate leave if mouse cursor is outside... bounding box..
26042 if (this.currentTip) {
26043 this.currentTip.leave();
26045 //Roo.log('clear currentEl');
26046 this.currentEl = false;
26051 'left' : ['r-l', [-2,0], 'right'],
26052 'right' : ['l-r', [2,0], 'left'],
26053 'bottom' : ['t-b', [0,2], 'top'],
26054 'top' : [ 'b-t', [0,-2], 'bottom']
26060 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26065 delay : null, // can be { show : 300 , hide: 500}
26069 hoverState : null, //???
26071 placement : 'bottom',
26075 getAutoCreate : function(){
26082 cls : 'tooltip-arrow'
26085 cls : 'tooltip-inner'
26092 bind : function(el)
26098 enter : function () {
26100 if (this.timeout != null) {
26101 clearTimeout(this.timeout);
26104 this.hoverState = 'in';
26105 //Roo.log("enter - show");
26106 if (!this.delay || !this.delay.show) {
26111 this.timeout = setTimeout(function () {
26112 if (_t.hoverState == 'in') {
26115 }, this.delay.show);
26119 clearTimeout(this.timeout);
26121 this.hoverState = 'out';
26122 if (!this.delay || !this.delay.hide) {
26128 this.timeout = setTimeout(function () {
26129 //Roo.log("leave - timeout");
26131 if (_t.hoverState == 'out') {
26133 Roo.bootstrap.Tooltip.currentEl = false;
26138 show : function (msg)
26141 this.render(document.body);
26144 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26146 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26148 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26150 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26152 var placement = typeof this.placement == 'function' ?
26153 this.placement.call(this, this.el, on_el) :
26156 var autoToken = /\s?auto?\s?/i;
26157 var autoPlace = autoToken.test(placement);
26159 placement = placement.replace(autoToken, '') || 'top';
26163 //this.el.setXY([0,0]);
26165 //this.el.dom.style.display='block';
26167 //this.el.appendTo(on_el);
26169 var p = this.getPosition();
26170 var box = this.el.getBox();
26176 var align = this.alignment[placement];
26178 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26180 if(placement == 'top' || placement == 'bottom'){
26182 placement = 'right';
26185 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26186 placement = 'left';
26189 var scroll = Roo.select('body', true).first().getScroll();
26191 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26197 this.el.alignTo(this.bindEl, align[0],align[1]);
26198 //var arrow = this.el.select('.arrow',true).first();
26199 //arrow.set(align[2],
26201 this.el.addClass(placement);
26203 this.el.addClass('in fade');
26205 this.hoverState = null;
26207 if (this.el.hasClass('fade')) {
26218 //this.el.setXY([0,0]);
26219 this.el.removeClass('in');
26235 * @class Roo.bootstrap.LocationPicker
26236 * @extends Roo.bootstrap.Component
26237 * Bootstrap LocationPicker class
26238 * @cfg {Number} latitude Position when init default 0
26239 * @cfg {Number} longitude Position when init default 0
26240 * @cfg {Number} zoom default 15
26241 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26242 * @cfg {Boolean} mapTypeControl default false
26243 * @cfg {Boolean} disableDoubleClickZoom default false
26244 * @cfg {Boolean} scrollwheel default true
26245 * @cfg {Boolean} streetViewControl default false
26246 * @cfg {Number} radius default 0
26247 * @cfg {String} locationName
26248 * @cfg {Boolean} draggable default true
26249 * @cfg {Boolean} enableAutocomplete default false
26250 * @cfg {Boolean} enableReverseGeocode default true
26251 * @cfg {String} markerTitle
26254 * Create a new LocationPicker
26255 * @param {Object} config The config object
26259 Roo.bootstrap.LocationPicker = function(config){
26261 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26266 * Fires when the picker initialized.
26267 * @param {Roo.bootstrap.LocationPicker} this
26268 * @param {Google Location} location
26272 * @event positionchanged
26273 * Fires when the picker position changed.
26274 * @param {Roo.bootstrap.LocationPicker} this
26275 * @param {Google Location} location
26277 positionchanged : true,
26280 * Fires when the map resize.
26281 * @param {Roo.bootstrap.LocationPicker} this
26286 * Fires when the map show.
26287 * @param {Roo.bootstrap.LocationPicker} this
26292 * Fires when the map hide.
26293 * @param {Roo.bootstrap.LocationPicker} this
26298 * Fires when click the map.
26299 * @param {Roo.bootstrap.LocationPicker} this
26300 * @param {Map event} e
26304 * @event mapRightClick
26305 * Fires when right click the map.
26306 * @param {Roo.bootstrap.LocationPicker} this
26307 * @param {Map event} e
26309 mapRightClick : true,
26311 * @event markerClick
26312 * Fires when click the marker.
26313 * @param {Roo.bootstrap.LocationPicker} this
26314 * @param {Map event} e
26316 markerClick : true,
26318 * @event markerRightClick
26319 * Fires when right click the marker.
26320 * @param {Roo.bootstrap.LocationPicker} this
26321 * @param {Map event} e
26323 markerRightClick : true,
26325 * @event OverlayViewDraw
26326 * Fires when OverlayView Draw
26327 * @param {Roo.bootstrap.LocationPicker} this
26329 OverlayViewDraw : true,
26331 * @event OverlayViewOnAdd
26332 * Fires when OverlayView Draw
26333 * @param {Roo.bootstrap.LocationPicker} this
26335 OverlayViewOnAdd : true,
26337 * @event OverlayViewOnRemove
26338 * Fires when OverlayView Draw
26339 * @param {Roo.bootstrap.LocationPicker} this
26341 OverlayViewOnRemove : true,
26343 * @event OverlayViewShow
26344 * Fires when OverlayView Draw
26345 * @param {Roo.bootstrap.LocationPicker} this
26346 * @param {Pixel} cpx
26348 OverlayViewShow : true,
26350 * @event OverlayViewHide
26351 * Fires when OverlayView Draw
26352 * @param {Roo.bootstrap.LocationPicker} this
26354 OverlayViewHide : true,
26356 * @event loadexception
26357 * Fires when load google lib failed.
26358 * @param {Roo.bootstrap.LocationPicker} this
26360 loadexception : true
26365 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26367 gMapContext: false,
26373 mapTypeControl: false,
26374 disableDoubleClickZoom: false,
26376 streetViewControl: false,
26380 enableAutocomplete: false,
26381 enableReverseGeocode: true,
26384 getAutoCreate: function()
26389 cls: 'roo-location-picker'
26395 initEvents: function(ct, position)
26397 if(!this.el.getWidth() || this.isApplied()){
26401 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26406 initial: function()
26408 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26409 this.fireEvent('loadexception', this);
26413 if(!this.mapTypeId){
26414 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26417 this.gMapContext = this.GMapContext();
26419 this.initOverlayView();
26421 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26425 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26426 _this.setPosition(_this.gMapContext.marker.position);
26429 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26430 _this.fireEvent('mapClick', this, event);
26434 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26435 _this.fireEvent('mapRightClick', this, event);
26439 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26440 _this.fireEvent('markerClick', this, event);
26444 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26445 _this.fireEvent('markerRightClick', this, event);
26449 this.setPosition(this.gMapContext.location);
26451 this.fireEvent('initial', this, this.gMapContext.location);
26454 initOverlayView: function()
26458 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26462 _this.fireEvent('OverlayViewDraw', _this);
26467 _this.fireEvent('OverlayViewOnAdd', _this);
26470 onRemove: function()
26472 _this.fireEvent('OverlayViewOnRemove', _this);
26475 show: function(cpx)
26477 _this.fireEvent('OverlayViewShow', _this, cpx);
26482 _this.fireEvent('OverlayViewHide', _this);
26488 fromLatLngToContainerPixel: function(event)
26490 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26493 isApplied: function()
26495 return this.getGmapContext() == false ? false : true;
26498 getGmapContext: function()
26500 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26503 GMapContext: function()
26505 var position = new google.maps.LatLng(this.latitude, this.longitude);
26507 var _map = new google.maps.Map(this.el.dom, {
26510 mapTypeId: this.mapTypeId,
26511 mapTypeControl: this.mapTypeControl,
26512 disableDoubleClickZoom: this.disableDoubleClickZoom,
26513 scrollwheel: this.scrollwheel,
26514 streetViewControl: this.streetViewControl,
26515 locationName: this.locationName,
26516 draggable: this.draggable,
26517 enableAutocomplete: this.enableAutocomplete,
26518 enableReverseGeocode: this.enableReverseGeocode
26521 var _marker = new google.maps.Marker({
26522 position: position,
26524 title: this.markerTitle,
26525 draggable: this.draggable
26532 location: position,
26533 radius: this.radius,
26534 locationName: this.locationName,
26535 addressComponents: {
26536 formatted_address: null,
26537 addressLine1: null,
26538 addressLine2: null,
26540 streetNumber: null,
26544 stateOrProvince: null
26547 domContainer: this.el.dom,
26548 geodecoder: new google.maps.Geocoder()
26552 drawCircle: function(center, radius, options)
26554 if (this.gMapContext.circle != null) {
26555 this.gMapContext.circle.setMap(null);
26559 options = Roo.apply({}, options, {
26560 strokeColor: "#0000FF",
26561 strokeOpacity: .35,
26563 fillColor: "#0000FF",
26567 options.map = this.gMapContext.map;
26568 options.radius = radius;
26569 options.center = center;
26570 this.gMapContext.circle = new google.maps.Circle(options);
26571 return this.gMapContext.circle;
26577 setPosition: function(location)
26579 this.gMapContext.location = location;
26580 this.gMapContext.marker.setPosition(location);
26581 this.gMapContext.map.panTo(location);
26582 this.drawCircle(location, this.gMapContext.radius, {});
26586 if (this.gMapContext.settings.enableReverseGeocode) {
26587 this.gMapContext.geodecoder.geocode({
26588 latLng: this.gMapContext.location
26589 }, function(results, status) {
26591 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26592 _this.gMapContext.locationName = results[0].formatted_address;
26593 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26595 _this.fireEvent('positionchanged', this, location);
26602 this.fireEvent('positionchanged', this, location);
26607 google.maps.event.trigger(this.gMapContext.map, "resize");
26609 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26611 this.fireEvent('resize', this);
26614 setPositionByLatLng: function(latitude, longitude)
26616 this.setPosition(new google.maps.LatLng(latitude, longitude));
26619 getCurrentPosition: function()
26622 latitude: this.gMapContext.location.lat(),
26623 longitude: this.gMapContext.location.lng()
26627 getAddressName: function()
26629 return this.gMapContext.locationName;
26632 getAddressComponents: function()
26634 return this.gMapContext.addressComponents;
26637 address_component_from_google_geocode: function(address_components)
26641 for (var i = 0; i < address_components.length; i++) {
26642 var component = address_components[i];
26643 if (component.types.indexOf("postal_code") >= 0) {
26644 result.postalCode = component.short_name;
26645 } else if (component.types.indexOf("street_number") >= 0) {
26646 result.streetNumber = component.short_name;
26647 } else if (component.types.indexOf("route") >= 0) {
26648 result.streetName = component.short_name;
26649 } else if (component.types.indexOf("neighborhood") >= 0) {
26650 result.city = component.short_name;
26651 } else if (component.types.indexOf("locality") >= 0) {
26652 result.city = component.short_name;
26653 } else if (component.types.indexOf("sublocality") >= 0) {
26654 result.district = component.short_name;
26655 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26656 result.stateOrProvince = component.short_name;
26657 } else if (component.types.indexOf("country") >= 0) {
26658 result.country = component.short_name;
26662 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26663 result.addressLine2 = "";
26667 setZoomLevel: function(zoom)
26669 this.gMapContext.map.setZoom(zoom);
26682 this.fireEvent('show', this);
26693 this.fireEvent('hide', this);
26698 Roo.apply(Roo.bootstrap.LocationPicker, {
26700 OverlayView : function(map, options)
26702 options = options || {};
26716 * @class Roo.bootstrap.Alert
26717 * @extends Roo.bootstrap.Component
26718 * Bootstrap Alert class
26719 * @cfg {String} title The title of alert
26720 * @cfg {String} html The content of alert
26721 * @cfg {String} weight ( success | info | warning | danger )
26722 * @cfg {String} faicon font-awesomeicon
26725 * Create a new alert
26726 * @param {Object} config The config object
26730 Roo.bootstrap.Alert = function(config){
26731 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26735 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26742 getAutoCreate : function()
26751 cls : 'roo-alert-icon'
26756 cls : 'roo-alert-title',
26761 cls : 'roo-alert-text',
26768 cfg.cn[0].cls += ' fa ' + this.faicon;
26772 cfg.cls += ' alert-' + this.weight;
26778 initEvents: function()
26780 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26783 setTitle : function(str)
26785 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26788 setText : function(str)
26790 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26793 setWeight : function(weight)
26796 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26799 this.weight = weight;
26801 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26804 setIcon : function(icon)
26807 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26810 this.faicon = icon;
26812 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26833 * @class Roo.bootstrap.UploadCropbox
26834 * @extends Roo.bootstrap.Component
26835 * Bootstrap UploadCropbox class
26836 * @cfg {String} emptyText show when image has been loaded
26837 * @cfg {String} rotateNotify show when image too small to rotate
26838 * @cfg {Number} errorTimeout default 3000
26839 * @cfg {Number} minWidth default 300
26840 * @cfg {Number} minHeight default 300
26841 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26842 * @cfg {Boolean} isDocument (true|false) default false
26843 * @cfg {String} url action url
26844 * @cfg {String} paramName default 'imageUpload'
26845 * @cfg {String} method default POST
26846 * @cfg {Boolean} loadMask (true|false) default true
26847 * @cfg {Boolean} loadingText default 'Loading...'
26850 * Create a new UploadCropbox
26851 * @param {Object} config The config object
26854 Roo.bootstrap.UploadCropbox = function(config){
26855 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26859 * @event beforeselectfile
26860 * Fire before select file
26861 * @param {Roo.bootstrap.UploadCropbox} this
26863 "beforeselectfile" : true,
26866 * Fire after initEvent
26867 * @param {Roo.bootstrap.UploadCropbox} this
26872 * Fire after initEvent
26873 * @param {Roo.bootstrap.UploadCropbox} this
26874 * @param {String} data
26879 * Fire when preparing the file data
26880 * @param {Roo.bootstrap.UploadCropbox} this
26881 * @param {Object} file
26886 * Fire when get exception
26887 * @param {Roo.bootstrap.UploadCropbox} this
26888 * @param {XMLHttpRequest} xhr
26890 "exception" : true,
26892 * @event beforeloadcanvas
26893 * Fire before load the canvas
26894 * @param {Roo.bootstrap.UploadCropbox} this
26895 * @param {String} src
26897 "beforeloadcanvas" : true,
26900 * Fire when trash image
26901 * @param {Roo.bootstrap.UploadCropbox} this
26906 * Fire when download the image
26907 * @param {Roo.bootstrap.UploadCropbox} this
26911 * @event footerbuttonclick
26912 * Fire when footerbuttonclick
26913 * @param {Roo.bootstrap.UploadCropbox} this
26914 * @param {String} type
26916 "footerbuttonclick" : true,
26920 * @param {Roo.bootstrap.UploadCropbox} this
26925 * Fire when rotate the image
26926 * @param {Roo.bootstrap.UploadCropbox} this
26927 * @param {String} pos
26932 * Fire when inspect the file
26933 * @param {Roo.bootstrap.UploadCropbox} this
26934 * @param {Object} file
26939 * Fire when xhr upload the file
26940 * @param {Roo.bootstrap.UploadCropbox} this
26941 * @param {Object} data
26946 * Fire when arrange the file data
26947 * @param {Roo.bootstrap.UploadCropbox} this
26948 * @param {Object} formData
26953 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26956 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26958 emptyText : 'Click to upload image',
26959 rotateNotify : 'Image is too small to rotate',
26960 errorTimeout : 3000,
26974 cropType : 'image/jpeg',
26976 canvasLoaded : false,
26977 isDocument : false,
26979 paramName : 'imageUpload',
26981 loadingText : 'Loading...',
26984 getAutoCreate : function()
26988 cls : 'roo-upload-cropbox',
26992 cls : 'roo-upload-cropbox-selector',
26997 cls : 'roo-upload-cropbox-body',
26998 style : 'cursor:pointer',
27002 cls : 'roo-upload-cropbox-preview'
27006 cls : 'roo-upload-cropbox-thumb'
27010 cls : 'roo-upload-cropbox-empty-notify',
27011 html : this.emptyText
27015 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27016 html : this.rotateNotify
27022 cls : 'roo-upload-cropbox-footer',
27025 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27035 onRender : function(ct, position)
27037 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27039 if (this.buttons.length) {
27041 Roo.each(this.buttons, function(bb) {
27043 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27045 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27051 this.maskEl = this.el;
27055 initEvents : function()
27057 this.urlAPI = (window.createObjectURL && window) ||
27058 (window.URL && URL.revokeObjectURL && URL) ||
27059 (window.webkitURL && webkitURL);
27061 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27062 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27064 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27065 this.selectorEl.hide();
27067 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27068 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27070 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27071 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27072 this.thumbEl.hide();
27074 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27075 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27077 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27078 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27079 this.errorEl.hide();
27081 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27082 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27083 this.footerEl.hide();
27085 this.setThumbBoxSize();
27091 this.fireEvent('initial', this);
27098 window.addEventListener("resize", function() { _this.resize(); } );
27100 this.bodyEl.on('click', this.beforeSelectFile, this);
27103 this.bodyEl.on('touchstart', this.onTouchStart, this);
27104 this.bodyEl.on('touchmove', this.onTouchMove, this);
27105 this.bodyEl.on('touchend', this.onTouchEnd, this);
27109 this.bodyEl.on('mousedown', this.onMouseDown, this);
27110 this.bodyEl.on('mousemove', this.onMouseMove, this);
27111 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27112 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27113 Roo.get(document).on('mouseup', this.onMouseUp, this);
27116 this.selectorEl.on('change', this.onFileSelected, this);
27122 this.baseScale = 1;
27124 this.baseRotate = 1;
27125 this.dragable = false;
27126 this.pinching = false;
27129 this.cropData = false;
27130 this.notifyEl.dom.innerHTML = this.emptyText;
27132 this.selectorEl.dom.value = '';
27136 resize : function()
27138 if(this.fireEvent('resize', this) != false){
27139 this.setThumbBoxPosition();
27140 this.setCanvasPosition();
27144 onFooterButtonClick : function(e, el, o, type)
27147 case 'rotate-left' :
27148 this.onRotateLeft(e);
27150 case 'rotate-right' :
27151 this.onRotateRight(e);
27154 this.beforeSelectFile(e);
27169 this.fireEvent('footerbuttonclick', this, type);
27172 beforeSelectFile : function(e)
27174 e.preventDefault();
27176 if(this.fireEvent('beforeselectfile', this) != false){
27177 this.selectorEl.dom.click();
27181 onFileSelected : function(e)
27183 e.preventDefault();
27185 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27189 var file = this.selectorEl.dom.files[0];
27191 if(this.fireEvent('inspect', this, file) != false){
27192 this.prepare(file);
27197 trash : function(e)
27199 this.fireEvent('trash', this);
27202 download : function(e)
27204 this.fireEvent('download', this);
27207 loadCanvas : function(src)
27209 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27213 this.imageEl = document.createElement('img');
27217 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27219 this.imageEl.src = src;
27223 onLoadCanvas : function()
27225 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27226 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27228 this.bodyEl.un('click', this.beforeSelectFile, this);
27230 this.notifyEl.hide();
27231 this.thumbEl.show();
27232 this.footerEl.show();
27234 this.baseRotateLevel();
27236 if(this.isDocument){
27237 this.setThumbBoxSize();
27240 this.setThumbBoxPosition();
27242 this.baseScaleLevel();
27248 this.canvasLoaded = true;
27251 this.maskEl.unmask();
27256 setCanvasPosition : function()
27258 if(!this.canvasEl){
27262 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27263 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27265 this.previewEl.setLeft(pw);
27266 this.previewEl.setTop(ph);
27270 onMouseDown : function(e)
27274 this.dragable = true;
27275 this.pinching = false;
27277 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27278 this.dragable = false;
27282 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27283 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27287 onMouseMove : function(e)
27291 if(!this.canvasLoaded){
27295 if (!this.dragable){
27299 var minX = Math.ceil(this.thumbEl.getLeft(true));
27300 var minY = Math.ceil(this.thumbEl.getTop(true));
27302 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27303 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27305 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27306 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27308 x = x - this.mouseX;
27309 y = y - this.mouseY;
27311 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27312 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27314 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27315 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27317 this.previewEl.setLeft(bgX);
27318 this.previewEl.setTop(bgY);
27320 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27321 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27324 onMouseUp : function(e)
27328 this.dragable = false;
27331 onMouseWheel : function(e)
27335 this.startScale = this.scale;
27337 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27339 if(!this.zoomable()){
27340 this.scale = this.startScale;
27349 zoomable : function()
27351 var minScale = this.thumbEl.getWidth() / this.minWidth;
27353 if(this.minWidth < this.minHeight){
27354 minScale = this.thumbEl.getHeight() / this.minHeight;
27357 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27358 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27362 (this.rotate == 0 || this.rotate == 180) &&
27364 width > this.imageEl.OriginWidth ||
27365 height > this.imageEl.OriginHeight ||
27366 (width < this.minWidth && height < this.minHeight)
27374 (this.rotate == 90 || this.rotate == 270) &&
27376 width > this.imageEl.OriginWidth ||
27377 height > this.imageEl.OriginHeight ||
27378 (width < this.minHeight && height < this.minWidth)
27385 !this.isDocument &&
27386 (this.rotate == 0 || this.rotate == 180) &&
27388 width < this.minWidth ||
27389 width > this.imageEl.OriginWidth ||
27390 height < this.minHeight ||
27391 height > this.imageEl.OriginHeight
27398 !this.isDocument &&
27399 (this.rotate == 90 || this.rotate == 270) &&
27401 width < this.minHeight ||
27402 width > this.imageEl.OriginWidth ||
27403 height < this.minWidth ||
27404 height > this.imageEl.OriginHeight
27414 onRotateLeft : function(e)
27416 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27418 var minScale = this.thumbEl.getWidth() / this.minWidth;
27420 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27421 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27423 this.startScale = this.scale;
27425 while (this.getScaleLevel() < minScale){
27427 this.scale = this.scale + 1;
27429 if(!this.zoomable()){
27434 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27435 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27440 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27447 this.scale = this.startScale;
27449 this.onRotateFail();
27454 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27456 if(this.isDocument){
27457 this.setThumbBoxSize();
27458 this.setThumbBoxPosition();
27459 this.setCanvasPosition();
27464 this.fireEvent('rotate', this, 'left');
27468 onRotateRight : function(e)
27470 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27472 var minScale = this.thumbEl.getWidth() / this.minWidth;
27474 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27475 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27477 this.startScale = this.scale;
27479 while (this.getScaleLevel() < minScale){
27481 this.scale = this.scale + 1;
27483 if(!this.zoomable()){
27488 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27489 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27494 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27501 this.scale = this.startScale;
27503 this.onRotateFail();
27508 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27510 if(this.isDocument){
27511 this.setThumbBoxSize();
27512 this.setThumbBoxPosition();
27513 this.setCanvasPosition();
27518 this.fireEvent('rotate', this, 'right');
27521 onRotateFail : function()
27523 this.errorEl.show(true);
27527 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27532 this.previewEl.dom.innerHTML = '';
27534 var canvasEl = document.createElement("canvas");
27536 var contextEl = canvasEl.getContext("2d");
27538 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27539 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27540 var center = this.imageEl.OriginWidth / 2;
27542 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27543 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27544 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27545 center = this.imageEl.OriginHeight / 2;
27548 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27550 contextEl.translate(center, center);
27551 contextEl.rotate(this.rotate * Math.PI / 180);
27553 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27555 this.canvasEl = document.createElement("canvas");
27557 this.contextEl = this.canvasEl.getContext("2d");
27559 switch (this.rotate) {
27562 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27563 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27565 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27570 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27571 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27573 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27574 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);
27578 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27583 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27584 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27586 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27587 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);
27591 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);
27596 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27597 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27599 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27600 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27604 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);
27611 this.previewEl.appendChild(this.canvasEl);
27613 this.setCanvasPosition();
27618 if(!this.canvasLoaded){
27622 var imageCanvas = document.createElement("canvas");
27624 var imageContext = imageCanvas.getContext("2d");
27626 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27627 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27629 var center = imageCanvas.width / 2;
27631 imageContext.translate(center, center);
27633 imageContext.rotate(this.rotate * Math.PI / 180);
27635 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27637 var canvas = document.createElement("canvas");
27639 var context = canvas.getContext("2d");
27641 canvas.width = this.minWidth;
27642 canvas.height = this.minHeight;
27644 switch (this.rotate) {
27647 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27648 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27650 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27651 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27653 var targetWidth = this.minWidth - 2 * x;
27654 var targetHeight = this.minHeight - 2 * y;
27658 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27659 scale = targetWidth / width;
27662 if(x > 0 && y == 0){
27663 scale = targetHeight / height;
27666 if(x > 0 && y > 0){
27667 scale = targetWidth / width;
27669 if(width < height){
27670 scale = targetHeight / height;
27674 context.scale(scale, scale);
27676 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27677 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27679 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27680 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27682 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27687 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27688 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27690 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27691 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27693 var targetWidth = this.minWidth - 2 * x;
27694 var targetHeight = this.minHeight - 2 * y;
27698 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27699 scale = targetWidth / width;
27702 if(x > 0 && y == 0){
27703 scale = targetHeight / height;
27706 if(x > 0 && y > 0){
27707 scale = targetWidth / width;
27709 if(width < height){
27710 scale = targetHeight / height;
27714 context.scale(scale, scale);
27716 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27717 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27719 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27720 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27722 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27724 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27729 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27730 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27732 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27733 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27735 var targetWidth = this.minWidth - 2 * x;
27736 var targetHeight = this.minHeight - 2 * y;
27740 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27741 scale = targetWidth / width;
27744 if(x > 0 && y == 0){
27745 scale = targetHeight / height;
27748 if(x > 0 && y > 0){
27749 scale = targetWidth / width;
27751 if(width < height){
27752 scale = targetHeight / height;
27756 context.scale(scale, scale);
27758 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27759 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27761 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27762 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27764 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27765 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27767 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27772 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27773 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27775 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27776 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27778 var targetWidth = this.minWidth - 2 * x;
27779 var targetHeight = this.minHeight - 2 * y;
27783 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27784 scale = targetWidth / width;
27787 if(x > 0 && y == 0){
27788 scale = targetHeight / height;
27791 if(x > 0 && y > 0){
27792 scale = targetWidth / width;
27794 if(width < height){
27795 scale = targetHeight / height;
27799 context.scale(scale, scale);
27801 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27802 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27804 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27805 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27807 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27809 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27816 this.cropData = canvas.toDataURL(this.cropType);
27818 if(this.fireEvent('crop', this, this.cropData) !== false){
27819 this.process(this.file, this.cropData);
27826 setThumbBoxSize : function()
27830 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27831 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27832 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27834 this.minWidth = width;
27835 this.minHeight = height;
27837 if(this.rotate == 90 || this.rotate == 270){
27838 this.minWidth = height;
27839 this.minHeight = width;
27844 width = Math.ceil(this.minWidth * height / this.minHeight);
27846 if(this.minWidth > this.minHeight){
27848 height = Math.ceil(this.minHeight * width / this.minWidth);
27851 this.thumbEl.setStyle({
27852 width : width + 'px',
27853 height : height + 'px'
27860 setThumbBoxPosition : function()
27862 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27863 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27865 this.thumbEl.setLeft(x);
27866 this.thumbEl.setTop(y);
27870 baseRotateLevel : function()
27872 this.baseRotate = 1;
27875 typeof(this.exif) != 'undefined' &&
27876 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27877 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27879 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27882 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27886 baseScaleLevel : function()
27890 if(this.isDocument){
27892 if(this.baseRotate == 6 || this.baseRotate == 8){
27894 height = this.thumbEl.getHeight();
27895 this.baseScale = height / this.imageEl.OriginWidth;
27897 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27898 width = this.thumbEl.getWidth();
27899 this.baseScale = width / this.imageEl.OriginHeight;
27905 height = this.thumbEl.getHeight();
27906 this.baseScale = height / this.imageEl.OriginHeight;
27908 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27909 width = this.thumbEl.getWidth();
27910 this.baseScale = width / this.imageEl.OriginWidth;
27916 if(this.baseRotate == 6 || this.baseRotate == 8){
27918 width = this.thumbEl.getHeight();
27919 this.baseScale = width / this.imageEl.OriginHeight;
27921 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27922 height = this.thumbEl.getWidth();
27923 this.baseScale = height / this.imageEl.OriginHeight;
27926 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27927 height = this.thumbEl.getWidth();
27928 this.baseScale = height / this.imageEl.OriginHeight;
27930 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27931 width = this.thumbEl.getHeight();
27932 this.baseScale = width / this.imageEl.OriginWidth;
27939 width = this.thumbEl.getWidth();
27940 this.baseScale = width / this.imageEl.OriginWidth;
27942 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27943 height = this.thumbEl.getHeight();
27944 this.baseScale = height / this.imageEl.OriginHeight;
27947 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27949 height = this.thumbEl.getHeight();
27950 this.baseScale = height / this.imageEl.OriginHeight;
27952 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27953 width = this.thumbEl.getWidth();
27954 this.baseScale = width / this.imageEl.OriginWidth;
27962 getScaleLevel : function()
27964 return this.baseScale * Math.pow(1.1, this.scale);
27967 onTouchStart : function(e)
27969 if(!this.canvasLoaded){
27970 this.beforeSelectFile(e);
27974 var touches = e.browserEvent.touches;
27980 if(touches.length == 1){
27981 this.onMouseDown(e);
27985 if(touches.length != 2){
27991 for(var i = 0, finger; finger = touches[i]; i++){
27992 coords.push(finger.pageX, finger.pageY);
27995 var x = Math.pow(coords[0] - coords[2], 2);
27996 var y = Math.pow(coords[1] - coords[3], 2);
27998 this.startDistance = Math.sqrt(x + y);
28000 this.startScale = this.scale;
28002 this.pinching = true;
28003 this.dragable = false;
28007 onTouchMove : function(e)
28009 if(!this.pinching && !this.dragable){
28013 var touches = e.browserEvent.touches;
28020 this.onMouseMove(e);
28026 for(var i = 0, finger; finger = touches[i]; i++){
28027 coords.push(finger.pageX, finger.pageY);
28030 var x = Math.pow(coords[0] - coords[2], 2);
28031 var y = Math.pow(coords[1] - coords[3], 2);
28033 this.endDistance = Math.sqrt(x + y);
28035 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28037 if(!this.zoomable()){
28038 this.scale = this.startScale;
28046 onTouchEnd : function(e)
28048 this.pinching = false;
28049 this.dragable = false;
28053 process : function(file, crop)
28056 this.maskEl.mask(this.loadingText);
28059 this.xhr = new XMLHttpRequest();
28061 file.xhr = this.xhr;
28063 this.xhr.open(this.method, this.url, true);
28066 "Accept": "application/json",
28067 "Cache-Control": "no-cache",
28068 "X-Requested-With": "XMLHttpRequest"
28071 for (var headerName in headers) {
28072 var headerValue = headers[headerName];
28074 this.xhr.setRequestHeader(headerName, headerValue);
28080 this.xhr.onload = function()
28082 _this.xhrOnLoad(_this.xhr);
28085 this.xhr.onerror = function()
28087 _this.xhrOnError(_this.xhr);
28090 var formData = new FormData();
28092 formData.append('returnHTML', 'NO');
28095 formData.append('crop', crop);
28098 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28099 formData.append(this.paramName, file, file.name);
28102 if(typeof(file.filename) != 'undefined'){
28103 formData.append('filename', file.filename);
28106 if(typeof(file.mimetype) != 'undefined'){
28107 formData.append('mimetype', file.mimetype);
28110 if(this.fireEvent('arrange', this, formData) != false){
28111 this.xhr.send(formData);
28115 xhrOnLoad : function(xhr)
28118 this.maskEl.unmask();
28121 if (xhr.readyState !== 4) {
28122 this.fireEvent('exception', this, xhr);
28126 var response = Roo.decode(xhr.responseText);
28128 if(!response.success){
28129 this.fireEvent('exception', this, xhr);
28133 var response = Roo.decode(xhr.responseText);
28135 this.fireEvent('upload', this, response);
28139 xhrOnError : function()
28142 this.maskEl.unmask();
28145 Roo.log('xhr on error');
28147 var response = Roo.decode(xhr.responseText);
28153 prepare : function(file)
28156 this.maskEl.mask(this.loadingText);
28162 if(typeof(file) === 'string'){
28163 this.loadCanvas(file);
28167 if(!file || !this.urlAPI){
28172 this.cropType = file.type;
28176 if(this.fireEvent('prepare', this, this.file) != false){
28178 var reader = new FileReader();
28180 reader.onload = function (e) {
28181 if (e.target.error) {
28182 Roo.log(e.target.error);
28186 var buffer = e.target.result,
28187 dataView = new DataView(buffer),
28189 maxOffset = dataView.byteLength - 4,
28193 if (dataView.getUint16(0) === 0xffd8) {
28194 while (offset < maxOffset) {
28195 markerBytes = dataView.getUint16(offset);
28197 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28198 markerLength = dataView.getUint16(offset + 2) + 2;
28199 if (offset + markerLength > dataView.byteLength) {
28200 Roo.log('Invalid meta data: Invalid segment size.');
28204 if(markerBytes == 0xffe1){
28205 _this.parseExifData(
28212 offset += markerLength;
28222 var url = _this.urlAPI.createObjectURL(_this.file);
28224 _this.loadCanvas(url);
28229 reader.readAsArrayBuffer(this.file);
28235 parseExifData : function(dataView, offset, length)
28237 var tiffOffset = offset + 10,
28241 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28242 // No Exif data, might be XMP data instead
28246 // Check for the ASCII code for "Exif" (0x45786966):
28247 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28248 // No Exif data, might be XMP data instead
28251 if (tiffOffset + 8 > dataView.byteLength) {
28252 Roo.log('Invalid Exif data: Invalid segment size.');
28255 // Check for the two null bytes:
28256 if (dataView.getUint16(offset + 8) !== 0x0000) {
28257 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28260 // Check the byte alignment:
28261 switch (dataView.getUint16(tiffOffset)) {
28263 littleEndian = true;
28266 littleEndian = false;
28269 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28272 // Check for the TIFF tag marker (0x002A):
28273 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28274 Roo.log('Invalid Exif data: Missing TIFF marker.');
28277 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28278 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28280 this.parseExifTags(
28283 tiffOffset + dirOffset,
28288 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28293 if (dirOffset + 6 > dataView.byteLength) {
28294 Roo.log('Invalid Exif data: Invalid directory offset.');
28297 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28298 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28299 if (dirEndOffset + 4 > dataView.byteLength) {
28300 Roo.log('Invalid Exif data: Invalid directory size.');
28303 for (i = 0; i < tagsNumber; i += 1) {
28307 dirOffset + 2 + 12 * i, // tag offset
28311 // Return the offset to the next directory:
28312 return dataView.getUint32(dirEndOffset, littleEndian);
28315 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28317 var tag = dataView.getUint16(offset, littleEndian);
28319 this.exif[tag] = this.getExifValue(
28323 dataView.getUint16(offset + 2, littleEndian), // tag type
28324 dataView.getUint32(offset + 4, littleEndian), // tag length
28329 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28331 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28340 Roo.log('Invalid Exif data: Invalid tag type.');
28344 tagSize = tagType.size * length;
28345 // Determine if the value is contained in the dataOffset bytes,
28346 // or if the value at the dataOffset is a pointer to the actual data:
28347 dataOffset = tagSize > 4 ?
28348 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28349 if (dataOffset + tagSize > dataView.byteLength) {
28350 Roo.log('Invalid Exif data: Invalid data offset.');
28353 if (length === 1) {
28354 return tagType.getValue(dataView, dataOffset, littleEndian);
28357 for (i = 0; i < length; i += 1) {
28358 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28361 if (tagType.ascii) {
28363 // Concatenate the chars:
28364 for (i = 0; i < values.length; i += 1) {
28366 // Ignore the terminating NULL byte(s):
28367 if (c === '\u0000') {
28379 Roo.apply(Roo.bootstrap.UploadCropbox, {
28381 'Orientation': 0x0112
28385 1: 0, //'top-left',
28387 3: 180, //'bottom-right',
28388 // 4: 'bottom-left',
28390 6: 90, //'right-top',
28391 // 7: 'right-bottom',
28392 8: 270 //'left-bottom'
28396 // byte, 8-bit unsigned int:
28398 getValue: function (dataView, dataOffset) {
28399 return dataView.getUint8(dataOffset);
28403 // ascii, 8-bit byte:
28405 getValue: function (dataView, dataOffset) {
28406 return String.fromCharCode(dataView.getUint8(dataOffset));
28411 // short, 16 bit int:
28413 getValue: function (dataView, dataOffset, littleEndian) {
28414 return dataView.getUint16(dataOffset, littleEndian);
28418 // long, 32 bit int:
28420 getValue: function (dataView, dataOffset, littleEndian) {
28421 return dataView.getUint32(dataOffset, littleEndian);
28425 // rational = two long values, first is numerator, second is denominator:
28427 getValue: function (dataView, dataOffset, littleEndian) {
28428 return dataView.getUint32(dataOffset, littleEndian) /
28429 dataView.getUint32(dataOffset + 4, littleEndian);
28433 // slong, 32 bit signed int:
28435 getValue: function (dataView, dataOffset, littleEndian) {
28436 return dataView.getInt32(dataOffset, littleEndian);
28440 // srational, two slongs, first is numerator, second is denominator:
28442 getValue: function (dataView, dataOffset, littleEndian) {
28443 return dataView.getInt32(dataOffset, littleEndian) /
28444 dataView.getInt32(dataOffset + 4, littleEndian);
28454 cls : 'btn-group roo-upload-cropbox-rotate-left',
28455 action : 'rotate-left',
28459 cls : 'btn btn-default',
28460 html : '<i class="fa fa-undo"></i>'
28466 cls : 'btn-group roo-upload-cropbox-picture',
28467 action : 'picture',
28471 cls : 'btn btn-default',
28472 html : '<i class="fa fa-picture-o"></i>'
28478 cls : 'btn-group roo-upload-cropbox-rotate-right',
28479 action : 'rotate-right',
28483 cls : 'btn btn-default',
28484 html : '<i class="fa fa-repeat"></i>'
28492 cls : 'btn-group roo-upload-cropbox-rotate-left',
28493 action : 'rotate-left',
28497 cls : 'btn btn-default',
28498 html : '<i class="fa fa-undo"></i>'
28504 cls : 'btn-group roo-upload-cropbox-download',
28505 action : 'download',
28509 cls : 'btn btn-default',
28510 html : '<i class="fa fa-download"></i>'
28516 cls : 'btn-group roo-upload-cropbox-crop',
28521 cls : 'btn btn-default',
28522 html : '<i class="fa fa-crop"></i>'
28528 cls : 'btn-group roo-upload-cropbox-trash',
28533 cls : 'btn btn-default',
28534 html : '<i class="fa fa-trash"></i>'
28540 cls : 'btn-group roo-upload-cropbox-rotate-right',
28541 action : 'rotate-right',
28545 cls : 'btn btn-default',
28546 html : '<i class="fa fa-repeat"></i>'
28554 cls : 'btn-group roo-upload-cropbox-rotate-left',
28555 action : 'rotate-left',
28559 cls : 'btn btn-default',
28560 html : '<i class="fa fa-undo"></i>'
28566 cls : 'btn-group roo-upload-cropbox-rotate-right',
28567 action : 'rotate-right',
28571 cls : 'btn btn-default',
28572 html : '<i class="fa fa-repeat"></i>'
28585 * @class Roo.bootstrap.DocumentManager
28586 * @extends Roo.bootstrap.Component
28587 * Bootstrap DocumentManager class
28588 * @cfg {String} paramName default 'imageUpload'
28589 * @cfg {String} toolTipName default 'filename'
28590 * @cfg {String} method default POST
28591 * @cfg {String} url action url
28592 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28593 * @cfg {Boolean} multiple multiple upload default true
28594 * @cfg {Number} thumbSize default 300
28595 * @cfg {String} fieldLabel
28596 * @cfg {Number} labelWidth default 4
28597 * @cfg {String} labelAlign (left|top) default left
28598 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28599 * @cfg {Number} labellg set the width of label (1-12)
28600 * @cfg {Number} labelmd set the width of label (1-12)
28601 * @cfg {Number} labelsm set the width of label (1-12)
28602 * @cfg {Number} labelxs set the width of label (1-12)
28605 * Create a new DocumentManager
28606 * @param {Object} config The config object
28609 Roo.bootstrap.DocumentManager = function(config){
28610 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28613 this.delegates = [];
28618 * Fire when initial the DocumentManager
28619 * @param {Roo.bootstrap.DocumentManager} this
28624 * inspect selected file
28625 * @param {Roo.bootstrap.DocumentManager} this
28626 * @param {File} file
28631 * Fire when xhr load exception
28632 * @param {Roo.bootstrap.DocumentManager} this
28633 * @param {XMLHttpRequest} xhr
28635 "exception" : true,
28637 * @event afterupload
28638 * Fire when xhr load exception
28639 * @param {Roo.bootstrap.DocumentManager} this
28640 * @param {XMLHttpRequest} xhr
28642 "afterupload" : true,
28645 * prepare the form data
28646 * @param {Roo.bootstrap.DocumentManager} this
28647 * @param {Object} formData
28652 * Fire when remove the file
28653 * @param {Roo.bootstrap.DocumentManager} this
28654 * @param {Object} file
28659 * Fire after refresh the file
28660 * @param {Roo.bootstrap.DocumentManager} this
28665 * Fire after click the image
28666 * @param {Roo.bootstrap.DocumentManager} this
28667 * @param {Object} file
28672 * Fire when upload a image and editable set to true
28673 * @param {Roo.bootstrap.DocumentManager} this
28674 * @param {Object} file
28678 * @event beforeselectfile
28679 * Fire before select file
28680 * @param {Roo.bootstrap.DocumentManager} this
28682 "beforeselectfile" : true,
28685 * Fire before process file
28686 * @param {Roo.bootstrap.DocumentManager} this
28687 * @param {Object} file
28691 * @event previewrendered
28692 * Fire when preview rendered
28693 * @param {Roo.bootstrap.DocumentManager} this
28694 * @param {Object} file
28696 "previewrendered" : true
28701 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28710 paramName : 'imageUpload',
28711 toolTipName : 'filename',
28714 labelAlign : 'left',
28724 getAutoCreate : function()
28726 var managerWidget = {
28728 cls : 'roo-document-manager',
28732 cls : 'roo-document-manager-selector',
28737 cls : 'roo-document-manager-uploader',
28741 cls : 'roo-document-manager-upload-btn',
28742 html : '<i class="fa fa-plus"></i>'
28753 cls : 'column col-md-12',
28758 if(this.fieldLabel.length){
28763 cls : 'column col-md-12',
28764 html : this.fieldLabel
28768 cls : 'column col-md-12',
28773 if(this.labelAlign == 'left'){
28778 html : this.fieldLabel
28787 if(this.labelWidth > 12){
28788 content[0].style = "width: " + this.labelWidth + 'px';
28791 if(this.labelWidth < 13 && this.labelmd == 0){
28792 this.labelmd = this.labelWidth;
28795 if(this.labellg > 0){
28796 content[0].cls += ' col-lg-' + this.labellg;
28797 content[1].cls += ' col-lg-' + (12 - this.labellg);
28800 if(this.labelmd > 0){
28801 content[0].cls += ' col-md-' + this.labelmd;
28802 content[1].cls += ' col-md-' + (12 - this.labelmd);
28805 if(this.labelsm > 0){
28806 content[0].cls += ' col-sm-' + this.labelsm;
28807 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28810 if(this.labelxs > 0){
28811 content[0].cls += ' col-xs-' + this.labelxs;
28812 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28820 cls : 'row clearfix',
28828 initEvents : function()
28830 this.managerEl = this.el.select('.roo-document-manager', true).first();
28831 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28833 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28834 this.selectorEl.hide();
28837 this.selectorEl.attr('multiple', 'multiple');
28840 this.selectorEl.on('change', this.onFileSelected, this);
28842 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28843 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28845 this.uploader.on('click', this.onUploaderClick, this);
28847 this.renderProgressDialog();
28851 window.addEventListener("resize", function() { _this.refresh(); } );
28853 this.fireEvent('initial', this);
28856 renderProgressDialog : function()
28860 this.progressDialog = new Roo.bootstrap.Modal({
28861 cls : 'roo-document-manager-progress-dialog',
28862 allow_close : false,
28872 btnclick : function() {
28873 _this.uploadCancel();
28879 this.progressDialog.render(Roo.get(document.body));
28881 this.progress = new Roo.bootstrap.Progress({
28882 cls : 'roo-document-manager-progress',
28887 this.progress.render(this.progressDialog.getChildContainer());
28889 this.progressBar = new Roo.bootstrap.ProgressBar({
28890 cls : 'roo-document-manager-progress-bar',
28893 aria_valuemax : 12,
28897 this.progressBar.render(this.progress.getChildContainer());
28900 onUploaderClick : function(e)
28902 e.preventDefault();
28904 if(this.fireEvent('beforeselectfile', this) != false){
28905 this.selectorEl.dom.click();
28910 onFileSelected : function(e)
28912 e.preventDefault();
28914 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28918 Roo.each(this.selectorEl.dom.files, function(file){
28919 if(this.fireEvent('inspect', this, file) != false){
28920 this.files.push(file);
28930 this.selectorEl.dom.value = '';
28932 if(!this.files || !this.files.length){
28936 if(this.boxes > 0 && this.files.length > this.boxes){
28937 this.files = this.files.slice(0, this.boxes);
28940 this.uploader.show();
28942 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28943 this.uploader.hide();
28952 Roo.each(this.files, function(file){
28954 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28955 var f = this.renderPreview(file);
28960 if(file.type.indexOf('image') != -1){
28961 this.delegates.push(
28963 _this.process(file);
28964 }).createDelegate(this)
28972 _this.process(file);
28973 }).createDelegate(this)
28978 this.files = files;
28980 this.delegates = this.delegates.concat(docs);
28982 if(!this.delegates.length){
28987 this.progressBar.aria_valuemax = this.delegates.length;
28994 arrange : function()
28996 if(!this.delegates.length){
28997 this.progressDialog.hide();
29002 var delegate = this.delegates.shift();
29004 this.progressDialog.show();
29006 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29008 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29013 refresh : function()
29015 this.uploader.show();
29017 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29018 this.uploader.hide();
29021 Roo.isTouch ? this.closable(false) : this.closable(true);
29023 this.fireEvent('refresh', this);
29026 onRemove : function(e, el, o)
29028 e.preventDefault();
29030 this.fireEvent('remove', this, o);
29034 remove : function(o)
29038 Roo.each(this.files, function(file){
29039 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29048 this.files = files;
29055 Roo.each(this.files, function(file){
29060 file.target.remove();
29069 onClick : function(e, el, o)
29071 e.preventDefault();
29073 this.fireEvent('click', this, o);
29077 closable : function(closable)
29079 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29081 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29093 xhrOnLoad : function(xhr)
29095 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29099 if (xhr.readyState !== 4) {
29101 this.fireEvent('exception', this, xhr);
29105 var response = Roo.decode(xhr.responseText);
29107 if(!response.success){
29109 this.fireEvent('exception', this, xhr);
29113 var file = this.renderPreview(response.data);
29115 this.files.push(file);
29119 this.fireEvent('afterupload', this, xhr);
29123 xhrOnError : function(xhr)
29125 Roo.log('xhr on error');
29127 var response = Roo.decode(xhr.responseText);
29134 process : function(file)
29136 if(this.fireEvent('process', this, file) !== false){
29137 if(this.editable && file.type.indexOf('image') != -1){
29138 this.fireEvent('edit', this, file);
29142 this.uploadStart(file, false);
29149 uploadStart : function(file, crop)
29151 this.xhr = new XMLHttpRequest();
29153 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29158 file.xhr = this.xhr;
29160 this.managerEl.createChild({
29162 cls : 'roo-document-manager-loading',
29166 tooltip : file.name,
29167 cls : 'roo-document-manager-thumb',
29168 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29174 this.xhr.open(this.method, this.url, true);
29177 "Accept": "application/json",
29178 "Cache-Control": "no-cache",
29179 "X-Requested-With": "XMLHttpRequest"
29182 for (var headerName in headers) {
29183 var headerValue = headers[headerName];
29185 this.xhr.setRequestHeader(headerName, headerValue);
29191 this.xhr.onload = function()
29193 _this.xhrOnLoad(_this.xhr);
29196 this.xhr.onerror = function()
29198 _this.xhrOnError(_this.xhr);
29201 var formData = new FormData();
29203 formData.append('returnHTML', 'NO');
29206 formData.append('crop', crop);
29209 formData.append(this.paramName, file, file.name);
29216 if(this.fireEvent('prepare', this, formData, options) != false){
29218 if(options.manually){
29222 this.xhr.send(formData);
29226 this.uploadCancel();
29229 uploadCancel : function()
29235 this.delegates = [];
29237 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29244 renderPreview : function(file)
29246 if(typeof(file.target) != 'undefined' && file.target){
29250 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29252 var previewEl = this.managerEl.createChild({
29254 cls : 'roo-document-manager-preview',
29258 tooltip : file[this.toolTipName],
29259 cls : 'roo-document-manager-thumb',
29260 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29265 html : '<i class="fa fa-times-circle"></i>'
29270 var close = previewEl.select('button.close', true).first();
29272 close.on('click', this.onRemove, this, file);
29274 file.target = previewEl;
29276 var image = previewEl.select('img', true).first();
29280 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29282 image.on('click', this.onClick, this, file);
29284 this.fireEvent('previewrendered', this, file);
29290 onPreviewLoad : function(file, image)
29292 if(typeof(file.target) == 'undefined' || !file.target){
29296 var width = image.dom.naturalWidth || image.dom.width;
29297 var height = image.dom.naturalHeight || image.dom.height;
29299 if(width > height){
29300 file.target.addClass('wide');
29304 file.target.addClass('tall');
29309 uploadFromSource : function(file, crop)
29311 this.xhr = new XMLHttpRequest();
29313 this.managerEl.createChild({
29315 cls : 'roo-document-manager-loading',
29319 tooltip : file.name,
29320 cls : 'roo-document-manager-thumb',
29321 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29327 this.xhr.open(this.method, this.url, true);
29330 "Accept": "application/json",
29331 "Cache-Control": "no-cache",
29332 "X-Requested-With": "XMLHttpRequest"
29335 for (var headerName in headers) {
29336 var headerValue = headers[headerName];
29338 this.xhr.setRequestHeader(headerName, headerValue);
29344 this.xhr.onload = function()
29346 _this.xhrOnLoad(_this.xhr);
29349 this.xhr.onerror = function()
29351 _this.xhrOnError(_this.xhr);
29354 var formData = new FormData();
29356 formData.append('returnHTML', 'NO');
29358 formData.append('crop', crop);
29360 if(typeof(file.filename) != 'undefined'){
29361 formData.append('filename', file.filename);
29364 if(typeof(file.mimetype) != 'undefined'){
29365 formData.append('mimetype', file.mimetype);
29370 if(this.fireEvent('prepare', this, formData) != false){
29371 this.xhr.send(formData);
29381 * @class Roo.bootstrap.DocumentViewer
29382 * @extends Roo.bootstrap.Component
29383 * Bootstrap DocumentViewer class
29384 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29385 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29388 * Create a new DocumentViewer
29389 * @param {Object} config The config object
29392 Roo.bootstrap.DocumentViewer = function(config){
29393 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29398 * Fire after initEvent
29399 * @param {Roo.bootstrap.DocumentViewer} this
29405 * @param {Roo.bootstrap.DocumentViewer} this
29410 * Fire after download button
29411 * @param {Roo.bootstrap.DocumentViewer} this
29416 * Fire after trash button
29417 * @param {Roo.bootstrap.DocumentViewer} this
29424 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29426 showDownload : true,
29430 getAutoCreate : function()
29434 cls : 'roo-document-viewer',
29438 cls : 'roo-document-viewer-body',
29442 cls : 'roo-document-viewer-thumb',
29446 cls : 'roo-document-viewer-image'
29454 cls : 'roo-document-viewer-footer',
29457 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29461 cls : 'btn-group roo-document-viewer-download',
29465 cls : 'btn btn-default',
29466 html : '<i class="fa fa-download"></i>'
29472 cls : 'btn-group roo-document-viewer-trash',
29476 cls : 'btn btn-default',
29477 html : '<i class="fa fa-trash"></i>'
29490 initEvents : function()
29492 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29493 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29495 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29496 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29498 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29499 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29501 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29502 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29504 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29505 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29507 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29508 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29510 this.bodyEl.on('click', this.onClick, this);
29511 this.downloadBtn.on('click', this.onDownload, this);
29512 this.trashBtn.on('click', this.onTrash, this);
29514 this.downloadBtn.hide();
29515 this.trashBtn.hide();
29517 if(this.showDownload){
29518 this.downloadBtn.show();
29521 if(this.showTrash){
29522 this.trashBtn.show();
29525 if(!this.showDownload && !this.showTrash) {
29526 this.footerEl.hide();
29531 initial : function()
29533 this.fireEvent('initial', this);
29537 onClick : function(e)
29539 e.preventDefault();
29541 this.fireEvent('click', this);
29544 onDownload : function(e)
29546 e.preventDefault();
29548 this.fireEvent('download', this);
29551 onTrash : function(e)
29553 e.preventDefault();
29555 this.fireEvent('trash', this);
29567 * @class Roo.bootstrap.NavProgressBar
29568 * @extends Roo.bootstrap.Component
29569 * Bootstrap NavProgressBar class
29572 * Create a new nav progress bar
29573 * @param {Object} config The config object
29576 Roo.bootstrap.NavProgressBar = function(config){
29577 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29579 this.bullets = this.bullets || [];
29581 // Roo.bootstrap.NavProgressBar.register(this);
29585 * Fires when the active item changes
29586 * @param {Roo.bootstrap.NavProgressBar} this
29587 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29588 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29595 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29600 getAutoCreate : function()
29602 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29606 cls : 'roo-navigation-bar-group',
29610 cls : 'roo-navigation-top-bar'
29614 cls : 'roo-navigation-bullets-bar',
29618 cls : 'roo-navigation-bar'
29625 cls : 'roo-navigation-bottom-bar'
29635 initEvents: function()
29640 onRender : function(ct, position)
29642 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29644 if(this.bullets.length){
29645 Roo.each(this.bullets, function(b){
29654 addItem : function(cfg)
29656 var item = new Roo.bootstrap.NavProgressItem(cfg);
29658 item.parentId = this.id;
29659 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29662 var top = new Roo.bootstrap.Element({
29664 cls : 'roo-navigation-bar-text'
29667 var bottom = new Roo.bootstrap.Element({
29669 cls : 'roo-navigation-bar-text'
29672 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29673 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29675 var topText = new Roo.bootstrap.Element({
29677 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29680 var bottomText = new Roo.bootstrap.Element({
29682 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29685 topText.onRender(top.el, null);
29686 bottomText.onRender(bottom.el, null);
29689 item.bottomEl = bottom;
29692 this.barItems.push(item);
29697 getActive : function()
29699 var active = false;
29701 Roo.each(this.barItems, function(v){
29703 if (!v.isActive()) {
29715 setActiveItem : function(item)
29719 Roo.each(this.barItems, function(v){
29720 if (v.rid == item.rid) {
29724 if (v.isActive()) {
29725 v.setActive(false);
29730 item.setActive(true);
29732 this.fireEvent('changed', this, item, prev);
29735 getBarItem: function(rid)
29739 Roo.each(this.barItems, function(e) {
29740 if (e.rid != rid) {
29751 indexOfItem : function(item)
29755 Roo.each(this.barItems, function(v, i){
29757 if (v.rid != item.rid) {
29768 setActiveNext : function()
29770 var i = this.indexOfItem(this.getActive());
29772 if (i > this.barItems.length) {
29776 this.setActiveItem(this.barItems[i+1]);
29779 setActivePrev : function()
29781 var i = this.indexOfItem(this.getActive());
29787 this.setActiveItem(this.barItems[i-1]);
29790 format : function()
29792 if(!this.barItems.length){
29796 var width = 100 / this.barItems.length;
29798 Roo.each(this.barItems, function(i){
29799 i.el.setStyle('width', width + '%');
29800 i.topEl.el.setStyle('width', width + '%');
29801 i.bottomEl.el.setStyle('width', width + '%');
29810 * Nav Progress Item
29815 * @class Roo.bootstrap.NavProgressItem
29816 * @extends Roo.bootstrap.Component
29817 * Bootstrap NavProgressItem class
29818 * @cfg {String} rid the reference id
29819 * @cfg {Boolean} active (true|false) Is item active default false
29820 * @cfg {Boolean} disabled (true|false) Is item active default false
29821 * @cfg {String} html
29822 * @cfg {String} position (top|bottom) text position default bottom
29823 * @cfg {String} icon show icon instead of number
29826 * Create a new NavProgressItem
29827 * @param {Object} config The config object
29829 Roo.bootstrap.NavProgressItem = function(config){
29830 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29835 * The raw click event for the entire grid.
29836 * @param {Roo.bootstrap.NavProgressItem} this
29837 * @param {Roo.EventObject} e
29844 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29850 position : 'bottom',
29853 getAutoCreate : function()
29855 var iconCls = 'roo-navigation-bar-item-icon';
29857 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29861 cls: 'roo-navigation-bar-item',
29871 cfg.cls += ' active';
29874 cfg.cls += ' disabled';
29880 disable : function()
29882 this.setDisabled(true);
29885 enable : function()
29887 this.setDisabled(false);
29890 initEvents: function()
29892 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29894 this.iconEl.on('click', this.onClick, this);
29897 onClick : function(e)
29899 e.preventDefault();
29905 if(this.fireEvent('click', this, e) === false){
29909 this.parent().setActiveItem(this);
29912 isActive: function ()
29914 return this.active;
29917 setActive : function(state)
29919 if(this.active == state){
29923 this.active = state;
29926 this.el.addClass('active');
29930 this.el.removeClass('active');
29935 setDisabled : function(state)
29937 if(this.disabled == state){
29941 this.disabled = state;
29944 this.el.addClass('disabled');
29948 this.el.removeClass('disabled');
29951 tooltipEl : function()
29953 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29966 * @class Roo.bootstrap.FieldLabel
29967 * @extends Roo.bootstrap.Component
29968 * Bootstrap FieldLabel class
29969 * @cfg {String} html contents of the element
29970 * @cfg {String} tag tag of the element default label
29971 * @cfg {String} cls class of the element
29972 * @cfg {String} target label target
29973 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29974 * @cfg {String} invalidClass default "text-warning"
29975 * @cfg {String} validClass default "text-success"
29976 * @cfg {String} iconTooltip default "This field is required"
29977 * @cfg {String} indicatorpos (left|right) default left
29980 * Create a new FieldLabel
29981 * @param {Object} config The config object
29984 Roo.bootstrap.FieldLabel = function(config){
29985 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29990 * Fires after the field has been marked as invalid.
29991 * @param {Roo.form.FieldLabel} this
29992 * @param {String} msg The validation message
29997 * Fires after the field has been validated with no errors.
29998 * @param {Roo.form.FieldLabel} this
30004 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30011 invalidClass : 'has-warning',
30012 validClass : 'has-success',
30013 iconTooltip : 'This field is required',
30014 indicatorpos : 'left',
30016 getAutoCreate : function(){
30020 cls : 'roo-bootstrap-field-label ' + this.cls,
30025 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
30026 tooltip : this.iconTooltip
30035 if(this.indicatorpos == 'right'){
30038 cls : 'roo-bootstrap-field-label ' + this.cls,
30047 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
30048 tooltip : this.iconTooltip
30057 initEvents: function()
30059 Roo.bootstrap.Element.superclass.initEvents.call(this);
30061 this.indicator = this.indicatorEl();
30063 if(this.indicator){
30064 this.indicator.removeClass('visible');
30065 this.indicator.addClass('invisible');
30068 Roo.bootstrap.FieldLabel.register(this);
30071 indicatorEl : function()
30073 var indicator = this.el.select('i.roo-required-indicator',true).first();
30084 * Mark this field as valid
30086 markValid : function()
30088 if(this.indicator){
30089 this.indicator.removeClass('visible');
30090 this.indicator.addClass('invisible');
30093 this.el.removeClass(this.invalidClass);
30095 this.el.addClass(this.validClass);
30097 this.fireEvent('valid', this);
30101 * Mark this field as invalid
30102 * @param {String} msg The validation message
30104 markInvalid : function(msg)
30106 if(this.indicator){
30107 this.indicator.removeClass('invisible');
30108 this.indicator.addClass('visible');
30111 this.el.removeClass(this.validClass);
30113 this.el.addClass(this.invalidClass);
30115 this.fireEvent('invalid', this, msg);
30121 Roo.apply(Roo.bootstrap.FieldLabel, {
30126 * register a FieldLabel Group
30127 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30129 register : function(label)
30131 if(this.groups.hasOwnProperty(label.target)){
30135 this.groups[label.target] = label;
30139 * fetch a FieldLabel Group based on the target
30140 * @param {string} target
30141 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30143 get: function(target) {
30144 if (typeof(this.groups[target]) == 'undefined') {
30148 return this.groups[target] ;
30157 * page DateSplitField.
30163 * @class Roo.bootstrap.DateSplitField
30164 * @extends Roo.bootstrap.Component
30165 * Bootstrap DateSplitField class
30166 * @cfg {string} fieldLabel - the label associated
30167 * @cfg {Number} labelWidth set the width of label (0-12)
30168 * @cfg {String} labelAlign (top|left)
30169 * @cfg {Boolean} dayAllowBlank (true|false) default false
30170 * @cfg {Boolean} monthAllowBlank (true|false) default false
30171 * @cfg {Boolean} yearAllowBlank (true|false) default false
30172 * @cfg {string} dayPlaceholder
30173 * @cfg {string} monthPlaceholder
30174 * @cfg {string} yearPlaceholder
30175 * @cfg {string} dayFormat default 'd'
30176 * @cfg {string} monthFormat default 'm'
30177 * @cfg {string} yearFormat default 'Y'
30178 * @cfg {Number} labellg set the width of label (1-12)
30179 * @cfg {Number} labelmd set the width of label (1-12)
30180 * @cfg {Number} labelsm set the width of label (1-12)
30181 * @cfg {Number} labelxs set the width of label (1-12)
30185 * Create a new DateSplitField
30186 * @param {Object} config The config object
30189 Roo.bootstrap.DateSplitField = function(config){
30190 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30196 * getting the data of years
30197 * @param {Roo.bootstrap.DateSplitField} this
30198 * @param {Object} years
30203 * getting the data of days
30204 * @param {Roo.bootstrap.DateSplitField} this
30205 * @param {Object} days
30210 * Fires after the field has been marked as invalid.
30211 * @param {Roo.form.Field} this
30212 * @param {String} msg The validation message
30217 * Fires after the field has been validated with no errors.
30218 * @param {Roo.form.Field} this
30224 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30227 labelAlign : 'top',
30229 dayAllowBlank : false,
30230 monthAllowBlank : false,
30231 yearAllowBlank : false,
30232 dayPlaceholder : '',
30233 monthPlaceholder : '',
30234 yearPlaceholder : '',
30238 isFormField : true,
30244 getAutoCreate : function()
30248 cls : 'row roo-date-split-field-group',
30253 cls : 'form-hidden-field roo-date-split-field-group-value',
30259 var labelCls = 'col-md-12';
30260 var contentCls = 'col-md-4';
30262 if(this.fieldLabel){
30266 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30270 html : this.fieldLabel
30275 if(this.labelAlign == 'left'){
30277 if(this.labelWidth > 12){
30278 label.style = "width: " + this.labelWidth + 'px';
30281 if(this.labelWidth < 13 && this.labelmd == 0){
30282 this.labelmd = this.labelWidth;
30285 if(this.labellg > 0){
30286 labelCls = ' col-lg-' + this.labellg;
30287 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30290 if(this.labelmd > 0){
30291 labelCls = ' col-md-' + this.labelmd;
30292 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30295 if(this.labelsm > 0){
30296 labelCls = ' col-sm-' + this.labelsm;
30297 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30300 if(this.labelxs > 0){
30301 labelCls = ' col-xs-' + this.labelxs;
30302 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30306 label.cls += ' ' + labelCls;
30308 cfg.cn.push(label);
30311 Roo.each(['day', 'month', 'year'], function(t){
30314 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30321 inputEl: function ()
30323 return this.el.select('.roo-date-split-field-group-value', true).first();
30326 onRender : function(ct, position)
30330 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30332 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30334 this.dayField = new Roo.bootstrap.ComboBox({
30335 allowBlank : this.dayAllowBlank,
30336 alwaysQuery : true,
30337 displayField : 'value',
30340 forceSelection : true,
30342 placeholder : this.dayPlaceholder,
30343 selectOnFocus : true,
30344 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30345 triggerAction : 'all',
30347 valueField : 'value',
30348 store : new Roo.data.SimpleStore({
30349 data : (function() {
30351 _this.fireEvent('days', _this, days);
30354 fields : [ 'value' ]
30357 select : function (_self, record, index)
30359 _this.setValue(_this.getValue());
30364 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30366 this.monthField = new Roo.bootstrap.MonthField({
30367 after : '<i class=\"fa fa-calendar\"></i>',
30368 allowBlank : this.monthAllowBlank,
30369 placeholder : this.monthPlaceholder,
30372 render : function (_self)
30374 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30375 e.preventDefault();
30379 select : function (_self, oldvalue, newvalue)
30381 _this.setValue(_this.getValue());
30386 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30388 this.yearField = new Roo.bootstrap.ComboBox({
30389 allowBlank : this.yearAllowBlank,
30390 alwaysQuery : true,
30391 displayField : 'value',
30394 forceSelection : true,
30396 placeholder : this.yearPlaceholder,
30397 selectOnFocus : true,
30398 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30399 triggerAction : 'all',
30401 valueField : 'value',
30402 store : new Roo.data.SimpleStore({
30403 data : (function() {
30405 _this.fireEvent('years', _this, years);
30408 fields : [ 'value' ]
30411 select : function (_self, record, index)
30413 _this.setValue(_this.getValue());
30418 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30421 setValue : function(v, format)
30423 this.inputEl.dom.value = v;
30425 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30427 var d = Date.parseDate(v, f);
30434 this.setDay(d.format(this.dayFormat));
30435 this.setMonth(d.format(this.monthFormat));
30436 this.setYear(d.format(this.yearFormat));
30443 setDay : function(v)
30445 this.dayField.setValue(v);
30446 this.inputEl.dom.value = this.getValue();
30451 setMonth : function(v)
30453 this.monthField.setValue(v, true);
30454 this.inputEl.dom.value = this.getValue();
30459 setYear : function(v)
30461 this.yearField.setValue(v);
30462 this.inputEl.dom.value = this.getValue();
30467 getDay : function()
30469 return this.dayField.getValue();
30472 getMonth : function()
30474 return this.monthField.getValue();
30477 getYear : function()
30479 return this.yearField.getValue();
30482 getValue : function()
30484 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30486 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30496 this.inputEl.dom.value = '';
30501 validate : function()
30503 var d = this.dayField.validate();
30504 var m = this.monthField.validate();
30505 var y = this.yearField.validate();
30510 (!this.dayAllowBlank && !d) ||
30511 (!this.monthAllowBlank && !m) ||
30512 (!this.yearAllowBlank && !y)
30517 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30526 this.markInvalid();
30531 markValid : function()
30534 var label = this.el.select('label', true).first();
30535 var icon = this.el.select('i.fa-star', true).first();
30541 this.fireEvent('valid', this);
30545 * Mark this field as invalid
30546 * @param {String} msg The validation message
30548 markInvalid : function(msg)
30551 var label = this.el.select('label', true).first();
30552 var icon = this.el.select('i.fa-star', true).first();
30554 if(label && !icon){
30555 this.el.select('.roo-date-split-field-label', true).createChild({
30557 cls : 'text-danger fa fa-lg fa-star',
30558 tooltip : 'This field is required',
30559 style : 'margin-right:5px;'
30563 this.fireEvent('invalid', this, msg);
30566 clearInvalid : function()
30568 var label = this.el.select('label', true).first();
30569 var icon = this.el.select('i.fa-star', true).first();
30575 this.fireEvent('valid', this);
30578 getName: function()
30588 * http://masonry.desandro.com
30590 * The idea is to render all the bricks based on vertical width...
30592 * The original code extends 'outlayer' - we might need to use that....
30598 * @class Roo.bootstrap.LayoutMasonry
30599 * @extends Roo.bootstrap.Component
30600 * Bootstrap Layout Masonry class
30603 * Create a new Element
30604 * @param {Object} config The config object
30607 Roo.bootstrap.LayoutMasonry = function(config){
30609 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30613 Roo.bootstrap.LayoutMasonry.register(this);
30619 * Fire after layout the items
30620 * @param {Roo.bootstrap.LayoutMasonry} this
30621 * @param {Roo.EventObject} e
30628 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30631 * @cfg {Boolean} isLayoutInstant = no animation?
30633 isLayoutInstant : false, // needed?
30636 * @cfg {Number} boxWidth width of the columns
30641 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30646 * @cfg {Number} padWidth padding below box..
30651 * @cfg {Number} gutter gutter width..
30656 * @cfg {Number} maxCols maximum number of columns
30662 * @cfg {Boolean} isAutoInitial defalut true
30664 isAutoInitial : true,
30669 * @cfg {Boolean} isHorizontal defalut false
30671 isHorizontal : false,
30673 currentSize : null,
30679 bricks: null, //CompositeElement
30683 _isLayoutInited : false,
30685 // isAlternative : false, // only use for vertical layout...
30688 * @cfg {Number} alternativePadWidth padding below box..
30690 alternativePadWidth : 50,
30692 selectedBrick : [],
30694 getAutoCreate : function(){
30696 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30700 cls: 'blog-masonary-wrapper ' + this.cls,
30702 cls : 'mas-boxes masonary'
30709 getChildContainer: function( )
30711 if (this.boxesEl) {
30712 return this.boxesEl;
30715 this.boxesEl = this.el.select('.mas-boxes').first();
30717 return this.boxesEl;
30721 initEvents : function()
30725 if(this.isAutoInitial){
30726 Roo.log('hook children rendered');
30727 this.on('childrenrendered', function() {
30728 Roo.log('children rendered');
30734 initial : function()
30736 this.selectedBrick = [];
30738 this.currentSize = this.el.getBox(true);
30740 Roo.EventManager.onWindowResize(this.resize, this);
30742 if(!this.isAutoInitial){
30750 //this.layout.defer(500,this);
30754 resize : function()
30756 var cs = this.el.getBox(true);
30759 this.currentSize.width == cs.width &&
30760 this.currentSize.x == cs.x &&
30761 this.currentSize.height == cs.height &&
30762 this.currentSize.y == cs.y
30764 Roo.log("no change in with or X or Y");
30768 this.currentSize = cs;
30774 layout : function()
30776 this._resetLayout();
30778 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30780 this.layoutItems( isInstant );
30782 this._isLayoutInited = true;
30784 this.fireEvent('layout', this);
30788 _resetLayout : function()
30790 if(this.isHorizontal){
30791 this.horizontalMeasureColumns();
30795 this.verticalMeasureColumns();
30799 verticalMeasureColumns : function()
30801 this.getContainerWidth();
30803 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30804 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30808 var boxWidth = this.boxWidth + this.padWidth;
30810 if(this.containerWidth < this.boxWidth){
30811 boxWidth = this.containerWidth
30814 var containerWidth = this.containerWidth;
30816 var cols = Math.floor(containerWidth / boxWidth);
30818 this.cols = Math.max( cols, 1 );
30820 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30822 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30824 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30826 this.colWidth = boxWidth + avail - this.padWidth;
30828 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30829 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30832 horizontalMeasureColumns : function()
30834 this.getContainerWidth();
30836 var boxWidth = this.boxWidth;
30838 if(this.containerWidth < boxWidth){
30839 boxWidth = this.containerWidth;
30842 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30844 this.el.setHeight(boxWidth);
30848 getContainerWidth : function()
30850 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30853 layoutItems : function( isInstant )
30855 Roo.log(this.bricks);
30857 var items = Roo.apply([], this.bricks);
30859 if(this.isHorizontal){
30860 this._horizontalLayoutItems( items , isInstant );
30864 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30865 // this._verticalAlternativeLayoutItems( items , isInstant );
30869 this._verticalLayoutItems( items , isInstant );
30873 _verticalLayoutItems : function ( items , isInstant)
30875 if ( !items || !items.length ) {
30880 ['xs', 'xs', 'xs', 'tall'],
30881 ['xs', 'xs', 'tall'],
30882 ['xs', 'xs', 'sm'],
30883 ['xs', 'xs', 'xs'],
30889 ['sm', 'xs', 'xs'],
30893 ['tall', 'xs', 'xs', 'xs'],
30894 ['tall', 'xs', 'xs'],
30906 Roo.each(items, function(item, k){
30908 switch (item.size) {
30909 // these layouts take up a full box,
30920 boxes.push([item]);
30943 var filterPattern = function(box, length)
30951 var pattern = box.slice(0, length);
30955 Roo.each(pattern, function(i){
30956 format.push(i.size);
30959 Roo.each(standard, function(s){
30961 if(String(s) != String(format)){
30970 if(!match && length == 1){
30975 filterPattern(box, length - 1);
30979 queue.push(pattern);
30981 box = box.slice(length, box.length);
30983 filterPattern(box, 4);
30989 Roo.each(boxes, function(box, k){
30995 if(box.length == 1){
31000 filterPattern(box, 4);
31004 this._processVerticalLayoutQueue( queue, isInstant );
31008 // _verticalAlternativeLayoutItems : function( items , isInstant )
31010 // if ( !items || !items.length ) {
31014 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31018 _horizontalLayoutItems : function ( items , isInstant)
31020 if ( !items || !items.length || items.length < 3) {
31026 var eItems = items.slice(0, 3);
31028 items = items.slice(3, items.length);
31031 ['xs', 'xs', 'xs', 'wide'],
31032 ['xs', 'xs', 'wide'],
31033 ['xs', 'xs', 'sm'],
31034 ['xs', 'xs', 'xs'],
31040 ['sm', 'xs', 'xs'],
31044 ['wide', 'xs', 'xs', 'xs'],
31045 ['wide', 'xs', 'xs'],
31058 Roo.each(items, function(item, k){
31060 switch (item.size) {
31071 boxes.push([item]);
31095 var filterPattern = function(box, length)
31103 var pattern = box.slice(0, length);
31107 Roo.each(pattern, function(i){
31108 format.push(i.size);
31111 Roo.each(standard, function(s){
31113 if(String(s) != String(format)){
31122 if(!match && length == 1){
31127 filterPattern(box, length - 1);
31131 queue.push(pattern);
31133 box = box.slice(length, box.length);
31135 filterPattern(box, 4);
31141 Roo.each(boxes, function(box, k){
31147 if(box.length == 1){
31152 filterPattern(box, 4);
31159 var pos = this.el.getBox(true);
31163 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31165 var hit_end = false;
31167 Roo.each(queue, function(box){
31171 Roo.each(box, function(b){
31173 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31183 Roo.each(box, function(b){
31185 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31188 mx = Math.max(mx, b.x);
31192 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31196 Roo.each(box, function(b){
31198 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31212 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31215 /** Sets position of item in DOM
31216 * @param {Element} item
31217 * @param {Number} x - horizontal position
31218 * @param {Number} y - vertical position
31219 * @param {Boolean} isInstant - disables transitions
31221 _processVerticalLayoutQueue : function( queue, isInstant )
31223 var pos = this.el.getBox(true);
31228 for (var i = 0; i < this.cols; i++){
31232 Roo.each(queue, function(box, k){
31234 var col = k % this.cols;
31236 Roo.each(box, function(b,kk){
31238 b.el.position('absolute');
31240 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31241 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31243 if(b.size == 'md-left' || b.size == 'md-right'){
31244 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31245 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31248 b.el.setWidth(width);
31249 b.el.setHeight(height);
31251 b.el.select('iframe',true).setSize(width,height);
31255 for (var i = 0; i < this.cols; i++){
31257 if(maxY[i] < maxY[col]){
31262 col = Math.min(col, i);
31266 x = pos.x + col * (this.colWidth + this.padWidth);
31270 var positions = [];
31272 switch (box.length){
31274 positions = this.getVerticalOneBoxColPositions(x, y, box);
31277 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31280 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31283 positions = this.getVerticalFourBoxColPositions(x, y, box);
31289 Roo.each(box, function(b,kk){
31291 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31293 var sz = b.el.getSize();
31295 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31303 for (var i = 0; i < this.cols; i++){
31304 mY = Math.max(mY, maxY[i]);
31307 this.el.setHeight(mY - pos.y);
31311 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31313 // var pos = this.el.getBox(true);
31316 // var maxX = pos.right;
31318 // var maxHeight = 0;
31320 // Roo.each(items, function(item, k){
31324 // item.el.position('absolute');
31326 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31328 // item.el.setWidth(width);
31330 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31332 // item.el.setHeight(height);
31335 // item.el.setXY([x, y], isInstant ? false : true);
31337 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31340 // y = y + height + this.alternativePadWidth;
31342 // maxHeight = maxHeight + height + this.alternativePadWidth;
31346 // this.el.setHeight(maxHeight);
31350 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31352 var pos = this.el.getBox(true);
31357 var maxX = pos.right;
31359 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31361 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31363 Roo.each(queue, function(box, k){
31365 Roo.each(box, function(b, kk){
31367 b.el.position('absolute');
31369 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31370 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31372 if(b.size == 'md-left' || b.size == 'md-right'){
31373 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31374 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31377 b.el.setWidth(width);
31378 b.el.setHeight(height);
31386 var positions = [];
31388 switch (box.length){
31390 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31393 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31396 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31399 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31405 Roo.each(box, function(b,kk){
31407 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31409 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31417 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31419 Roo.each(eItems, function(b,k){
31421 b.size = (k == 0) ? 'sm' : 'xs';
31422 b.x = (k == 0) ? 2 : 1;
31423 b.y = (k == 0) ? 2 : 1;
31425 b.el.position('absolute');
31427 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31429 b.el.setWidth(width);
31431 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31433 b.el.setHeight(height);
31437 var positions = [];
31440 x : maxX - this.unitWidth * 2 - this.gutter,
31445 x : maxX - this.unitWidth,
31446 y : minY + (this.unitWidth + this.gutter) * 2
31450 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31454 Roo.each(eItems, function(b,k){
31456 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31462 getVerticalOneBoxColPositions : function(x, y, box)
31466 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31468 if(box[0].size == 'md-left'){
31472 if(box[0].size == 'md-right'){
31477 x : x + (this.unitWidth + this.gutter) * rand,
31484 getVerticalTwoBoxColPositions : function(x, y, box)
31488 if(box[0].size == 'xs'){
31492 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31496 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31510 x : x + (this.unitWidth + this.gutter) * 2,
31511 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31518 getVerticalThreeBoxColPositions : function(x, y, box)
31522 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31530 x : x + (this.unitWidth + this.gutter) * 1,
31535 x : x + (this.unitWidth + this.gutter) * 2,
31543 if(box[0].size == 'xs' && box[1].size == 'xs'){
31552 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31556 x : x + (this.unitWidth + this.gutter) * 1,
31570 x : x + (this.unitWidth + this.gutter) * 2,
31575 x : x + (this.unitWidth + this.gutter) * 2,
31576 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31583 getVerticalFourBoxColPositions : function(x, y, box)
31587 if(box[0].size == 'xs'){
31596 y : y + (this.unitHeight + this.gutter) * 1
31601 y : y + (this.unitHeight + this.gutter) * 2
31605 x : x + (this.unitWidth + this.gutter) * 1,
31619 x : x + (this.unitWidth + this.gutter) * 2,
31624 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31625 y : y + (this.unitHeight + this.gutter) * 1
31629 x : x + (this.unitWidth + this.gutter) * 2,
31630 y : y + (this.unitWidth + this.gutter) * 2
31637 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31641 if(box[0].size == 'md-left'){
31643 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31650 if(box[0].size == 'md-right'){
31652 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31653 y : minY + (this.unitWidth + this.gutter) * 1
31659 var rand = Math.floor(Math.random() * (4 - box[0].y));
31662 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31663 y : minY + (this.unitWidth + this.gutter) * rand
31670 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31674 if(box[0].size == 'xs'){
31677 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31682 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31683 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31691 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31696 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31697 y : minY + (this.unitWidth + this.gutter) * 2
31704 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31708 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31711 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31716 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31717 y : minY + (this.unitWidth + this.gutter) * 1
31721 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31722 y : minY + (this.unitWidth + this.gutter) * 2
31729 if(box[0].size == 'xs' && box[1].size == 'xs'){
31732 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31737 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31742 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31743 y : minY + (this.unitWidth + this.gutter) * 1
31751 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31756 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31757 y : minY + (this.unitWidth + this.gutter) * 2
31761 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31762 y : minY + (this.unitWidth + this.gutter) * 2
31769 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31773 if(box[0].size == 'xs'){
31776 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31781 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31786 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),
31791 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31792 y : minY + (this.unitWidth + this.gutter) * 1
31800 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31805 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31806 y : minY + (this.unitWidth + this.gutter) * 2
31810 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31811 y : minY + (this.unitWidth + this.gutter) * 2
31815 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),
31816 y : minY + (this.unitWidth + this.gutter) * 2
31824 * remove a Masonry Brick
31825 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31827 removeBrick : function(brick_id)
31833 for (var i = 0; i<this.bricks.length; i++) {
31834 if (this.bricks[i].id == brick_id) {
31835 this.bricks.splice(i,1);
31836 this.el.dom.removeChild(Roo.get(brick_id).dom);
31843 * adds a Masonry Brick
31844 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31846 addBrick : function(cfg)
31848 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31849 //this.register(cn);
31850 cn.parentId = this.id;
31851 cn.onRender(this.el, null);
31856 * register a Masonry Brick
31857 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31860 register : function(brick)
31862 this.bricks.push(brick);
31863 brick.masonryId = this.id;
31867 * clear all the Masonry Brick
31869 clearAll : function()
31872 //this.getChildContainer().dom.innerHTML = "";
31873 this.el.dom.innerHTML = '';
31876 getSelected : function()
31878 if (!this.selectedBrick) {
31882 return this.selectedBrick;
31886 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31890 * register a Masonry Layout
31891 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31894 register : function(layout)
31896 this.groups[layout.id] = layout;
31899 * fetch a Masonry Layout based on the masonry layout ID
31900 * @param {string} the masonry layout to add
31901 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31904 get: function(layout_id) {
31905 if (typeof(this.groups[layout_id]) == 'undefined') {
31908 return this.groups[layout_id] ;
31920 * http://masonry.desandro.com
31922 * The idea is to render all the bricks based on vertical width...
31924 * The original code extends 'outlayer' - we might need to use that....
31930 * @class Roo.bootstrap.LayoutMasonryAuto
31931 * @extends Roo.bootstrap.Component
31932 * Bootstrap Layout Masonry class
31935 * Create a new Element
31936 * @param {Object} config The config object
31939 Roo.bootstrap.LayoutMasonryAuto = function(config){
31940 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31943 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31946 * @cfg {Boolean} isFitWidth - resize the width..
31948 isFitWidth : false, // options..
31950 * @cfg {Boolean} isOriginLeft = left align?
31952 isOriginLeft : true,
31954 * @cfg {Boolean} isOriginTop = top align?
31956 isOriginTop : false,
31958 * @cfg {Boolean} isLayoutInstant = no animation?
31960 isLayoutInstant : false, // needed?
31962 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31964 isResizingContainer : true,
31966 * @cfg {Number} columnWidth width of the columns
31972 * @cfg {Number} maxCols maximum number of columns
31977 * @cfg {Number} padHeight padding below box..
31983 * @cfg {Boolean} isAutoInitial defalut true
31986 isAutoInitial : true,
31992 initialColumnWidth : 0,
31993 currentSize : null,
31995 colYs : null, // array.
32002 bricks: null, //CompositeElement
32003 cols : 0, // array?
32004 // element : null, // wrapped now this.el
32005 _isLayoutInited : null,
32008 getAutoCreate : function(){
32012 cls: 'blog-masonary-wrapper ' + this.cls,
32014 cls : 'mas-boxes masonary'
32021 getChildContainer: function( )
32023 if (this.boxesEl) {
32024 return this.boxesEl;
32027 this.boxesEl = this.el.select('.mas-boxes').first();
32029 return this.boxesEl;
32033 initEvents : function()
32037 if(this.isAutoInitial){
32038 Roo.log('hook children rendered');
32039 this.on('childrenrendered', function() {
32040 Roo.log('children rendered');
32047 initial : function()
32049 this.reloadItems();
32051 this.currentSize = this.el.getBox(true);
32053 /// was window resize... - let's see if this works..
32054 Roo.EventManager.onWindowResize(this.resize, this);
32056 if(!this.isAutoInitial){
32061 this.layout.defer(500,this);
32064 reloadItems: function()
32066 this.bricks = this.el.select('.masonry-brick', true);
32068 this.bricks.each(function(b) {
32069 //Roo.log(b.getSize());
32070 if (!b.attr('originalwidth')) {
32071 b.attr('originalwidth', b.getSize().width);
32076 Roo.log(this.bricks.elements.length);
32079 resize : function()
32082 var cs = this.el.getBox(true);
32084 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32085 Roo.log("no change in with or X");
32088 this.currentSize = cs;
32092 layout : function()
32095 this._resetLayout();
32096 //this._manageStamps();
32098 // don't animate first layout
32099 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32100 this.layoutItems( isInstant );
32102 // flag for initalized
32103 this._isLayoutInited = true;
32106 layoutItems : function( isInstant )
32108 //var items = this._getItemsForLayout( this.items );
32109 // original code supports filtering layout items.. we just ignore it..
32111 this._layoutItems( this.bricks , isInstant );
32113 this._postLayout();
32115 _layoutItems : function ( items , isInstant)
32117 //this.fireEvent( 'layout', this, items );
32120 if ( !items || !items.elements.length ) {
32121 // no items, emit event with empty array
32126 items.each(function(item) {
32127 Roo.log("layout item");
32129 // get x/y object from method
32130 var position = this._getItemLayoutPosition( item );
32132 position.item = item;
32133 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32134 queue.push( position );
32137 this._processLayoutQueue( queue );
32139 /** Sets position of item in DOM
32140 * @param {Element} item
32141 * @param {Number} x - horizontal position
32142 * @param {Number} y - vertical position
32143 * @param {Boolean} isInstant - disables transitions
32145 _processLayoutQueue : function( queue )
32147 for ( var i=0, len = queue.length; i < len; i++ ) {
32148 var obj = queue[i];
32149 obj.item.position('absolute');
32150 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32156 * Any logic you want to do after each layout,
32157 * i.e. size the container
32159 _postLayout : function()
32161 this.resizeContainer();
32164 resizeContainer : function()
32166 if ( !this.isResizingContainer ) {
32169 var size = this._getContainerSize();
32171 this.el.setSize(size.width,size.height);
32172 this.boxesEl.setSize(size.width,size.height);
32178 _resetLayout : function()
32180 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32181 this.colWidth = this.el.getWidth();
32182 //this.gutter = this.el.getWidth();
32184 this.measureColumns();
32190 this.colYs.push( 0 );
32196 measureColumns : function()
32198 this.getContainerWidth();
32199 // if columnWidth is 0, default to outerWidth of first item
32200 if ( !this.columnWidth ) {
32201 var firstItem = this.bricks.first();
32202 Roo.log(firstItem);
32203 this.columnWidth = this.containerWidth;
32204 if (firstItem && firstItem.attr('originalwidth') ) {
32205 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32207 // columnWidth fall back to item of first element
32208 Roo.log("set column width?");
32209 this.initialColumnWidth = this.columnWidth ;
32211 // if first elem has no width, default to size of container
32216 if (this.initialColumnWidth) {
32217 this.columnWidth = this.initialColumnWidth;
32222 // column width is fixed at the top - however if container width get's smaller we should
32225 // this bit calcs how man columns..
32227 var columnWidth = this.columnWidth += this.gutter;
32229 // calculate columns
32230 var containerWidth = this.containerWidth + this.gutter;
32232 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32233 // fix rounding errors, typically with gutters
32234 var excess = columnWidth - containerWidth % columnWidth;
32237 // if overshoot is less than a pixel, round up, otherwise floor it
32238 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32239 cols = Math[ mathMethod ]( cols );
32240 this.cols = Math.max( cols, 1 );
32241 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32243 // padding positioning..
32244 var totalColWidth = this.cols * this.columnWidth;
32245 var padavail = this.containerWidth - totalColWidth;
32246 // so for 2 columns - we need 3 'pads'
32248 var padNeeded = (1+this.cols) * this.padWidth;
32250 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32252 this.columnWidth += padExtra
32253 //this.padWidth = Math.floor(padavail / ( this.cols));
32255 // adjust colum width so that padding is fixed??
32257 // we have 3 columns ... total = width * 3
32258 // we have X left over... that should be used by
32260 //if (this.expandC) {
32268 getContainerWidth : function()
32270 /* // container is parent if fit width
32271 var container = this.isFitWidth ? this.element.parentNode : this.element;
32272 // check that this.size and size are there
32273 // IE8 triggers resize on body size change, so they might not be
32275 var size = getSize( container ); //FIXME
32276 this.containerWidth = size && size.innerWidth; //FIXME
32279 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32283 _getItemLayoutPosition : function( item ) // what is item?
32285 // we resize the item to our columnWidth..
32287 item.setWidth(this.columnWidth);
32288 item.autoBoxAdjust = false;
32290 var sz = item.getSize();
32292 // how many columns does this brick span
32293 var remainder = this.containerWidth % this.columnWidth;
32295 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32296 // round if off by 1 pixel, otherwise use ceil
32297 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32298 colSpan = Math.min( colSpan, this.cols );
32300 // normally this should be '1' as we dont' currently allow multi width columns..
32302 var colGroup = this._getColGroup( colSpan );
32303 // get the minimum Y value from the columns
32304 var minimumY = Math.min.apply( Math, colGroup );
32305 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32307 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32309 // position the brick
32311 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32312 y: this.currentSize.y + minimumY + this.padHeight
32316 // apply setHeight to necessary columns
32317 var setHeight = minimumY + sz.height + this.padHeight;
32318 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32320 var setSpan = this.cols + 1 - colGroup.length;
32321 for ( var i = 0; i < setSpan; i++ ) {
32322 this.colYs[ shortColIndex + i ] = setHeight ;
32329 * @param {Number} colSpan - number of columns the element spans
32330 * @returns {Array} colGroup
32332 _getColGroup : function( colSpan )
32334 if ( colSpan < 2 ) {
32335 // if brick spans only one column, use all the column Ys
32340 // how many different places could this brick fit horizontally
32341 var groupCount = this.cols + 1 - colSpan;
32342 // for each group potential horizontal position
32343 for ( var i = 0; i < groupCount; i++ ) {
32344 // make an array of colY values for that one group
32345 var groupColYs = this.colYs.slice( i, i + colSpan );
32346 // and get the max value of the array
32347 colGroup[i] = Math.max.apply( Math, groupColYs );
32352 _manageStamp : function( stamp )
32354 var stampSize = stamp.getSize();
32355 var offset = stamp.getBox();
32356 // get the columns that this stamp affects
32357 var firstX = this.isOriginLeft ? offset.x : offset.right;
32358 var lastX = firstX + stampSize.width;
32359 var firstCol = Math.floor( firstX / this.columnWidth );
32360 firstCol = Math.max( 0, firstCol );
32362 var lastCol = Math.floor( lastX / this.columnWidth );
32363 // lastCol should not go over if multiple of columnWidth #425
32364 lastCol -= lastX % this.columnWidth ? 0 : 1;
32365 lastCol = Math.min( this.cols - 1, lastCol );
32367 // set colYs to bottom of the stamp
32368 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32371 for ( var i = firstCol; i <= lastCol; i++ ) {
32372 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32377 _getContainerSize : function()
32379 this.maxY = Math.max.apply( Math, this.colYs );
32384 if ( this.isFitWidth ) {
32385 size.width = this._getContainerFitWidth();
32391 _getContainerFitWidth : function()
32393 var unusedCols = 0;
32394 // count unused columns
32397 if ( this.colYs[i] !== 0 ) {
32402 // fit container to columns that have been used
32403 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32406 needsResizeLayout : function()
32408 var previousWidth = this.containerWidth;
32409 this.getContainerWidth();
32410 return previousWidth !== this.containerWidth;
32425 * @class Roo.bootstrap.MasonryBrick
32426 * @extends Roo.bootstrap.Component
32427 * Bootstrap MasonryBrick class
32430 * Create a new MasonryBrick
32431 * @param {Object} config The config object
32434 Roo.bootstrap.MasonryBrick = function(config){
32436 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32438 Roo.bootstrap.MasonryBrick.register(this);
32444 * When a MasonryBrick is clcik
32445 * @param {Roo.bootstrap.MasonryBrick} this
32446 * @param {Roo.EventObject} e
32452 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32455 * @cfg {String} title
32459 * @cfg {String} html
32463 * @cfg {String} bgimage
32467 * @cfg {String} videourl
32471 * @cfg {String} cls
32475 * @cfg {String} href
32479 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32484 * @cfg {String} placetitle (center|bottom)
32489 * @cfg {Boolean} isFitContainer defalut true
32491 isFitContainer : true,
32494 * @cfg {Boolean} preventDefault defalut false
32496 preventDefault : false,
32499 * @cfg {Boolean} inverse defalut false
32501 maskInverse : false,
32503 getAutoCreate : function()
32505 if(!this.isFitContainer){
32506 return this.getSplitAutoCreate();
32509 var cls = 'masonry-brick masonry-brick-full';
32511 if(this.href.length){
32512 cls += ' masonry-brick-link';
32515 if(this.bgimage.length){
32516 cls += ' masonry-brick-image';
32519 if(this.maskInverse){
32520 cls += ' mask-inverse';
32523 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32524 cls += ' enable-mask';
32528 cls += ' masonry-' + this.size + '-brick';
32531 if(this.placetitle.length){
32533 switch (this.placetitle) {
32535 cls += ' masonry-center-title';
32538 cls += ' masonry-bottom-title';
32545 if(!this.html.length && !this.bgimage.length){
32546 cls += ' masonry-center-title';
32549 if(!this.html.length && this.bgimage.length){
32550 cls += ' masonry-bottom-title';
32555 cls += ' ' + this.cls;
32559 tag: (this.href.length) ? 'a' : 'div',
32564 cls: 'masonry-brick-mask'
32568 cls: 'masonry-brick-paragraph',
32574 if(this.href.length){
32575 cfg.href = this.href;
32578 var cn = cfg.cn[1].cn;
32580 if(this.title.length){
32583 cls: 'masonry-brick-title',
32588 if(this.html.length){
32591 cls: 'masonry-brick-text',
32596 if (!this.title.length && !this.html.length) {
32597 cfg.cn[1].cls += ' hide';
32600 if(this.bgimage.length){
32603 cls: 'masonry-brick-image-view',
32608 if(this.videourl.length){
32609 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32610 // youtube support only?
32613 cls: 'masonry-brick-image-view',
32616 allowfullscreen : true
32624 getSplitAutoCreate : function()
32626 var cls = 'masonry-brick masonry-brick-split';
32628 if(this.href.length){
32629 cls += ' masonry-brick-link';
32632 if(this.bgimage.length){
32633 cls += ' masonry-brick-image';
32637 cls += ' masonry-' + this.size + '-brick';
32640 switch (this.placetitle) {
32642 cls += ' masonry-center-title';
32645 cls += ' masonry-bottom-title';
32648 if(!this.bgimage.length){
32649 cls += ' masonry-center-title';
32652 if(this.bgimage.length){
32653 cls += ' masonry-bottom-title';
32659 cls += ' ' + this.cls;
32663 tag: (this.href.length) ? 'a' : 'div',
32668 cls: 'masonry-brick-split-head',
32672 cls: 'masonry-brick-paragraph',
32679 cls: 'masonry-brick-split-body',
32685 if(this.href.length){
32686 cfg.href = this.href;
32689 if(this.title.length){
32690 cfg.cn[0].cn[0].cn.push({
32692 cls: 'masonry-brick-title',
32697 if(this.html.length){
32698 cfg.cn[1].cn.push({
32700 cls: 'masonry-brick-text',
32705 if(this.bgimage.length){
32706 cfg.cn[0].cn.push({
32708 cls: 'masonry-brick-image-view',
32713 if(this.videourl.length){
32714 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32715 // youtube support only?
32716 cfg.cn[0].cn.cn.push({
32718 cls: 'masonry-brick-image-view',
32721 allowfullscreen : true
32728 initEvents: function()
32730 switch (this.size) {
32763 this.el.on('touchstart', this.onTouchStart, this);
32764 this.el.on('touchmove', this.onTouchMove, this);
32765 this.el.on('touchend', this.onTouchEnd, this);
32766 this.el.on('contextmenu', this.onContextMenu, this);
32768 this.el.on('mouseenter' ,this.enter, this);
32769 this.el.on('mouseleave', this.leave, this);
32770 this.el.on('click', this.onClick, this);
32773 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32774 this.parent().bricks.push(this);
32779 onClick: function(e, el)
32781 var time = this.endTimer - this.startTimer;
32782 // Roo.log(e.preventDefault());
32785 e.preventDefault();
32790 if(!this.preventDefault){
32794 e.preventDefault();
32796 if (this.activeClass != '') {
32797 this.selectBrick();
32800 this.fireEvent('click', this, e);
32803 enter: function(e, el)
32805 e.preventDefault();
32807 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32811 if(this.bgimage.length && this.html.length){
32812 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32816 leave: function(e, el)
32818 e.preventDefault();
32820 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32824 if(this.bgimage.length && this.html.length){
32825 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32829 onTouchStart: function(e, el)
32831 // e.preventDefault();
32833 this.touchmoved = false;
32835 if(!this.isFitContainer){
32839 if(!this.bgimage.length || !this.html.length){
32843 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32845 this.timer = new Date().getTime();
32849 onTouchMove: function(e, el)
32851 this.touchmoved = true;
32854 onContextMenu : function(e,el)
32856 e.preventDefault();
32857 e.stopPropagation();
32861 onTouchEnd: function(e, el)
32863 // e.preventDefault();
32865 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32872 if(!this.bgimage.length || !this.html.length){
32874 if(this.href.length){
32875 window.location.href = this.href;
32881 if(!this.isFitContainer){
32885 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32887 window.location.href = this.href;
32890 //selection on single brick only
32891 selectBrick : function() {
32893 if (!this.parentId) {
32897 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32898 var index = m.selectedBrick.indexOf(this.id);
32901 m.selectedBrick.splice(index,1);
32902 this.el.removeClass(this.activeClass);
32906 for(var i = 0; i < m.selectedBrick.length; i++) {
32907 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32908 b.el.removeClass(b.activeClass);
32911 m.selectedBrick = [];
32913 m.selectedBrick.push(this.id);
32914 this.el.addClass(this.activeClass);
32918 isSelected : function(){
32919 return this.el.hasClass(this.activeClass);
32924 Roo.apply(Roo.bootstrap.MasonryBrick, {
32927 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32929 * register a Masonry Brick
32930 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32933 register : function(brick)
32935 //this.groups[brick.id] = brick;
32936 this.groups.add(brick.id, brick);
32939 * fetch a masonry brick based on the masonry brick ID
32940 * @param {string} the masonry brick to add
32941 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32944 get: function(brick_id)
32946 // if (typeof(this.groups[brick_id]) == 'undefined') {
32949 // return this.groups[brick_id] ;
32951 if(this.groups.key(brick_id)) {
32952 return this.groups.key(brick_id);
32970 * @class Roo.bootstrap.Brick
32971 * @extends Roo.bootstrap.Component
32972 * Bootstrap Brick class
32975 * Create a new Brick
32976 * @param {Object} config The config object
32979 Roo.bootstrap.Brick = function(config){
32980 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32986 * When a Brick is click
32987 * @param {Roo.bootstrap.Brick} this
32988 * @param {Roo.EventObject} e
32994 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32997 * @cfg {String} title
33001 * @cfg {String} html
33005 * @cfg {String} bgimage
33009 * @cfg {String} cls
33013 * @cfg {String} href
33017 * @cfg {String} video
33021 * @cfg {Boolean} square
33025 getAutoCreate : function()
33027 var cls = 'roo-brick';
33029 if(this.href.length){
33030 cls += ' roo-brick-link';
33033 if(this.bgimage.length){
33034 cls += ' roo-brick-image';
33037 if(!this.html.length && !this.bgimage.length){
33038 cls += ' roo-brick-center-title';
33041 if(!this.html.length && this.bgimage.length){
33042 cls += ' roo-brick-bottom-title';
33046 cls += ' ' + this.cls;
33050 tag: (this.href.length) ? 'a' : 'div',
33055 cls: 'roo-brick-paragraph',
33061 if(this.href.length){
33062 cfg.href = this.href;
33065 var cn = cfg.cn[0].cn;
33067 if(this.title.length){
33070 cls: 'roo-brick-title',
33075 if(this.html.length){
33078 cls: 'roo-brick-text',
33085 if(this.bgimage.length){
33088 cls: 'roo-brick-image-view',
33096 initEvents: function()
33098 if(this.title.length || this.html.length){
33099 this.el.on('mouseenter' ,this.enter, this);
33100 this.el.on('mouseleave', this.leave, this);
33103 Roo.EventManager.onWindowResize(this.resize, this);
33105 if(this.bgimage.length){
33106 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33107 this.imageEl.on('load', this.onImageLoad, this);
33114 onImageLoad : function()
33119 resize : function()
33121 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33123 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33125 if(this.bgimage.length){
33126 var image = this.el.select('.roo-brick-image-view', true).first();
33128 image.setWidth(paragraph.getWidth());
33131 image.setHeight(paragraph.getWidth());
33134 this.el.setHeight(image.getHeight());
33135 paragraph.setHeight(image.getHeight());
33141 enter: function(e, el)
33143 e.preventDefault();
33145 if(this.bgimage.length){
33146 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33147 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33151 leave: function(e, el)
33153 e.preventDefault();
33155 if(this.bgimage.length){
33156 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33157 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33172 * @class Roo.bootstrap.NumberField
33173 * @extends Roo.bootstrap.Input
33174 * Bootstrap NumberField class
33180 * Create a new NumberField
33181 * @param {Object} config The config object
33184 Roo.bootstrap.NumberField = function(config){
33185 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33188 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33191 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33193 allowDecimals : true,
33195 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33197 decimalSeparator : ".",
33199 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33201 decimalPrecision : 2,
33203 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33205 allowNegative : true,
33208 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33212 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33214 minValue : Number.NEGATIVE_INFINITY,
33216 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33218 maxValue : Number.MAX_VALUE,
33220 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33222 minText : "The minimum value for this field is {0}",
33224 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33226 maxText : "The maximum value for this field is {0}",
33228 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33229 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33231 nanText : "{0} is not a valid number",
33233 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33237 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33239 thousandsDelimiter : false,
33241 * @cfg {String} valueAlign alignment of value
33243 valueAlign : "left",
33245 getAutoCreate : function()
33247 var hiddenInput = {
33251 cls: 'hidden-number-input'
33255 hiddenInput.name = this.name;
33260 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33262 this.name = hiddenInput.name;
33264 if(cfg.cn.length > 0) {
33265 cfg.cn.push(hiddenInput);
33272 initEvents : function()
33274 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33276 var allowed = "0123456789";
33278 if(this.allowDecimals){
33279 allowed += this.decimalSeparator;
33282 if(this.allowNegative){
33286 if(this.thousandsDelimiter) {
33290 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33292 var keyPress = function(e){
33294 var k = e.getKey();
33296 var c = e.getCharCode();
33299 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33300 allowed.indexOf(String.fromCharCode(c)) === -1
33306 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33310 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33315 this.el.on("keypress", keyPress, this);
33318 validateValue : function(value)
33321 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33325 var num = this.parseValue(value);
33328 this.markInvalid(String.format(this.nanText, value));
33332 if(num < this.minValue){
33333 this.markInvalid(String.format(this.minText, this.minValue));
33337 if(num > this.maxValue){
33338 this.markInvalid(String.format(this.maxText, this.maxValue));
33345 getValue : function()
33347 var v = this.hiddenEl().getValue();
33349 return this.fixPrecision(this.parseValue(v));
33352 parseValue : function(value)
33354 if(this.thousandsDelimiter) {
33356 r = new RegExp(",", "g");
33357 value = value.replace(r, "");
33360 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33361 return isNaN(value) ? '' : value;
33364 fixPrecision : function(value)
33366 if(this.thousandsDelimiter) {
33368 r = new RegExp(",", "g");
33369 value = value.replace(r, "");
33372 var nan = isNaN(value);
33374 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33375 return nan ? '' : value;
33377 return parseFloat(value).toFixed(this.decimalPrecision);
33380 setValue : function(v)
33382 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33388 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33390 this.inputEl().dom.value = (v == '') ? '' :
33391 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33393 if(!this.allowZero && v === '0') {
33394 this.hiddenEl().dom.value = '';
33395 this.inputEl().dom.value = '';
33402 decimalPrecisionFcn : function(v)
33404 return Math.floor(v);
33407 beforeBlur : function()
33413 var v = this.parseValue(this.getRawValue());
33420 hiddenEl : function()
33422 return this.el.select('input.hidden-number-input',true).first();
33434 * @class Roo.bootstrap.DocumentSlider
33435 * @extends Roo.bootstrap.Component
33436 * Bootstrap DocumentSlider class
33439 * Create a new DocumentViewer
33440 * @param {Object} config The config object
33443 Roo.bootstrap.DocumentSlider = function(config){
33444 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33451 * Fire after initEvent
33452 * @param {Roo.bootstrap.DocumentSlider} this
33457 * Fire after update
33458 * @param {Roo.bootstrap.DocumentSlider} this
33464 * @param {Roo.bootstrap.DocumentSlider} this
33470 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33476 getAutoCreate : function()
33480 cls : 'roo-document-slider',
33484 cls : 'roo-document-slider-header',
33488 cls : 'roo-document-slider-header-title'
33494 cls : 'roo-document-slider-body',
33498 cls : 'roo-document-slider-prev',
33502 cls : 'fa fa-chevron-left'
33508 cls : 'roo-document-slider-thumb',
33512 cls : 'roo-document-slider-image'
33518 cls : 'roo-document-slider-next',
33522 cls : 'fa fa-chevron-right'
33534 initEvents : function()
33536 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33537 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33539 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33540 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33542 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33543 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33545 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33546 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33548 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33549 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33551 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33552 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33554 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33555 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33557 this.thumbEl.on('click', this.onClick, this);
33559 this.prevIndicator.on('click', this.prev, this);
33561 this.nextIndicator.on('click', this.next, this);
33565 initial : function()
33567 if(this.files.length){
33568 this.indicator = 1;
33572 this.fireEvent('initial', this);
33575 update : function()
33577 this.imageEl.attr('src', this.files[this.indicator - 1]);
33579 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33581 this.prevIndicator.show();
33583 if(this.indicator == 1){
33584 this.prevIndicator.hide();
33587 this.nextIndicator.show();
33589 if(this.indicator == this.files.length){
33590 this.nextIndicator.hide();
33593 this.thumbEl.scrollTo('top');
33595 this.fireEvent('update', this);
33598 onClick : function(e)
33600 e.preventDefault();
33602 this.fireEvent('click', this);
33607 e.preventDefault();
33609 this.indicator = Math.max(1, this.indicator - 1);
33616 e.preventDefault();
33618 this.indicator = Math.min(this.files.length, this.indicator + 1);
33632 * @class Roo.bootstrap.RadioSet
33633 * @extends Roo.bootstrap.Input
33634 * Bootstrap RadioSet class
33635 * @cfg {String} indicatorpos (left|right) default left
33636 * @cfg {Boolean} inline (true|false) inline the element (default true)
33637 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33639 * Create a new RadioSet
33640 * @param {Object} config The config object
33643 Roo.bootstrap.RadioSet = function(config){
33645 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33649 Roo.bootstrap.RadioSet.register(this);
33654 * Fires when the element is checked or unchecked.
33655 * @param {Roo.bootstrap.RadioSet} this This radio
33656 * @param {Roo.bootstrap.Radio} item The checked item
33661 * Fires when the element is click.
33662 * @param {Roo.bootstrap.RadioSet} this This radio set
33663 * @param {Roo.bootstrap.Radio} item The checked item
33664 * @param {Roo.EventObject} e The event object
33671 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33679 indicatorpos : 'left',
33681 getAutoCreate : function()
33685 cls : 'roo-radio-set-label',
33689 html : this.fieldLabel
33694 if(this.indicatorpos == 'left'){
33697 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33698 tooltip : 'This field is required'
33703 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33704 tooltip : 'This field is required'
33710 cls : 'roo-radio-set-items'
33713 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33715 if (align === 'left' && this.fieldLabel.length) {
33718 cls : "roo-radio-set-right",
33724 if(this.labelWidth > 12){
33725 label.style = "width: " + this.labelWidth + 'px';
33728 if(this.labelWidth < 13 && this.labelmd == 0){
33729 this.labelmd = this.labelWidth;
33732 if(this.labellg > 0){
33733 label.cls += ' col-lg-' + this.labellg;
33734 items.cls += ' col-lg-' + (12 - this.labellg);
33737 if(this.labelmd > 0){
33738 label.cls += ' col-md-' + this.labelmd;
33739 items.cls += ' col-md-' + (12 - this.labelmd);
33742 if(this.labelsm > 0){
33743 label.cls += ' col-sm-' + this.labelsm;
33744 items.cls += ' col-sm-' + (12 - this.labelsm);
33747 if(this.labelxs > 0){
33748 label.cls += ' col-xs-' + this.labelxs;
33749 items.cls += ' col-xs-' + (12 - this.labelxs);
33755 cls : 'roo-radio-set',
33759 cls : 'roo-radio-set-input',
33762 value : this.value ? this.value : ''
33769 if(this.weight.length){
33770 cfg.cls += ' roo-radio-' + this.weight;
33774 cfg.cls += ' roo-radio-set-inline';
33778 ['xs','sm','md','lg'].map(function(size){
33779 if (settings[size]) {
33780 cfg.cls += ' col-' + size + '-' + settings[size];
33788 initEvents : function()
33790 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33791 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33793 if(!this.fieldLabel.length){
33794 this.labelEl.hide();
33797 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33798 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33800 this.indicator = this.indicatorEl();
33802 if(this.indicator){
33803 this.indicator.addClass('invisible');
33806 this.originalValue = this.getValue();
33810 inputEl: function ()
33812 return this.el.select('.roo-radio-set-input', true).first();
33815 getChildContainer : function()
33817 return this.itemsEl;
33820 register : function(item)
33822 this.radioes.push(item);
33826 validate : function()
33828 if(this.getVisibilityEl().hasClass('hidden')){
33834 Roo.each(this.radioes, function(i){
33843 if(this.allowBlank) {
33847 if(this.disabled || valid){
33852 this.markInvalid();
33857 markValid : function()
33859 if(this.labelEl.isVisible(true)){
33860 this.indicatorEl().removeClass('visible');
33861 this.indicatorEl().addClass('invisible');
33864 this.el.removeClass([this.invalidClass, this.validClass]);
33865 this.el.addClass(this.validClass);
33867 this.fireEvent('valid', this);
33870 markInvalid : function(msg)
33872 if(this.allowBlank || this.disabled){
33876 if(this.labelEl.isVisible(true)){
33877 this.indicatorEl().removeClass('invisible');
33878 this.indicatorEl().addClass('visible');
33881 this.el.removeClass([this.invalidClass, this.validClass]);
33882 this.el.addClass(this.invalidClass);
33884 this.fireEvent('invalid', this, msg);
33888 setValue : function(v, suppressEvent)
33890 if(this.value === v){
33897 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33900 Roo.each(this.radioes, function(i){
33902 i.el.removeClass('checked');
33905 Roo.each(this.radioes, function(i){
33907 if(i.value === v || i.value.toString() === v.toString()){
33909 i.el.addClass('checked');
33911 if(suppressEvent !== true){
33912 this.fireEvent('check', this, i);
33923 clearInvalid : function(){
33925 if(!this.el || this.preventMark){
33929 this.el.removeClass([this.invalidClass]);
33931 this.fireEvent('valid', this);
33936 Roo.apply(Roo.bootstrap.RadioSet, {
33940 register : function(set)
33942 this.groups[set.name] = set;
33945 get: function(name)
33947 if (typeof(this.groups[name]) == 'undefined') {
33951 return this.groups[name] ;
33957 * Ext JS Library 1.1.1
33958 * Copyright(c) 2006-2007, Ext JS, LLC.
33960 * Originally Released Under LGPL - original licence link has changed is not relivant.
33963 * <script type="text/javascript">
33968 * @class Roo.bootstrap.SplitBar
33969 * @extends Roo.util.Observable
33970 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33974 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33975 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33976 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33977 split.minSize = 100;
33978 split.maxSize = 600;
33979 split.animate = true;
33980 split.on('moved', splitterMoved);
33983 * Create a new SplitBar
33984 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33985 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33986 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33987 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33988 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33989 position of the SplitBar).
33991 Roo.bootstrap.SplitBar = function(cfg){
33996 // dragElement : elm
33997 // resizingElement: el,
33999 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34000 // placement : Roo.bootstrap.SplitBar.LEFT ,
34001 // existingProxy ???
34004 this.el = Roo.get(cfg.dragElement, true);
34005 this.el.dom.unselectable = "on";
34007 this.resizingEl = Roo.get(cfg.resizingElement, true);
34011 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34012 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34015 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34018 * The minimum size of the resizing element. (Defaults to 0)
34024 * The maximum size of the resizing element. (Defaults to 2000)
34027 this.maxSize = 2000;
34030 * Whether to animate the transition to the new size
34033 this.animate = false;
34036 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34039 this.useShim = false;
34044 if(!cfg.existingProxy){
34046 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34048 this.proxy = Roo.get(cfg.existingProxy).dom;
34051 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34054 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34057 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34060 this.dragSpecs = {};
34063 * @private The adapter to use to positon and resize elements
34065 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34066 this.adapter.init(this);
34068 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34070 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34071 this.el.addClass("roo-splitbar-h");
34074 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34075 this.el.addClass("roo-splitbar-v");
34081 * Fires when the splitter is moved (alias for {@link #event-moved})
34082 * @param {Roo.bootstrap.SplitBar} this
34083 * @param {Number} newSize the new width or height
34088 * Fires when the splitter is moved
34089 * @param {Roo.bootstrap.SplitBar} this
34090 * @param {Number} newSize the new width or height
34094 * @event beforeresize
34095 * Fires before the splitter is dragged
34096 * @param {Roo.bootstrap.SplitBar} this
34098 "beforeresize" : true,
34100 "beforeapply" : true
34103 Roo.util.Observable.call(this);
34106 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34107 onStartProxyDrag : function(x, y){
34108 this.fireEvent("beforeresize", this);
34110 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34112 o.enableDisplayMode("block");
34113 // all splitbars share the same overlay
34114 Roo.bootstrap.SplitBar.prototype.overlay = o;
34116 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34117 this.overlay.show();
34118 Roo.get(this.proxy).setDisplayed("block");
34119 var size = this.adapter.getElementSize(this);
34120 this.activeMinSize = this.getMinimumSize();;
34121 this.activeMaxSize = this.getMaximumSize();;
34122 var c1 = size - this.activeMinSize;
34123 var c2 = Math.max(this.activeMaxSize - size, 0);
34124 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34125 this.dd.resetConstraints();
34126 this.dd.setXConstraint(
34127 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34128 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34130 this.dd.setYConstraint(0, 0);
34132 this.dd.resetConstraints();
34133 this.dd.setXConstraint(0, 0);
34134 this.dd.setYConstraint(
34135 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34136 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34139 this.dragSpecs.startSize = size;
34140 this.dragSpecs.startPoint = [x, y];
34141 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34145 * @private Called after the drag operation by the DDProxy
34147 onEndProxyDrag : function(e){
34148 Roo.get(this.proxy).setDisplayed(false);
34149 var endPoint = Roo.lib.Event.getXY(e);
34151 this.overlay.hide();
34154 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34155 newSize = this.dragSpecs.startSize +
34156 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34157 endPoint[0] - this.dragSpecs.startPoint[0] :
34158 this.dragSpecs.startPoint[0] - endPoint[0]
34161 newSize = this.dragSpecs.startSize +
34162 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34163 endPoint[1] - this.dragSpecs.startPoint[1] :
34164 this.dragSpecs.startPoint[1] - endPoint[1]
34167 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34168 if(newSize != this.dragSpecs.startSize){
34169 if(this.fireEvent('beforeapply', this, newSize) !== false){
34170 this.adapter.setElementSize(this, newSize);
34171 this.fireEvent("moved", this, newSize);
34172 this.fireEvent("resize", this, newSize);
34178 * Get the adapter this SplitBar uses
34179 * @return The adapter object
34181 getAdapter : function(){
34182 return this.adapter;
34186 * Set the adapter this SplitBar uses
34187 * @param {Object} adapter A SplitBar adapter object
34189 setAdapter : function(adapter){
34190 this.adapter = adapter;
34191 this.adapter.init(this);
34195 * Gets the minimum size for the resizing element
34196 * @return {Number} The minimum size
34198 getMinimumSize : function(){
34199 return this.minSize;
34203 * Sets the minimum size for the resizing element
34204 * @param {Number} minSize The minimum size
34206 setMinimumSize : function(minSize){
34207 this.minSize = minSize;
34211 * Gets the maximum size for the resizing element
34212 * @return {Number} The maximum size
34214 getMaximumSize : function(){
34215 return this.maxSize;
34219 * Sets the maximum size for the resizing element
34220 * @param {Number} maxSize The maximum size
34222 setMaximumSize : function(maxSize){
34223 this.maxSize = maxSize;
34227 * Sets the initialize size for the resizing element
34228 * @param {Number} size The initial size
34230 setCurrentSize : function(size){
34231 var oldAnimate = this.animate;
34232 this.animate = false;
34233 this.adapter.setElementSize(this, size);
34234 this.animate = oldAnimate;
34238 * Destroy this splitbar.
34239 * @param {Boolean} removeEl True to remove the element
34241 destroy : function(removeEl){
34243 this.shim.remove();
34246 this.proxy.parentNode.removeChild(this.proxy);
34254 * @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.
34256 Roo.bootstrap.SplitBar.createProxy = function(dir){
34257 var proxy = new Roo.Element(document.createElement("div"));
34258 proxy.unselectable();
34259 var cls = 'roo-splitbar-proxy';
34260 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34261 document.body.appendChild(proxy.dom);
34266 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34267 * Default Adapter. It assumes the splitter and resizing element are not positioned
34268 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34270 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34273 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34274 // do nothing for now
34275 init : function(s){
34279 * Called before drag operations to get the current size of the resizing element.
34280 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34282 getElementSize : function(s){
34283 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34284 return s.resizingEl.getWidth();
34286 return s.resizingEl.getHeight();
34291 * Called after drag operations to set the size of the resizing element.
34292 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34293 * @param {Number} newSize The new size to set
34294 * @param {Function} onComplete A function to be invoked when resizing is complete
34296 setElementSize : function(s, newSize, onComplete){
34297 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34299 s.resizingEl.setWidth(newSize);
34301 onComplete(s, newSize);
34304 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34309 s.resizingEl.setHeight(newSize);
34311 onComplete(s, newSize);
34314 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34321 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34322 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34323 * Adapter that moves the splitter element to align with the resized sizing element.
34324 * Used with an absolute positioned SplitBar.
34325 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34326 * document.body, make sure you assign an id to the body element.
34328 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34329 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34330 this.container = Roo.get(container);
34333 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34334 init : function(s){
34335 this.basic.init(s);
34338 getElementSize : function(s){
34339 return this.basic.getElementSize(s);
34342 setElementSize : function(s, newSize, onComplete){
34343 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34346 moveSplitter : function(s){
34347 var yes = Roo.bootstrap.SplitBar;
34348 switch(s.placement){
34350 s.el.setX(s.resizingEl.getRight());
34353 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34356 s.el.setY(s.resizingEl.getBottom());
34359 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34366 * Orientation constant - Create a vertical SplitBar
34370 Roo.bootstrap.SplitBar.VERTICAL = 1;
34373 * Orientation constant - Create a horizontal SplitBar
34377 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34380 * Placement constant - The resizing element is to the left of the splitter element
34384 Roo.bootstrap.SplitBar.LEFT = 1;
34387 * Placement constant - The resizing element is to the right of the splitter element
34391 Roo.bootstrap.SplitBar.RIGHT = 2;
34394 * Placement constant - The resizing element is positioned above the splitter element
34398 Roo.bootstrap.SplitBar.TOP = 3;
34401 * Placement constant - The resizing element is positioned under splitter element
34405 Roo.bootstrap.SplitBar.BOTTOM = 4;
34406 Roo.namespace("Roo.bootstrap.layout");/*
34408 * Ext JS Library 1.1.1
34409 * Copyright(c) 2006-2007, Ext JS, LLC.
34411 * Originally Released Under LGPL - original licence link has changed is not relivant.
34414 * <script type="text/javascript">
34418 * @class Roo.bootstrap.layout.Manager
34419 * @extends Roo.bootstrap.Component
34420 * Base class for layout managers.
34422 Roo.bootstrap.layout.Manager = function(config)
34424 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34430 /** false to disable window resize monitoring @type Boolean */
34431 this.monitorWindowResize = true;
34436 * Fires when a layout is performed.
34437 * @param {Roo.LayoutManager} this
34441 * @event regionresized
34442 * Fires when the user resizes a region.
34443 * @param {Roo.LayoutRegion} region The resized region
34444 * @param {Number} newSize The new size (width for east/west, height for north/south)
34446 "regionresized" : true,
34448 * @event regioncollapsed
34449 * Fires when a region is collapsed.
34450 * @param {Roo.LayoutRegion} region The collapsed region
34452 "regioncollapsed" : true,
34454 * @event regionexpanded
34455 * Fires when a region is expanded.
34456 * @param {Roo.LayoutRegion} region The expanded region
34458 "regionexpanded" : true
34460 this.updating = false;
34463 this.el = Roo.get(config.el);
34469 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34474 monitorWindowResize : true,
34480 onRender : function(ct, position)
34483 this.el = Roo.get(ct);
34486 //this.fireEvent('render',this);
34490 initEvents: function()
34494 // ie scrollbar fix
34495 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34496 document.body.scroll = "no";
34497 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34498 this.el.position('relative');
34500 this.id = this.el.id;
34501 this.el.addClass("roo-layout-container");
34502 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34503 if(this.el.dom != document.body ) {
34504 this.el.on('resize', this.layout,this);
34505 this.el.on('show', this.layout,this);
34511 * Returns true if this layout is currently being updated
34512 * @return {Boolean}
34514 isUpdating : function(){
34515 return this.updating;
34519 * Suspend the LayoutManager from doing auto-layouts while
34520 * making multiple add or remove calls
34522 beginUpdate : function(){
34523 this.updating = true;
34527 * Restore auto-layouts and optionally disable the manager from performing a layout
34528 * @param {Boolean} noLayout true to disable a layout update
34530 endUpdate : function(noLayout){
34531 this.updating = false;
34537 layout: function(){
34541 onRegionResized : function(region, newSize){
34542 this.fireEvent("regionresized", region, newSize);
34546 onRegionCollapsed : function(region){
34547 this.fireEvent("regioncollapsed", region);
34550 onRegionExpanded : function(region){
34551 this.fireEvent("regionexpanded", region);
34555 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34556 * performs box-model adjustments.
34557 * @return {Object} The size as an object {width: (the width), height: (the height)}
34559 getViewSize : function()
34562 if(this.el.dom != document.body){
34563 size = this.el.getSize();
34565 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34567 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34568 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34573 * Returns the Element this layout is bound to.
34574 * @return {Roo.Element}
34576 getEl : function(){
34581 * Returns the specified region.
34582 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34583 * @return {Roo.LayoutRegion}
34585 getRegion : function(target){
34586 return this.regions[target.toLowerCase()];
34589 onWindowResize : function(){
34590 if(this.monitorWindowResize){
34597 * Ext JS Library 1.1.1
34598 * Copyright(c) 2006-2007, Ext JS, LLC.
34600 * Originally Released Under LGPL - original licence link has changed is not relivant.
34603 * <script type="text/javascript">
34606 * @class Roo.bootstrap.layout.Border
34607 * @extends Roo.bootstrap.layout.Manager
34608 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34609 * please see: examples/bootstrap/nested.html<br><br>
34611 <b>The container the layout is rendered into can be either the body element or any other element.
34612 If it is not the body element, the container needs to either be an absolute positioned element,
34613 or you will need to add "position:relative" to the css of the container. You will also need to specify
34614 the container size if it is not the body element.</b>
34617 * Create a new Border
34618 * @param {Object} config Configuration options
34620 Roo.bootstrap.layout.Border = function(config){
34621 config = config || {};
34622 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34626 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34627 if(config[region]){
34628 config[region].region = region;
34629 this.addRegion(config[region]);
34635 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34637 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34639 * Creates and adds a new region if it doesn't already exist.
34640 * @param {String} target The target region key (north, south, east, west or center).
34641 * @param {Object} config The regions config object
34642 * @return {BorderLayoutRegion} The new region
34644 addRegion : function(config)
34646 if(!this.regions[config.region]){
34647 var r = this.factory(config);
34648 this.bindRegion(r);
34650 return this.regions[config.region];
34654 bindRegion : function(r){
34655 this.regions[r.config.region] = r;
34657 r.on("visibilitychange", this.layout, this);
34658 r.on("paneladded", this.layout, this);
34659 r.on("panelremoved", this.layout, this);
34660 r.on("invalidated", this.layout, this);
34661 r.on("resized", this.onRegionResized, this);
34662 r.on("collapsed", this.onRegionCollapsed, this);
34663 r.on("expanded", this.onRegionExpanded, this);
34667 * Performs a layout update.
34669 layout : function()
34671 if(this.updating) {
34675 // render all the rebions if they have not been done alreayd?
34676 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34677 if(this.regions[region] && !this.regions[region].bodyEl){
34678 this.regions[region].onRender(this.el)
34682 var size = this.getViewSize();
34683 var w = size.width;
34684 var h = size.height;
34689 //var x = 0, y = 0;
34691 var rs = this.regions;
34692 var north = rs["north"];
34693 var south = rs["south"];
34694 var west = rs["west"];
34695 var east = rs["east"];
34696 var center = rs["center"];
34697 //if(this.hideOnLayout){ // not supported anymore
34698 //c.el.setStyle("display", "none");
34700 if(north && north.isVisible()){
34701 var b = north.getBox();
34702 var m = north.getMargins();
34703 b.width = w - (m.left+m.right);
34706 centerY = b.height + b.y + m.bottom;
34707 centerH -= centerY;
34708 north.updateBox(this.safeBox(b));
34710 if(south && south.isVisible()){
34711 var b = south.getBox();
34712 var m = south.getMargins();
34713 b.width = w - (m.left+m.right);
34715 var totalHeight = (b.height + m.top + m.bottom);
34716 b.y = h - totalHeight + m.top;
34717 centerH -= totalHeight;
34718 south.updateBox(this.safeBox(b));
34720 if(west && west.isVisible()){
34721 var b = west.getBox();
34722 var m = west.getMargins();
34723 b.height = centerH - (m.top+m.bottom);
34725 b.y = centerY + m.top;
34726 var totalWidth = (b.width + m.left + m.right);
34727 centerX += totalWidth;
34728 centerW -= totalWidth;
34729 west.updateBox(this.safeBox(b));
34731 if(east && east.isVisible()){
34732 var b = east.getBox();
34733 var m = east.getMargins();
34734 b.height = centerH - (m.top+m.bottom);
34735 var totalWidth = (b.width + m.left + m.right);
34736 b.x = w - totalWidth + m.left;
34737 b.y = centerY + m.top;
34738 centerW -= totalWidth;
34739 east.updateBox(this.safeBox(b));
34742 var m = center.getMargins();
34744 x: centerX + m.left,
34745 y: centerY + m.top,
34746 width: centerW - (m.left+m.right),
34747 height: centerH - (m.top+m.bottom)
34749 //if(this.hideOnLayout){
34750 //center.el.setStyle("display", "block");
34752 center.updateBox(this.safeBox(centerBox));
34755 this.fireEvent("layout", this);
34759 safeBox : function(box){
34760 box.width = Math.max(0, box.width);
34761 box.height = Math.max(0, box.height);
34766 * Adds a ContentPanel (or subclass) to this layout.
34767 * @param {String} target The target region key (north, south, east, west or center).
34768 * @param {Roo.ContentPanel} panel The panel to add
34769 * @return {Roo.ContentPanel} The added panel
34771 add : function(target, panel){
34773 target = target.toLowerCase();
34774 return this.regions[target].add(panel);
34778 * Remove a ContentPanel (or subclass) to this layout.
34779 * @param {String} target The target region key (north, south, east, west or center).
34780 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34781 * @return {Roo.ContentPanel} The removed panel
34783 remove : function(target, panel){
34784 target = target.toLowerCase();
34785 return this.regions[target].remove(panel);
34789 * Searches all regions for a panel with the specified id
34790 * @param {String} panelId
34791 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34793 findPanel : function(panelId){
34794 var rs = this.regions;
34795 for(var target in rs){
34796 if(typeof rs[target] != "function"){
34797 var p = rs[target].getPanel(panelId);
34807 * Searches all regions for a panel with the specified id and activates (shows) it.
34808 * @param {String/ContentPanel} panelId The panels id or the panel itself
34809 * @return {Roo.ContentPanel} The shown panel or null
34811 showPanel : function(panelId) {
34812 var rs = this.regions;
34813 for(var target in rs){
34814 var r = rs[target];
34815 if(typeof r != "function"){
34816 if(r.hasPanel(panelId)){
34817 return r.showPanel(panelId);
34825 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34826 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34829 restoreState : function(provider){
34831 provider = Roo.state.Manager;
34833 var sm = new Roo.LayoutStateManager();
34834 sm.init(this, provider);
34840 * Adds a xtype elements to the layout.
34844 xtype : 'ContentPanel',
34851 xtype : 'NestedLayoutPanel',
34857 items : [ ... list of content panels or nested layout panels.. ]
34861 * @param {Object} cfg Xtype definition of item to add.
34863 addxtype : function(cfg)
34865 // basically accepts a pannel...
34866 // can accept a layout region..!?!?
34867 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34870 // theory? children can only be panels??
34872 //if (!cfg.xtype.match(/Panel$/)) {
34877 if (typeof(cfg.region) == 'undefined') {
34878 Roo.log("Failed to add Panel, region was not set");
34882 var region = cfg.region;
34888 xitems = cfg.items;
34895 case 'Content': // ContentPanel (el, cfg)
34896 case 'Scroll': // ContentPanel (el, cfg)
34898 cfg.autoCreate = true;
34899 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34901 // var el = this.el.createChild();
34902 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34905 this.add(region, ret);
34909 case 'TreePanel': // our new panel!
34910 cfg.el = this.el.createChild();
34911 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34912 this.add(region, ret);
34917 // create a new Layout (which is a Border Layout...
34919 var clayout = cfg.layout;
34920 clayout.el = this.el.createChild();
34921 clayout.items = clayout.items || [];
34925 // replace this exitems with the clayout ones..
34926 xitems = clayout.items;
34928 // force background off if it's in center...
34929 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34930 cfg.background = false;
34932 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34935 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34936 //console.log('adding nested layout panel ' + cfg.toSource());
34937 this.add(region, ret);
34938 nb = {}; /// find first...
34943 // needs grid and region
34945 //var el = this.getRegion(region).el.createChild();
34947 *var el = this.el.createChild();
34948 // create the grid first...
34949 cfg.grid.container = el;
34950 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34953 if (region == 'center' && this.active ) {
34954 cfg.background = false;
34957 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34959 this.add(region, ret);
34961 if (cfg.background) {
34962 // render grid on panel activation (if panel background)
34963 ret.on('activate', function(gp) {
34964 if (!gp.grid.rendered) {
34965 // gp.grid.render(el);
34969 // cfg.grid.render(el);
34975 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34976 // it was the old xcomponent building that caused this before.
34977 // espeically if border is the top element in the tree.
34987 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34989 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34990 this.add(region, ret);
34994 throw "Can not add '" + cfg.xtype + "' to Border";
35000 this.beginUpdate();
35004 Roo.each(xitems, function(i) {
35005 region = nb && i.region ? i.region : false;
35007 var add = ret.addxtype(i);
35010 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35011 if (!i.background) {
35012 abn[region] = nb[region] ;
35019 // make the last non-background panel active..
35020 //if (nb) { Roo.log(abn); }
35023 for(var r in abn) {
35024 region = this.getRegion(r);
35026 // tried using nb[r], but it does not work..
35028 region.showPanel(abn[r]);
35039 factory : function(cfg)
35042 var validRegions = Roo.bootstrap.layout.Border.regions;
35044 var target = cfg.region;
35047 var r = Roo.bootstrap.layout;
35051 return new r.North(cfg);
35053 return new r.South(cfg);
35055 return new r.East(cfg);
35057 return new r.West(cfg);
35059 return new r.Center(cfg);
35061 throw 'Layout region "'+target+'" not supported.';
35068 * Ext JS Library 1.1.1
35069 * Copyright(c) 2006-2007, Ext JS, LLC.
35071 * Originally Released Under LGPL - original licence link has changed is not relivant.
35074 * <script type="text/javascript">
35078 * @class Roo.bootstrap.layout.Basic
35079 * @extends Roo.util.Observable
35080 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35081 * and does not have a titlebar, tabs or any other features. All it does is size and position
35082 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35083 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35084 * @cfg {string} region the region that it inhabits..
35085 * @cfg {bool} skipConfig skip config?
35089 Roo.bootstrap.layout.Basic = function(config){
35091 this.mgr = config.mgr;
35093 this.position = config.region;
35095 var skipConfig = config.skipConfig;
35099 * @scope Roo.BasicLayoutRegion
35103 * @event beforeremove
35104 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35105 * @param {Roo.LayoutRegion} this
35106 * @param {Roo.ContentPanel} panel The panel
35107 * @param {Object} e The cancel event object
35109 "beforeremove" : true,
35111 * @event invalidated
35112 * Fires when the layout for this region is changed.
35113 * @param {Roo.LayoutRegion} this
35115 "invalidated" : true,
35117 * @event visibilitychange
35118 * Fires when this region is shown or hidden
35119 * @param {Roo.LayoutRegion} this
35120 * @param {Boolean} visibility true or false
35122 "visibilitychange" : true,
35124 * @event paneladded
35125 * Fires when a panel is added.
35126 * @param {Roo.LayoutRegion} this
35127 * @param {Roo.ContentPanel} panel The panel
35129 "paneladded" : true,
35131 * @event panelremoved
35132 * Fires when a panel is removed.
35133 * @param {Roo.LayoutRegion} this
35134 * @param {Roo.ContentPanel} panel The panel
35136 "panelremoved" : true,
35138 * @event beforecollapse
35139 * Fires when this region before collapse.
35140 * @param {Roo.LayoutRegion} this
35142 "beforecollapse" : true,
35145 * Fires when this region is collapsed.
35146 * @param {Roo.LayoutRegion} this
35148 "collapsed" : true,
35151 * Fires when this region is expanded.
35152 * @param {Roo.LayoutRegion} this
35157 * Fires when this region is slid into view.
35158 * @param {Roo.LayoutRegion} this
35160 "slideshow" : true,
35163 * Fires when this region slides out of view.
35164 * @param {Roo.LayoutRegion} this
35166 "slidehide" : true,
35168 * @event panelactivated
35169 * Fires when a panel is activated.
35170 * @param {Roo.LayoutRegion} this
35171 * @param {Roo.ContentPanel} panel The activated panel
35173 "panelactivated" : true,
35176 * Fires when the user resizes this region.
35177 * @param {Roo.LayoutRegion} this
35178 * @param {Number} newSize The new size (width for east/west, height for north/south)
35182 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35183 this.panels = new Roo.util.MixedCollection();
35184 this.panels.getKey = this.getPanelId.createDelegate(this);
35186 this.activePanel = null;
35187 // ensure listeners are added...
35189 if (config.listeners || config.events) {
35190 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35191 listeners : config.listeners || {},
35192 events : config.events || {}
35196 if(skipConfig !== true){
35197 this.applyConfig(config);
35201 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35203 getPanelId : function(p){
35207 applyConfig : function(config){
35208 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35209 this.config = config;
35214 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35215 * the width, for horizontal (north, south) the height.
35216 * @param {Number} newSize The new width or height
35218 resizeTo : function(newSize){
35219 var el = this.el ? this.el :
35220 (this.activePanel ? this.activePanel.getEl() : null);
35222 switch(this.position){
35225 el.setWidth(newSize);
35226 this.fireEvent("resized", this, newSize);
35230 el.setHeight(newSize);
35231 this.fireEvent("resized", this, newSize);
35237 getBox : function(){
35238 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35241 getMargins : function(){
35242 return this.margins;
35245 updateBox : function(box){
35247 var el = this.activePanel.getEl();
35248 el.dom.style.left = box.x + "px";
35249 el.dom.style.top = box.y + "px";
35250 this.activePanel.setSize(box.width, box.height);
35254 * Returns the container element for this region.
35255 * @return {Roo.Element}
35257 getEl : function(){
35258 return this.activePanel;
35262 * Returns true if this region is currently visible.
35263 * @return {Boolean}
35265 isVisible : function(){
35266 return this.activePanel ? true : false;
35269 setActivePanel : function(panel){
35270 panel = this.getPanel(panel);
35271 if(this.activePanel && this.activePanel != panel){
35272 this.activePanel.setActiveState(false);
35273 this.activePanel.getEl().setLeftTop(-10000,-10000);
35275 this.activePanel = panel;
35276 panel.setActiveState(true);
35278 panel.setSize(this.box.width, this.box.height);
35280 this.fireEvent("panelactivated", this, panel);
35281 this.fireEvent("invalidated");
35285 * Show the specified panel.
35286 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35287 * @return {Roo.ContentPanel} The shown panel or null
35289 showPanel : function(panel){
35290 panel = this.getPanel(panel);
35292 this.setActivePanel(panel);
35298 * Get the active panel for this region.
35299 * @return {Roo.ContentPanel} The active panel or null
35301 getActivePanel : function(){
35302 return this.activePanel;
35306 * Add the passed ContentPanel(s)
35307 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35308 * @return {Roo.ContentPanel} The panel added (if only one was added)
35310 add : function(panel){
35311 if(arguments.length > 1){
35312 for(var i = 0, len = arguments.length; i < len; i++) {
35313 this.add(arguments[i]);
35317 if(this.hasPanel(panel)){
35318 this.showPanel(panel);
35321 var el = panel.getEl();
35322 if(el.dom.parentNode != this.mgr.el.dom){
35323 this.mgr.el.dom.appendChild(el.dom);
35325 if(panel.setRegion){
35326 panel.setRegion(this);
35328 this.panels.add(panel);
35329 el.setStyle("position", "absolute");
35330 if(!panel.background){
35331 this.setActivePanel(panel);
35332 if(this.config.initialSize && this.panels.getCount()==1){
35333 this.resizeTo(this.config.initialSize);
35336 this.fireEvent("paneladded", this, panel);
35341 * Returns true if the panel is in this region.
35342 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35343 * @return {Boolean}
35345 hasPanel : function(panel){
35346 if(typeof panel == "object"){ // must be panel obj
35347 panel = panel.getId();
35349 return this.getPanel(panel) ? true : false;
35353 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35354 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35355 * @param {Boolean} preservePanel Overrides the config preservePanel option
35356 * @return {Roo.ContentPanel} The panel that was removed
35358 remove : function(panel, preservePanel){
35359 panel = this.getPanel(panel);
35364 this.fireEvent("beforeremove", this, panel, e);
35365 if(e.cancel === true){
35368 var panelId = panel.getId();
35369 this.panels.removeKey(panelId);
35374 * Returns the panel specified or null if it's not in this region.
35375 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35376 * @return {Roo.ContentPanel}
35378 getPanel : function(id){
35379 if(typeof id == "object"){ // must be panel obj
35382 return this.panels.get(id);
35386 * Returns this regions position (north/south/east/west/center).
35389 getPosition: function(){
35390 return this.position;
35394 * Ext JS Library 1.1.1
35395 * Copyright(c) 2006-2007, Ext JS, LLC.
35397 * Originally Released Under LGPL - original licence link has changed is not relivant.
35400 * <script type="text/javascript">
35404 * @class Roo.bootstrap.layout.Region
35405 * @extends Roo.bootstrap.layout.Basic
35406 * This class represents a region in a layout manager.
35408 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35409 * @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})
35410 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35411 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35412 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35413 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35414 * @cfg {String} title The title for the region (overrides panel titles)
35415 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35416 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35417 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35418 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35419 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35420 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35421 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35422 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35423 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35424 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35426 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35427 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35428 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35429 * @cfg {Number} width For East/West panels
35430 * @cfg {Number} height For North/South panels
35431 * @cfg {Boolean} split To show the splitter
35432 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35434 * @cfg {string} cls Extra CSS classes to add to region
35436 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35437 * @cfg {string} region the region that it inhabits..
35440 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35441 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35443 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35444 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35445 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35447 Roo.bootstrap.layout.Region = function(config)
35449 this.applyConfig(config);
35451 var mgr = config.mgr;
35452 var pos = config.region;
35453 config.skipConfig = true;
35454 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35457 this.onRender(mgr.el);
35460 this.visible = true;
35461 this.collapsed = false;
35462 this.unrendered_panels = [];
35465 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35467 position: '', // set by wrapper (eg. north/south etc..)
35468 unrendered_panels : null, // unrendered panels.
35469 createBody : function(){
35470 /** This region's body element
35471 * @type Roo.Element */
35472 this.bodyEl = this.el.createChild({
35474 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35478 onRender: function(ctr, pos)
35480 var dh = Roo.DomHelper;
35481 /** This region's container element
35482 * @type Roo.Element */
35483 this.el = dh.append(ctr.dom, {
35485 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35487 /** This region's title element
35488 * @type Roo.Element */
35490 this.titleEl = dh.append(this.el.dom,
35493 unselectable: "on",
35494 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35496 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35497 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35500 this.titleEl.enableDisplayMode();
35501 /** This region's title text element
35502 * @type HTMLElement */
35503 this.titleTextEl = this.titleEl.dom.firstChild;
35504 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35506 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35507 this.closeBtn.enableDisplayMode();
35508 this.closeBtn.on("click", this.closeClicked, this);
35509 this.closeBtn.hide();
35511 this.createBody(this.config);
35512 if(this.config.hideWhenEmpty){
35514 this.on("paneladded", this.validateVisibility, this);
35515 this.on("panelremoved", this.validateVisibility, this);
35517 if(this.autoScroll){
35518 this.bodyEl.setStyle("overflow", "auto");
35520 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35522 //if(c.titlebar !== false){
35523 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35524 this.titleEl.hide();
35526 this.titleEl.show();
35527 if(this.config.title){
35528 this.titleTextEl.innerHTML = this.config.title;
35532 if(this.config.collapsed){
35533 this.collapse(true);
35535 if(this.config.hidden){
35539 if (this.unrendered_panels && this.unrendered_panels.length) {
35540 for (var i =0;i< this.unrendered_panels.length; i++) {
35541 this.add(this.unrendered_panels[i]);
35543 this.unrendered_panels = null;
35549 applyConfig : function(c)
35552 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35553 var dh = Roo.DomHelper;
35554 if(c.titlebar !== false){
35555 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35556 this.collapseBtn.on("click", this.collapse, this);
35557 this.collapseBtn.enableDisplayMode();
35559 if(c.showPin === true || this.showPin){
35560 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35561 this.stickBtn.enableDisplayMode();
35562 this.stickBtn.on("click", this.expand, this);
35563 this.stickBtn.hide();
35568 /** This region's collapsed element
35569 * @type Roo.Element */
35572 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35573 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35576 if(c.floatable !== false){
35577 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35578 this.collapsedEl.on("click", this.collapseClick, this);
35581 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35582 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35583 id: "message", unselectable: "on", style:{"float":"left"}});
35584 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35586 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35587 this.expandBtn.on("click", this.expand, this);
35591 if(this.collapseBtn){
35592 this.collapseBtn.setVisible(c.collapsible == true);
35595 this.cmargins = c.cmargins || this.cmargins ||
35596 (this.position == "west" || this.position == "east" ?
35597 {top: 0, left: 2, right:2, bottom: 0} :
35598 {top: 2, left: 0, right:0, bottom: 2});
35600 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35603 this.bottomTabs = c.tabPosition != "top";
35605 this.autoScroll = c.autoScroll || false;
35610 this.duration = c.duration || .30;
35611 this.slideDuration = c.slideDuration || .45;
35616 * Returns true if this region is currently visible.
35617 * @return {Boolean}
35619 isVisible : function(){
35620 return this.visible;
35624 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35625 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35627 //setCollapsedTitle : function(title){
35628 // title = title || " ";
35629 // if(this.collapsedTitleTextEl){
35630 // this.collapsedTitleTextEl.innerHTML = title;
35634 getBox : function(){
35636 // if(!this.collapsed){
35637 b = this.el.getBox(false, true);
35639 // b = this.collapsedEl.getBox(false, true);
35644 getMargins : function(){
35645 return this.margins;
35646 //return this.collapsed ? this.cmargins : this.margins;
35649 highlight : function(){
35650 this.el.addClass("x-layout-panel-dragover");
35653 unhighlight : function(){
35654 this.el.removeClass("x-layout-panel-dragover");
35657 updateBox : function(box)
35659 if (!this.bodyEl) {
35660 return; // not rendered yet..
35664 if(!this.collapsed){
35665 this.el.dom.style.left = box.x + "px";
35666 this.el.dom.style.top = box.y + "px";
35667 this.updateBody(box.width, box.height);
35669 this.collapsedEl.dom.style.left = box.x + "px";
35670 this.collapsedEl.dom.style.top = box.y + "px";
35671 this.collapsedEl.setSize(box.width, box.height);
35674 this.tabs.autoSizeTabs();
35678 updateBody : function(w, h)
35681 this.el.setWidth(w);
35682 w -= this.el.getBorderWidth("rl");
35683 if(this.config.adjustments){
35684 w += this.config.adjustments[0];
35687 if(h !== null && h > 0){
35688 this.el.setHeight(h);
35689 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35690 h -= this.el.getBorderWidth("tb");
35691 if(this.config.adjustments){
35692 h += this.config.adjustments[1];
35694 this.bodyEl.setHeight(h);
35696 h = this.tabs.syncHeight(h);
35699 if(this.panelSize){
35700 w = w !== null ? w : this.panelSize.width;
35701 h = h !== null ? h : this.panelSize.height;
35703 if(this.activePanel){
35704 var el = this.activePanel.getEl();
35705 w = w !== null ? w : el.getWidth();
35706 h = h !== null ? h : el.getHeight();
35707 this.panelSize = {width: w, height: h};
35708 this.activePanel.setSize(w, h);
35710 if(Roo.isIE && this.tabs){
35711 this.tabs.el.repaint();
35716 * Returns the container element for this region.
35717 * @return {Roo.Element}
35719 getEl : function(){
35724 * Hides this region.
35727 //if(!this.collapsed){
35728 this.el.dom.style.left = "-2000px";
35731 // this.collapsedEl.dom.style.left = "-2000px";
35732 // this.collapsedEl.hide();
35734 this.visible = false;
35735 this.fireEvent("visibilitychange", this, false);
35739 * Shows this region if it was previously hidden.
35742 //if(!this.collapsed){
35745 // this.collapsedEl.show();
35747 this.visible = true;
35748 this.fireEvent("visibilitychange", this, true);
35751 closeClicked : function(){
35752 if(this.activePanel){
35753 this.remove(this.activePanel);
35757 collapseClick : function(e){
35759 e.stopPropagation();
35762 e.stopPropagation();
35768 * Collapses this region.
35769 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35772 collapse : function(skipAnim, skipCheck = false){
35773 if(this.collapsed) {
35777 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35779 this.collapsed = true;
35781 this.split.el.hide();
35783 if(this.config.animate && skipAnim !== true){
35784 this.fireEvent("invalidated", this);
35785 this.animateCollapse();
35787 this.el.setLocation(-20000,-20000);
35789 this.collapsedEl.show();
35790 this.fireEvent("collapsed", this);
35791 this.fireEvent("invalidated", this);
35797 animateCollapse : function(){
35802 * Expands this region if it was previously collapsed.
35803 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35804 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35807 expand : function(e, skipAnim){
35809 e.stopPropagation();
35811 if(!this.collapsed || this.el.hasActiveFx()) {
35815 this.afterSlideIn();
35818 this.collapsed = false;
35819 if(this.config.animate && skipAnim !== true){
35820 this.animateExpand();
35824 this.split.el.show();
35826 this.collapsedEl.setLocation(-2000,-2000);
35827 this.collapsedEl.hide();
35828 this.fireEvent("invalidated", this);
35829 this.fireEvent("expanded", this);
35833 animateExpand : function(){
35837 initTabs : function()
35839 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35841 var ts = new Roo.bootstrap.panel.Tabs({
35842 el: this.bodyEl.dom,
35843 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35844 disableTooltips: this.config.disableTabTips,
35845 toolbar : this.config.toolbar
35848 if(this.config.hideTabs){
35849 ts.stripWrap.setDisplayed(false);
35852 ts.resizeTabs = this.config.resizeTabs === true;
35853 ts.minTabWidth = this.config.minTabWidth || 40;
35854 ts.maxTabWidth = this.config.maxTabWidth || 250;
35855 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35856 ts.monitorResize = false;
35857 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35858 ts.bodyEl.addClass('roo-layout-tabs-body');
35859 this.panels.each(this.initPanelAsTab, this);
35862 initPanelAsTab : function(panel){
35863 var ti = this.tabs.addTab(
35867 this.config.closeOnTab && panel.isClosable(),
35870 if(panel.tabTip !== undefined){
35871 ti.setTooltip(panel.tabTip);
35873 ti.on("activate", function(){
35874 this.setActivePanel(panel);
35877 if(this.config.closeOnTab){
35878 ti.on("beforeclose", function(t, e){
35880 this.remove(panel);
35884 panel.tabItem = ti;
35889 updatePanelTitle : function(panel, title)
35891 if(this.activePanel == panel){
35892 this.updateTitle(title);
35895 var ti = this.tabs.getTab(panel.getEl().id);
35897 if(panel.tabTip !== undefined){
35898 ti.setTooltip(panel.tabTip);
35903 updateTitle : function(title){
35904 if(this.titleTextEl && !this.config.title){
35905 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35909 setActivePanel : function(panel)
35911 panel = this.getPanel(panel);
35912 if(this.activePanel && this.activePanel != panel){
35913 if(this.activePanel.setActiveState(false) === false){
35917 this.activePanel = panel;
35918 panel.setActiveState(true);
35919 if(this.panelSize){
35920 panel.setSize(this.panelSize.width, this.panelSize.height);
35923 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35925 this.updateTitle(panel.getTitle());
35927 this.fireEvent("invalidated", this);
35929 this.fireEvent("panelactivated", this, panel);
35933 * Shows the specified panel.
35934 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35935 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35937 showPanel : function(panel)
35939 panel = this.getPanel(panel);
35942 var tab = this.tabs.getTab(panel.getEl().id);
35943 if(tab.isHidden()){
35944 this.tabs.unhideTab(tab.id);
35948 this.setActivePanel(panel);
35955 * Get the active panel for this region.
35956 * @return {Roo.ContentPanel} The active panel or null
35958 getActivePanel : function(){
35959 return this.activePanel;
35962 validateVisibility : function(){
35963 if(this.panels.getCount() < 1){
35964 this.updateTitle(" ");
35965 this.closeBtn.hide();
35968 if(!this.isVisible()){
35975 * Adds the passed ContentPanel(s) to this region.
35976 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35977 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35979 add : function(panel)
35981 if(arguments.length > 1){
35982 for(var i = 0, len = arguments.length; i < len; i++) {
35983 this.add(arguments[i]);
35988 // if we have not been rendered yet, then we can not really do much of this..
35989 if (!this.bodyEl) {
35990 this.unrendered_panels.push(panel);
35997 if(this.hasPanel(panel)){
35998 this.showPanel(panel);
36001 panel.setRegion(this);
36002 this.panels.add(panel);
36003 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36004 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36005 // and hide them... ???
36006 this.bodyEl.dom.appendChild(panel.getEl().dom);
36007 if(panel.background !== true){
36008 this.setActivePanel(panel);
36010 this.fireEvent("paneladded", this, panel);
36017 this.initPanelAsTab(panel);
36021 if(panel.background !== true){
36022 this.tabs.activate(panel.getEl().id);
36024 this.fireEvent("paneladded", this, panel);
36029 * Hides the tab for the specified panel.
36030 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36032 hidePanel : function(panel){
36033 if(this.tabs && (panel = this.getPanel(panel))){
36034 this.tabs.hideTab(panel.getEl().id);
36039 * Unhides the tab for a previously hidden panel.
36040 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36042 unhidePanel : function(panel){
36043 if(this.tabs && (panel = this.getPanel(panel))){
36044 this.tabs.unhideTab(panel.getEl().id);
36048 clearPanels : function(){
36049 while(this.panels.getCount() > 0){
36050 this.remove(this.panels.first());
36055 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36056 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36057 * @param {Boolean} preservePanel Overrides the config preservePanel option
36058 * @return {Roo.ContentPanel} The panel that was removed
36060 remove : function(panel, preservePanel)
36062 panel = this.getPanel(panel);
36067 this.fireEvent("beforeremove", this, panel, e);
36068 if(e.cancel === true){
36071 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36072 var panelId = panel.getId();
36073 this.panels.removeKey(panelId);
36075 document.body.appendChild(panel.getEl().dom);
36078 this.tabs.removeTab(panel.getEl().id);
36079 }else if (!preservePanel){
36080 this.bodyEl.dom.removeChild(panel.getEl().dom);
36082 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36083 var p = this.panels.first();
36084 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36085 tempEl.appendChild(p.getEl().dom);
36086 this.bodyEl.update("");
36087 this.bodyEl.dom.appendChild(p.getEl().dom);
36089 this.updateTitle(p.getTitle());
36091 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36092 this.setActivePanel(p);
36094 panel.setRegion(null);
36095 if(this.activePanel == panel){
36096 this.activePanel = null;
36098 if(this.config.autoDestroy !== false && preservePanel !== true){
36099 try{panel.destroy();}catch(e){}
36101 this.fireEvent("panelremoved", this, panel);
36106 * Returns the TabPanel component used by this region
36107 * @return {Roo.TabPanel}
36109 getTabs : function(){
36113 createTool : function(parentEl, className){
36114 var btn = Roo.DomHelper.append(parentEl, {
36116 cls: "x-layout-tools-button",
36119 cls: "roo-layout-tools-button-inner " + className,
36123 btn.addClassOnOver("roo-layout-tools-button-over");
36128 * Ext JS Library 1.1.1
36129 * Copyright(c) 2006-2007, Ext JS, LLC.
36131 * Originally Released Under LGPL - original licence link has changed is not relivant.
36134 * <script type="text/javascript">
36140 * @class Roo.SplitLayoutRegion
36141 * @extends Roo.LayoutRegion
36142 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36144 Roo.bootstrap.layout.Split = function(config){
36145 this.cursor = config.cursor;
36146 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36149 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36151 splitTip : "Drag to resize.",
36152 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36153 useSplitTips : false,
36155 applyConfig : function(config){
36156 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36159 onRender : function(ctr,pos) {
36161 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36162 if(!this.config.split){
36167 var splitEl = Roo.DomHelper.append(ctr.dom, {
36169 id: this.el.id + "-split",
36170 cls: "roo-layout-split roo-layout-split-"+this.position,
36173 /** The SplitBar for this region
36174 * @type Roo.SplitBar */
36175 // does not exist yet...
36176 Roo.log([this.position, this.orientation]);
36178 this.split = new Roo.bootstrap.SplitBar({
36179 dragElement : splitEl,
36180 resizingElement: this.el,
36181 orientation : this.orientation
36184 this.split.on("moved", this.onSplitMove, this);
36185 this.split.useShim = this.config.useShim === true;
36186 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36187 if(this.useSplitTips){
36188 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36190 //if(config.collapsible){
36191 // this.split.el.on("dblclick", this.collapse, this);
36194 if(typeof this.config.minSize != "undefined"){
36195 this.split.minSize = this.config.minSize;
36197 if(typeof this.config.maxSize != "undefined"){
36198 this.split.maxSize = this.config.maxSize;
36200 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36201 this.hideSplitter();
36206 getHMaxSize : function(){
36207 var cmax = this.config.maxSize || 10000;
36208 var center = this.mgr.getRegion("center");
36209 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36212 getVMaxSize : function(){
36213 var cmax = this.config.maxSize || 10000;
36214 var center = this.mgr.getRegion("center");
36215 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36218 onSplitMove : function(split, newSize){
36219 this.fireEvent("resized", this, newSize);
36223 * Returns the {@link Roo.SplitBar} for this region.
36224 * @return {Roo.SplitBar}
36226 getSplitBar : function(){
36231 this.hideSplitter();
36232 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36235 hideSplitter : function(){
36237 this.split.el.setLocation(-2000,-2000);
36238 this.split.el.hide();
36244 this.split.el.show();
36246 Roo.bootstrap.layout.Split.superclass.show.call(this);
36249 beforeSlide: function(){
36250 if(Roo.isGecko){// firefox overflow auto bug workaround
36251 this.bodyEl.clip();
36253 this.tabs.bodyEl.clip();
36255 if(this.activePanel){
36256 this.activePanel.getEl().clip();
36258 if(this.activePanel.beforeSlide){
36259 this.activePanel.beforeSlide();
36265 afterSlide : function(){
36266 if(Roo.isGecko){// firefox overflow auto bug workaround
36267 this.bodyEl.unclip();
36269 this.tabs.bodyEl.unclip();
36271 if(this.activePanel){
36272 this.activePanel.getEl().unclip();
36273 if(this.activePanel.afterSlide){
36274 this.activePanel.afterSlide();
36280 initAutoHide : function(){
36281 if(this.autoHide !== false){
36282 if(!this.autoHideHd){
36283 var st = new Roo.util.DelayedTask(this.slideIn, this);
36284 this.autoHideHd = {
36285 "mouseout": function(e){
36286 if(!e.within(this.el, true)){
36290 "mouseover" : function(e){
36296 this.el.on(this.autoHideHd);
36300 clearAutoHide : function(){
36301 if(this.autoHide !== false){
36302 this.el.un("mouseout", this.autoHideHd.mouseout);
36303 this.el.un("mouseover", this.autoHideHd.mouseover);
36307 clearMonitor : function(){
36308 Roo.get(document).un("click", this.slideInIf, this);
36311 // these names are backwards but not changed for compat
36312 slideOut : function(){
36313 if(this.isSlid || this.el.hasActiveFx()){
36316 this.isSlid = true;
36317 if(this.collapseBtn){
36318 this.collapseBtn.hide();
36320 this.closeBtnState = this.closeBtn.getStyle('display');
36321 this.closeBtn.hide();
36323 this.stickBtn.show();
36326 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36327 this.beforeSlide();
36328 this.el.setStyle("z-index", 10001);
36329 this.el.slideIn(this.getSlideAnchor(), {
36330 callback: function(){
36332 this.initAutoHide();
36333 Roo.get(document).on("click", this.slideInIf, this);
36334 this.fireEvent("slideshow", this);
36341 afterSlideIn : function(){
36342 this.clearAutoHide();
36343 this.isSlid = false;
36344 this.clearMonitor();
36345 this.el.setStyle("z-index", "");
36346 if(this.collapseBtn){
36347 this.collapseBtn.show();
36349 this.closeBtn.setStyle('display', this.closeBtnState);
36351 this.stickBtn.hide();
36353 this.fireEvent("slidehide", this);
36356 slideIn : function(cb){
36357 if(!this.isSlid || this.el.hasActiveFx()){
36361 this.isSlid = false;
36362 this.beforeSlide();
36363 this.el.slideOut(this.getSlideAnchor(), {
36364 callback: function(){
36365 this.el.setLeftTop(-10000, -10000);
36367 this.afterSlideIn();
36375 slideInIf : function(e){
36376 if(!e.within(this.el)){
36381 animateCollapse : function(){
36382 this.beforeSlide();
36383 this.el.setStyle("z-index", 20000);
36384 var anchor = this.getSlideAnchor();
36385 this.el.slideOut(anchor, {
36386 callback : function(){
36387 this.el.setStyle("z-index", "");
36388 this.collapsedEl.slideIn(anchor, {duration:.3});
36390 this.el.setLocation(-10000,-10000);
36392 this.fireEvent("collapsed", this);
36399 animateExpand : function(){
36400 this.beforeSlide();
36401 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36402 this.el.setStyle("z-index", 20000);
36403 this.collapsedEl.hide({
36406 this.el.slideIn(this.getSlideAnchor(), {
36407 callback : function(){
36408 this.el.setStyle("z-index", "");
36411 this.split.el.show();
36413 this.fireEvent("invalidated", this);
36414 this.fireEvent("expanded", this);
36442 getAnchor : function(){
36443 return this.anchors[this.position];
36446 getCollapseAnchor : function(){
36447 return this.canchors[this.position];
36450 getSlideAnchor : function(){
36451 return this.sanchors[this.position];
36454 getAlignAdj : function(){
36455 var cm = this.cmargins;
36456 switch(this.position){
36472 getExpandAdj : function(){
36473 var c = this.collapsedEl, cm = this.cmargins;
36474 switch(this.position){
36476 return [-(cm.right+c.getWidth()+cm.left), 0];
36479 return [cm.right+c.getWidth()+cm.left, 0];
36482 return [0, -(cm.top+cm.bottom+c.getHeight())];
36485 return [0, cm.top+cm.bottom+c.getHeight()];
36491 * Ext JS Library 1.1.1
36492 * Copyright(c) 2006-2007, Ext JS, LLC.
36494 * Originally Released Under LGPL - original licence link has changed is not relivant.
36497 * <script type="text/javascript">
36500 * These classes are private internal classes
36502 Roo.bootstrap.layout.Center = function(config){
36503 config.region = "center";
36504 Roo.bootstrap.layout.Region.call(this, config);
36505 this.visible = true;
36506 this.minWidth = config.minWidth || 20;
36507 this.minHeight = config.minHeight || 20;
36510 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36512 // center panel can't be hidden
36516 // center panel can't be hidden
36519 getMinWidth: function(){
36520 return this.minWidth;
36523 getMinHeight: function(){
36524 return this.minHeight;
36537 Roo.bootstrap.layout.North = function(config)
36539 config.region = 'north';
36540 config.cursor = 'n-resize';
36542 Roo.bootstrap.layout.Split.call(this, config);
36546 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36547 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36548 this.split.el.addClass("roo-layout-split-v");
36550 var size = config.initialSize || config.height;
36551 if(typeof size != "undefined"){
36552 this.el.setHeight(size);
36555 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36557 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36561 getBox : function(){
36562 if(this.collapsed){
36563 return this.collapsedEl.getBox();
36565 var box = this.el.getBox();
36567 box.height += this.split.el.getHeight();
36572 updateBox : function(box){
36573 if(this.split && !this.collapsed){
36574 box.height -= this.split.el.getHeight();
36575 this.split.el.setLeft(box.x);
36576 this.split.el.setTop(box.y+box.height);
36577 this.split.el.setWidth(box.width);
36579 if(this.collapsed){
36580 this.updateBody(box.width, null);
36582 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36590 Roo.bootstrap.layout.South = function(config){
36591 config.region = 'south';
36592 config.cursor = 's-resize';
36593 Roo.bootstrap.layout.Split.call(this, config);
36595 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36596 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36597 this.split.el.addClass("roo-layout-split-v");
36599 var size = config.initialSize || config.height;
36600 if(typeof size != "undefined"){
36601 this.el.setHeight(size);
36605 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36606 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36607 getBox : function(){
36608 if(this.collapsed){
36609 return this.collapsedEl.getBox();
36611 var box = this.el.getBox();
36613 var sh = this.split.el.getHeight();
36620 updateBox : function(box){
36621 if(this.split && !this.collapsed){
36622 var sh = this.split.el.getHeight();
36625 this.split.el.setLeft(box.x);
36626 this.split.el.setTop(box.y-sh);
36627 this.split.el.setWidth(box.width);
36629 if(this.collapsed){
36630 this.updateBody(box.width, null);
36632 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36636 Roo.bootstrap.layout.East = function(config){
36637 config.region = "east";
36638 config.cursor = "e-resize";
36639 Roo.bootstrap.layout.Split.call(this, config);
36641 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36642 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36643 this.split.el.addClass("roo-layout-split-h");
36645 var size = config.initialSize || config.width;
36646 if(typeof size != "undefined"){
36647 this.el.setWidth(size);
36650 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36651 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36652 getBox : function(){
36653 if(this.collapsed){
36654 return this.collapsedEl.getBox();
36656 var box = this.el.getBox();
36658 var sw = this.split.el.getWidth();
36665 updateBox : function(box){
36666 if(this.split && !this.collapsed){
36667 var sw = this.split.el.getWidth();
36669 this.split.el.setLeft(box.x);
36670 this.split.el.setTop(box.y);
36671 this.split.el.setHeight(box.height);
36674 if(this.collapsed){
36675 this.updateBody(null, box.height);
36677 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36681 Roo.bootstrap.layout.West = function(config){
36682 config.region = "west";
36683 config.cursor = "w-resize";
36685 Roo.bootstrap.layout.Split.call(this, config);
36687 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36688 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36689 this.split.el.addClass("roo-layout-split-h");
36693 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36694 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36696 onRender: function(ctr, pos)
36698 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36699 var size = this.config.initialSize || this.config.width;
36700 if(typeof size != "undefined"){
36701 this.el.setWidth(size);
36705 getBox : function(){
36706 if(this.collapsed){
36707 return this.collapsedEl.getBox();
36709 var box = this.el.getBox();
36711 box.width += this.split.el.getWidth();
36716 updateBox : function(box){
36717 if(this.split && !this.collapsed){
36718 var sw = this.split.el.getWidth();
36720 this.split.el.setLeft(box.x+box.width);
36721 this.split.el.setTop(box.y);
36722 this.split.el.setHeight(box.height);
36724 if(this.collapsed){
36725 this.updateBody(null, box.height);
36727 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36730 Roo.namespace("Roo.bootstrap.panel");/*
36732 * Ext JS Library 1.1.1
36733 * Copyright(c) 2006-2007, Ext JS, LLC.
36735 * Originally Released Under LGPL - original licence link has changed is not relivant.
36738 * <script type="text/javascript">
36741 * @class Roo.ContentPanel
36742 * @extends Roo.util.Observable
36743 * A basic ContentPanel element.
36744 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36745 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36746 * @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
36747 * @cfg {Boolean} closable True if the panel can be closed/removed
36748 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36749 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36750 * @cfg {Toolbar} toolbar A toolbar for this panel
36751 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36752 * @cfg {String} title The title for this panel
36753 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36754 * @cfg {String} url Calls {@link #setUrl} with this value
36755 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36756 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36757 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36758 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36759 * @cfg {Boolean} badges render the badges
36762 * Create a new ContentPanel.
36763 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36764 * @param {String/Object} config A string to set only the title or a config object
36765 * @param {String} content (optional) Set the HTML content for this panel
36766 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36768 Roo.bootstrap.panel.Content = function( config){
36770 this.tpl = config.tpl || false;
36772 var el = config.el;
36773 var content = config.content;
36775 if(config.autoCreate){ // xtype is available if this is called from factory
36778 this.el = Roo.get(el);
36779 if(!this.el && config && config.autoCreate){
36780 if(typeof config.autoCreate == "object"){
36781 if(!config.autoCreate.id){
36782 config.autoCreate.id = config.id||el;
36784 this.el = Roo.DomHelper.append(document.body,
36785 config.autoCreate, true);
36787 var elcfg = { tag: "div",
36788 cls: "roo-layout-inactive-content",
36792 elcfg.html = config.html;
36796 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36799 this.closable = false;
36800 this.loaded = false;
36801 this.active = false;
36804 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36806 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36808 this.wrapEl = this.el; //this.el.wrap();
36810 if (config.toolbar.items) {
36811 ti = config.toolbar.items ;
36812 delete config.toolbar.items ;
36816 this.toolbar.render(this.wrapEl, 'before');
36817 for(var i =0;i < ti.length;i++) {
36818 // Roo.log(['add child', items[i]]);
36819 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36821 this.toolbar.items = nitems;
36822 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36823 delete config.toolbar;
36827 // xtype created footer. - not sure if will work as we normally have to render first..
36828 if (this.footer && !this.footer.el && this.footer.xtype) {
36829 if (!this.wrapEl) {
36830 this.wrapEl = this.el.wrap();
36833 this.footer.container = this.wrapEl.createChild();
36835 this.footer = Roo.factory(this.footer, Roo);
36840 if(typeof config == "string"){
36841 this.title = config;
36843 Roo.apply(this, config);
36847 this.resizeEl = Roo.get(this.resizeEl, true);
36849 this.resizeEl = this.el;
36851 // handle view.xtype
36859 * Fires when this panel is activated.
36860 * @param {Roo.ContentPanel} this
36864 * @event deactivate
36865 * Fires when this panel is activated.
36866 * @param {Roo.ContentPanel} this
36868 "deactivate" : true,
36872 * Fires when this panel is resized if fitToFrame is true.
36873 * @param {Roo.ContentPanel} this
36874 * @param {Number} width The width after any component adjustments
36875 * @param {Number} height The height after any component adjustments
36881 * Fires when this tab is created
36882 * @param {Roo.ContentPanel} this
36893 if(this.autoScroll){
36894 this.resizeEl.setStyle("overflow", "auto");
36896 // fix randome scrolling
36897 //this.el.on('scroll', function() {
36898 // Roo.log('fix random scolling');
36899 // this.scrollTo('top',0);
36902 content = content || this.content;
36904 this.setContent(content);
36906 if(config && config.url){
36907 this.setUrl(this.url, this.params, this.loadOnce);
36912 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36914 if (this.view && typeof(this.view.xtype) != 'undefined') {
36915 this.view.el = this.el.appendChild(document.createElement("div"));
36916 this.view = Roo.factory(this.view);
36917 this.view.render && this.view.render(false, '');
36921 this.fireEvent('render', this);
36924 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36928 setRegion : function(region){
36929 this.region = region;
36930 this.setActiveClass(region && !this.background);
36934 setActiveClass: function(state)
36937 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36938 this.el.setStyle('position','relative');
36940 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36941 this.el.setStyle('position', 'absolute');
36946 * Returns the toolbar for this Panel if one was configured.
36947 * @return {Roo.Toolbar}
36949 getToolbar : function(){
36950 return this.toolbar;
36953 setActiveState : function(active)
36955 this.active = active;
36956 this.setActiveClass(active);
36958 if(this.fireEvent("deactivate", this) === false){
36963 this.fireEvent("activate", this);
36967 * Updates this panel's element
36968 * @param {String} content The new content
36969 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36971 setContent : function(content, loadScripts){
36972 this.el.update(content, loadScripts);
36975 ignoreResize : function(w, h){
36976 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36979 this.lastSize = {width: w, height: h};
36984 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36985 * @return {Roo.UpdateManager} The UpdateManager
36987 getUpdateManager : function(){
36988 return this.el.getUpdateManager();
36991 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36992 * @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:
36995 url: "your-url.php",
36996 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36997 callback: yourFunction,
36998 scope: yourObject, //(optional scope)
37001 text: "Loading...",
37006 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37007 * 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.
37008 * @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}
37009 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37010 * @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.
37011 * @return {Roo.ContentPanel} this
37014 var um = this.el.getUpdateManager();
37015 um.update.apply(um, arguments);
37021 * 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.
37022 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37023 * @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)
37024 * @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)
37025 * @return {Roo.UpdateManager} The UpdateManager
37027 setUrl : function(url, params, loadOnce){
37028 if(this.refreshDelegate){
37029 this.removeListener("activate", this.refreshDelegate);
37031 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37032 this.on("activate", this.refreshDelegate);
37033 return this.el.getUpdateManager();
37036 _handleRefresh : function(url, params, loadOnce){
37037 if(!loadOnce || !this.loaded){
37038 var updater = this.el.getUpdateManager();
37039 updater.update(url, params, this._setLoaded.createDelegate(this));
37043 _setLoaded : function(){
37044 this.loaded = true;
37048 * Returns this panel's id
37051 getId : function(){
37056 * Returns this panel's element - used by regiosn to add.
37057 * @return {Roo.Element}
37059 getEl : function(){
37060 return this.wrapEl || this.el;
37065 adjustForComponents : function(width, height)
37067 //Roo.log('adjustForComponents ');
37068 if(this.resizeEl != this.el){
37069 width -= this.el.getFrameWidth('lr');
37070 height -= this.el.getFrameWidth('tb');
37073 var te = this.toolbar.getEl();
37074 te.setWidth(width);
37075 height -= te.getHeight();
37078 var te = this.footer.getEl();
37079 te.setWidth(width);
37080 height -= te.getHeight();
37084 if(this.adjustments){
37085 width += this.adjustments[0];
37086 height += this.adjustments[1];
37088 return {"width": width, "height": height};
37091 setSize : function(width, height){
37092 if(this.fitToFrame && !this.ignoreResize(width, height)){
37093 if(this.fitContainer && this.resizeEl != this.el){
37094 this.el.setSize(width, height);
37096 var size = this.adjustForComponents(width, height);
37097 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37098 this.fireEvent('resize', this, size.width, size.height);
37103 * Returns this panel's title
37106 getTitle : function(){
37108 if (typeof(this.title) != 'object') {
37113 for (var k in this.title) {
37114 if (!this.title.hasOwnProperty(k)) {
37118 if (k.indexOf('-') >= 0) {
37119 var s = k.split('-');
37120 for (var i = 0; i<s.length; i++) {
37121 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37124 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37131 * Set this panel's title
37132 * @param {String} title
37134 setTitle : function(title){
37135 this.title = title;
37137 this.region.updatePanelTitle(this, title);
37142 * Returns true is this panel was configured to be closable
37143 * @return {Boolean}
37145 isClosable : function(){
37146 return this.closable;
37149 beforeSlide : function(){
37151 this.resizeEl.clip();
37154 afterSlide : function(){
37156 this.resizeEl.unclip();
37160 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37161 * Will fail silently if the {@link #setUrl} method has not been called.
37162 * This does not activate the panel, just updates its content.
37164 refresh : function(){
37165 if(this.refreshDelegate){
37166 this.loaded = false;
37167 this.refreshDelegate();
37172 * Destroys this panel
37174 destroy : function(){
37175 this.el.removeAllListeners();
37176 var tempEl = document.createElement("span");
37177 tempEl.appendChild(this.el.dom);
37178 tempEl.innerHTML = "";
37184 * form - if the content panel contains a form - this is a reference to it.
37185 * @type {Roo.form.Form}
37189 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37190 * This contains a reference to it.
37196 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37206 * @param {Object} cfg Xtype definition of item to add.
37210 getChildContainer: function () {
37211 return this.getEl();
37216 var ret = new Roo.factory(cfg);
37221 if (cfg.xtype.match(/^Form$/)) {
37224 //if (this.footer) {
37225 // el = this.footer.container.insertSibling(false, 'before');
37227 el = this.el.createChild();
37230 this.form = new Roo.form.Form(cfg);
37233 if ( this.form.allItems.length) {
37234 this.form.render(el.dom);
37238 // should only have one of theses..
37239 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37240 // views.. should not be just added - used named prop 'view''
37242 cfg.el = this.el.appendChild(document.createElement("div"));
37245 var ret = new Roo.factory(cfg);
37247 ret.render && ret.render(false, ''); // render blank..
37257 * @class Roo.bootstrap.panel.Grid
37258 * @extends Roo.bootstrap.panel.Content
37260 * Create a new GridPanel.
37261 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37262 * @param {Object} config A the config object
37268 Roo.bootstrap.panel.Grid = function(config)
37272 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37273 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37275 config.el = this.wrapper;
37276 //this.el = this.wrapper;
37278 if (config.container) {
37279 // ctor'ed from a Border/panel.grid
37282 this.wrapper.setStyle("overflow", "hidden");
37283 this.wrapper.addClass('roo-grid-container');
37288 if(config.toolbar){
37289 var tool_el = this.wrapper.createChild();
37290 this.toolbar = Roo.factory(config.toolbar);
37292 if (config.toolbar.items) {
37293 ti = config.toolbar.items ;
37294 delete config.toolbar.items ;
37298 this.toolbar.render(tool_el);
37299 for(var i =0;i < ti.length;i++) {
37300 // Roo.log(['add child', items[i]]);
37301 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37303 this.toolbar.items = nitems;
37305 delete config.toolbar;
37308 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37309 config.grid.scrollBody = true;;
37310 config.grid.monitorWindowResize = false; // turn off autosizing
37311 config.grid.autoHeight = false;
37312 config.grid.autoWidth = false;
37314 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37316 if (config.background) {
37317 // render grid on panel activation (if panel background)
37318 this.on('activate', function(gp) {
37319 if (!gp.grid.rendered) {
37320 gp.grid.render(this.wrapper);
37321 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37326 this.grid.render(this.wrapper);
37327 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37330 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37331 // ??? needed ??? config.el = this.wrapper;
37336 // xtype created footer. - not sure if will work as we normally have to render first..
37337 if (this.footer && !this.footer.el && this.footer.xtype) {
37339 var ctr = this.grid.getView().getFooterPanel(true);
37340 this.footer.dataSource = this.grid.dataSource;
37341 this.footer = Roo.factory(this.footer, Roo);
37342 this.footer.render(ctr);
37352 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37353 getId : function(){
37354 return this.grid.id;
37358 * Returns the grid for this panel
37359 * @return {Roo.bootstrap.Table}
37361 getGrid : function(){
37365 setSize : function(width, height){
37366 if(!this.ignoreResize(width, height)){
37367 var grid = this.grid;
37368 var size = this.adjustForComponents(width, height);
37369 var gridel = grid.getGridEl();
37370 gridel.setSize(size.width, size.height);
37372 var thd = grid.getGridEl().select('thead',true).first();
37373 var tbd = grid.getGridEl().select('tbody', true).first();
37375 tbd.setSize(width, height - thd.getHeight());
37384 beforeSlide : function(){
37385 this.grid.getView().scroller.clip();
37388 afterSlide : function(){
37389 this.grid.getView().scroller.unclip();
37392 destroy : function(){
37393 this.grid.destroy();
37395 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37400 * @class Roo.bootstrap.panel.Nest
37401 * @extends Roo.bootstrap.panel.Content
37403 * Create a new Panel, that can contain a layout.Border.
37406 * @param {Roo.BorderLayout} layout The layout for this panel
37407 * @param {String/Object} config A string to set only the title or a config object
37409 Roo.bootstrap.panel.Nest = function(config)
37411 // construct with only one argument..
37412 /* FIXME - implement nicer consturctors
37413 if (layout.layout) {
37415 layout = config.layout;
37416 delete config.layout;
37418 if (layout.xtype && !layout.getEl) {
37419 // then layout needs constructing..
37420 layout = Roo.factory(layout, Roo);
37424 config.el = config.layout.getEl();
37426 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37428 config.layout.monitorWindowResize = false; // turn off autosizing
37429 this.layout = config.layout;
37430 this.layout.getEl().addClass("roo-layout-nested-layout");
37437 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37439 setSize : function(width, height){
37440 if(!this.ignoreResize(width, height)){
37441 var size = this.adjustForComponents(width, height);
37442 var el = this.layout.getEl();
37443 if (size.height < 1) {
37444 el.setWidth(size.width);
37446 el.setSize(size.width, size.height);
37448 var touch = el.dom.offsetWidth;
37449 this.layout.layout();
37450 // ie requires a double layout on the first pass
37451 if(Roo.isIE && !this.initialized){
37452 this.initialized = true;
37453 this.layout.layout();
37458 // activate all subpanels if not currently active..
37460 setActiveState : function(active){
37461 this.active = active;
37462 this.setActiveClass(active);
37465 this.fireEvent("deactivate", this);
37469 this.fireEvent("activate", this);
37470 // not sure if this should happen before or after..
37471 if (!this.layout) {
37472 return; // should not happen..
37475 for (var r in this.layout.regions) {
37476 reg = this.layout.getRegion(r);
37477 if (reg.getActivePanel()) {
37478 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37479 reg.setActivePanel(reg.getActivePanel());
37482 if (!reg.panels.length) {
37485 reg.showPanel(reg.getPanel(0));
37494 * Returns the nested BorderLayout for this panel
37495 * @return {Roo.BorderLayout}
37497 getLayout : function(){
37498 return this.layout;
37502 * Adds a xtype elements to the layout of the nested panel
37506 xtype : 'ContentPanel',
37513 xtype : 'NestedLayoutPanel',
37519 items : [ ... list of content panels or nested layout panels.. ]
37523 * @param {Object} cfg Xtype definition of item to add.
37525 addxtype : function(cfg) {
37526 return this.layout.addxtype(cfg);
37531 * Ext JS Library 1.1.1
37532 * Copyright(c) 2006-2007, Ext JS, LLC.
37534 * Originally Released Under LGPL - original licence link has changed is not relivant.
37537 * <script type="text/javascript">
37540 * @class Roo.TabPanel
37541 * @extends Roo.util.Observable
37542 * A lightweight tab container.
37546 // basic tabs 1, built from existing content
37547 var tabs = new Roo.TabPanel("tabs1");
37548 tabs.addTab("script", "View Script");
37549 tabs.addTab("markup", "View Markup");
37550 tabs.activate("script");
37552 // more advanced tabs, built from javascript
37553 var jtabs = new Roo.TabPanel("jtabs");
37554 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37556 // set up the UpdateManager
37557 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37558 var updater = tab2.getUpdateManager();
37559 updater.setDefaultUrl("ajax1.htm");
37560 tab2.on('activate', updater.refresh, updater, true);
37562 // Use setUrl for Ajax loading
37563 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37564 tab3.setUrl("ajax2.htm", null, true);
37567 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37570 jtabs.activate("jtabs-1");
37573 * Create a new TabPanel.
37574 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37575 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37577 Roo.bootstrap.panel.Tabs = function(config){
37579 * The container element for this TabPanel.
37580 * @type Roo.Element
37582 this.el = Roo.get(config.el);
37585 if(typeof config == "boolean"){
37586 this.tabPosition = config ? "bottom" : "top";
37588 Roo.apply(this, config);
37592 if(this.tabPosition == "bottom"){
37593 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37594 this.el.addClass("roo-tabs-bottom");
37596 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37597 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37598 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37600 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37602 if(this.tabPosition != "bottom"){
37603 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37604 * @type Roo.Element
37606 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37607 this.el.addClass("roo-tabs-top");
37611 this.bodyEl.setStyle("position", "relative");
37613 this.active = null;
37614 this.activateDelegate = this.activate.createDelegate(this);
37619 * Fires when the active tab changes
37620 * @param {Roo.TabPanel} this
37621 * @param {Roo.TabPanelItem} activePanel The new active tab
37625 * @event beforetabchange
37626 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37627 * @param {Roo.TabPanel} this
37628 * @param {Object} e Set cancel to true on this object to cancel the tab change
37629 * @param {Roo.TabPanelItem} tab The tab being changed to
37631 "beforetabchange" : true
37634 Roo.EventManager.onWindowResize(this.onResize, this);
37635 this.cpad = this.el.getPadding("lr");
37636 this.hiddenCount = 0;
37639 // toolbar on the tabbar support...
37640 if (this.toolbar) {
37641 alert("no toolbar support yet");
37642 this.toolbar = false;
37644 var tcfg = this.toolbar;
37645 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37646 this.toolbar = new Roo.Toolbar(tcfg);
37647 if (Roo.isSafari) {
37648 var tbl = tcfg.container.child('table', true);
37649 tbl.setAttribute('width', '100%');
37657 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37660 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37662 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37664 tabPosition : "top",
37666 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37668 currentTabWidth : 0,
37670 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37674 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37678 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37680 preferredTabWidth : 175,
37682 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37684 resizeTabs : false,
37686 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37688 monitorResize : true,
37690 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37695 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37696 * @param {String} id The id of the div to use <b>or create</b>
37697 * @param {String} text The text for the tab
37698 * @param {String} content (optional) Content to put in the TabPanelItem body
37699 * @param {Boolean} closable (optional) True to create a close icon on the tab
37700 * @return {Roo.TabPanelItem} The created TabPanelItem
37702 addTab : function(id, text, content, closable, tpl)
37704 var item = new Roo.bootstrap.panel.TabItem({
37708 closable : closable,
37711 this.addTabItem(item);
37713 item.setContent(content);
37719 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37720 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37721 * @return {Roo.TabPanelItem}
37723 getTab : function(id){
37724 return this.items[id];
37728 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37729 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37731 hideTab : function(id){
37732 var t = this.items[id];
37735 this.hiddenCount++;
37736 this.autoSizeTabs();
37741 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37742 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37744 unhideTab : function(id){
37745 var t = this.items[id];
37747 t.setHidden(false);
37748 this.hiddenCount--;
37749 this.autoSizeTabs();
37754 * Adds an existing {@link Roo.TabPanelItem}.
37755 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37757 addTabItem : function(item){
37758 this.items[item.id] = item;
37759 this.items.push(item);
37760 // if(this.resizeTabs){
37761 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37762 // this.autoSizeTabs();
37764 // item.autoSize();
37769 * Removes a {@link Roo.TabPanelItem}.
37770 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37772 removeTab : function(id){
37773 var items = this.items;
37774 var tab = items[id];
37775 if(!tab) { return; }
37776 var index = items.indexOf(tab);
37777 if(this.active == tab && items.length > 1){
37778 var newTab = this.getNextAvailable(index);
37783 this.stripEl.dom.removeChild(tab.pnode.dom);
37784 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37785 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37787 items.splice(index, 1);
37788 delete this.items[tab.id];
37789 tab.fireEvent("close", tab);
37790 tab.purgeListeners();
37791 this.autoSizeTabs();
37794 getNextAvailable : function(start){
37795 var items = this.items;
37797 // look for a next tab that will slide over to
37798 // replace the one being removed
37799 while(index < items.length){
37800 var item = items[++index];
37801 if(item && !item.isHidden()){
37805 // if one isn't found select the previous tab (on the left)
37808 var item = items[--index];
37809 if(item && !item.isHidden()){
37817 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37818 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37820 disableTab : function(id){
37821 var tab = this.items[id];
37822 if(tab && this.active != tab){
37828 * Enables a {@link Roo.TabPanelItem} that is disabled.
37829 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37831 enableTab : function(id){
37832 var tab = this.items[id];
37837 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37838 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37839 * @return {Roo.TabPanelItem} The TabPanelItem.
37841 activate : function(id){
37842 var tab = this.items[id];
37846 if(tab == this.active || tab.disabled){
37850 this.fireEvent("beforetabchange", this, e, tab);
37851 if(e.cancel !== true && !tab.disabled){
37853 this.active.hide();
37855 this.active = this.items[id];
37856 this.active.show();
37857 this.fireEvent("tabchange", this, this.active);
37863 * Gets the active {@link Roo.TabPanelItem}.
37864 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37866 getActiveTab : function(){
37867 return this.active;
37871 * Updates the tab body element to fit the height of the container element
37872 * for overflow scrolling
37873 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37875 syncHeight : function(targetHeight){
37876 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37877 var bm = this.bodyEl.getMargins();
37878 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37879 this.bodyEl.setHeight(newHeight);
37883 onResize : function(){
37884 if(this.monitorResize){
37885 this.autoSizeTabs();
37890 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37892 beginUpdate : function(){
37893 this.updating = true;
37897 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37899 endUpdate : function(){
37900 this.updating = false;
37901 this.autoSizeTabs();
37905 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37907 autoSizeTabs : function(){
37908 var count = this.items.length;
37909 var vcount = count - this.hiddenCount;
37910 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37913 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37914 var availWidth = Math.floor(w / vcount);
37915 var b = this.stripBody;
37916 if(b.getWidth() > w){
37917 var tabs = this.items;
37918 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37919 if(availWidth < this.minTabWidth){
37920 /*if(!this.sleft){ // incomplete scrolling code
37921 this.createScrollButtons();
37924 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37927 if(this.currentTabWidth < this.preferredTabWidth){
37928 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37934 * Returns the number of tabs in this TabPanel.
37937 getCount : function(){
37938 return this.items.length;
37942 * Resizes all the tabs to the passed width
37943 * @param {Number} The new width
37945 setTabWidth : function(width){
37946 this.currentTabWidth = width;
37947 for(var i = 0, len = this.items.length; i < len; i++) {
37948 if(!this.items[i].isHidden()) {
37949 this.items[i].setWidth(width);
37955 * Destroys this TabPanel
37956 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37958 destroy : function(removeEl){
37959 Roo.EventManager.removeResizeListener(this.onResize, this);
37960 for(var i = 0, len = this.items.length; i < len; i++){
37961 this.items[i].purgeListeners();
37963 if(removeEl === true){
37964 this.el.update("");
37969 createStrip : function(container)
37971 var strip = document.createElement("nav");
37972 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37973 container.appendChild(strip);
37977 createStripList : function(strip)
37979 // div wrapper for retard IE
37980 // returns the "tr" element.
37981 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37982 //'<div class="x-tabs-strip-wrap">'+
37983 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37984 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37985 return strip.firstChild; //.firstChild.firstChild.firstChild;
37987 createBody : function(container)
37989 var body = document.createElement("div");
37990 Roo.id(body, "tab-body");
37991 //Roo.fly(body).addClass("x-tabs-body");
37992 Roo.fly(body).addClass("tab-content");
37993 container.appendChild(body);
37996 createItemBody :function(bodyEl, id){
37997 var body = Roo.getDom(id);
37999 body = document.createElement("div");
38002 //Roo.fly(body).addClass("x-tabs-item-body");
38003 Roo.fly(body).addClass("tab-pane");
38004 bodyEl.insertBefore(body, bodyEl.firstChild);
38008 createStripElements : function(stripEl, text, closable, tpl)
38010 var td = document.createElement("li"); // was td..
38013 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38016 stripEl.appendChild(td);
38018 td.className = "x-tabs-closable";
38019 if(!this.closeTpl){
38020 this.closeTpl = new Roo.Template(
38021 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38022 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38023 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38026 var el = this.closeTpl.overwrite(td, {"text": text});
38027 var close = el.getElementsByTagName("div")[0];
38028 var inner = el.getElementsByTagName("em")[0];
38029 return {"el": el, "close": close, "inner": inner};
38032 // not sure what this is..
38033 // if(!this.tabTpl){
38034 //this.tabTpl = new Roo.Template(
38035 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38036 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38038 // this.tabTpl = new Roo.Template(
38039 // '<a href="#">' +
38040 // '<span unselectable="on"' +
38041 // (this.disableTooltips ? '' : ' title="{text}"') +
38042 // ' >{text}</span></a>'
38048 var template = tpl || this.tabTpl || false;
38052 template = new Roo.Template(
38054 '<span unselectable="on"' +
38055 (this.disableTooltips ? '' : ' title="{text}"') +
38056 ' >{text}</span></a>'
38060 switch (typeof(template)) {
38064 template = new Roo.Template(template);
38070 var el = template.overwrite(td, {"text": text});
38072 var inner = el.getElementsByTagName("span")[0];
38074 return {"el": el, "inner": inner};
38082 * @class Roo.TabPanelItem
38083 * @extends Roo.util.Observable
38084 * Represents an individual item (tab plus body) in a TabPanel.
38085 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38086 * @param {String} id The id of this TabPanelItem
38087 * @param {String} text The text for the tab of this TabPanelItem
38088 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38090 Roo.bootstrap.panel.TabItem = function(config){
38092 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38093 * @type Roo.TabPanel
38095 this.tabPanel = config.panel;
38097 * The id for this TabPanelItem
38100 this.id = config.id;
38102 this.disabled = false;
38104 this.text = config.text;
38106 this.loaded = false;
38107 this.closable = config.closable;
38110 * The body element for this TabPanelItem.
38111 * @type Roo.Element
38113 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38114 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38115 this.bodyEl.setStyle("display", "block");
38116 this.bodyEl.setStyle("zoom", "1");
38117 //this.hideAction();
38119 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38121 this.el = Roo.get(els.el);
38122 this.inner = Roo.get(els.inner, true);
38123 this.textEl = Roo.get(this.el.dom.firstChild, true);
38124 this.pnode = Roo.get(els.el.parentNode, true);
38125 // this.el.on("mousedown", this.onTabMouseDown, this);
38126 this.el.on("click", this.onTabClick, this);
38128 if(config.closable){
38129 var c = Roo.get(els.close, true);
38130 c.dom.title = this.closeText;
38131 c.addClassOnOver("close-over");
38132 c.on("click", this.closeClick, this);
38138 * Fires when this tab becomes the active tab.
38139 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38140 * @param {Roo.TabPanelItem} this
38144 * @event beforeclose
38145 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38146 * @param {Roo.TabPanelItem} this
38147 * @param {Object} e Set cancel to true on this object to cancel the close.
38149 "beforeclose": true,
38152 * Fires when this tab is closed.
38153 * @param {Roo.TabPanelItem} this
38157 * @event deactivate
38158 * Fires when this tab is no longer the active tab.
38159 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38160 * @param {Roo.TabPanelItem} this
38162 "deactivate" : true
38164 this.hidden = false;
38166 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38169 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38171 purgeListeners : function(){
38172 Roo.util.Observable.prototype.purgeListeners.call(this);
38173 this.el.removeAllListeners();
38176 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38179 this.pnode.addClass("active");
38182 this.tabPanel.stripWrap.repaint();
38184 this.fireEvent("activate", this.tabPanel, this);
38188 * Returns true if this tab is the active tab.
38189 * @return {Boolean}
38191 isActive : function(){
38192 return this.tabPanel.getActiveTab() == this;
38196 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38199 this.pnode.removeClass("active");
38201 this.fireEvent("deactivate", this.tabPanel, this);
38204 hideAction : function(){
38205 this.bodyEl.hide();
38206 this.bodyEl.setStyle("position", "absolute");
38207 this.bodyEl.setLeft("-20000px");
38208 this.bodyEl.setTop("-20000px");
38211 showAction : function(){
38212 this.bodyEl.setStyle("position", "relative");
38213 this.bodyEl.setTop("");
38214 this.bodyEl.setLeft("");
38215 this.bodyEl.show();
38219 * Set the tooltip for the tab.
38220 * @param {String} tooltip The tab's tooltip
38222 setTooltip : function(text){
38223 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38224 this.textEl.dom.qtip = text;
38225 this.textEl.dom.removeAttribute('title');
38227 this.textEl.dom.title = text;
38231 onTabClick : function(e){
38232 e.preventDefault();
38233 this.tabPanel.activate(this.id);
38236 onTabMouseDown : function(e){
38237 e.preventDefault();
38238 this.tabPanel.activate(this.id);
38241 getWidth : function(){
38242 return this.inner.getWidth();
38245 setWidth : function(width){
38246 var iwidth = width - this.pnode.getPadding("lr");
38247 this.inner.setWidth(iwidth);
38248 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38249 this.pnode.setWidth(width);
38253 * Show or hide the tab
38254 * @param {Boolean} hidden True to hide or false to show.
38256 setHidden : function(hidden){
38257 this.hidden = hidden;
38258 this.pnode.setStyle("display", hidden ? "none" : "");
38262 * Returns true if this tab is "hidden"
38263 * @return {Boolean}
38265 isHidden : function(){
38266 return this.hidden;
38270 * Returns the text for this tab
38273 getText : function(){
38277 autoSize : function(){
38278 //this.el.beginMeasure();
38279 this.textEl.setWidth(1);
38281 * #2804 [new] Tabs in Roojs
38282 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38284 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38285 //this.el.endMeasure();
38289 * Sets the text for the tab (Note: this also sets the tooltip text)
38290 * @param {String} text The tab's text and tooltip
38292 setText : function(text){
38294 this.textEl.update(text);
38295 this.setTooltip(text);
38296 //if(!this.tabPanel.resizeTabs){
38297 // this.autoSize();
38301 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38303 activate : function(){
38304 this.tabPanel.activate(this.id);
38308 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38310 disable : function(){
38311 if(this.tabPanel.active != this){
38312 this.disabled = true;
38313 this.pnode.addClass("disabled");
38318 * Enables this TabPanelItem if it was previously disabled.
38320 enable : function(){
38321 this.disabled = false;
38322 this.pnode.removeClass("disabled");
38326 * Sets the content for this TabPanelItem.
38327 * @param {String} content The content
38328 * @param {Boolean} loadScripts true to look for and load scripts
38330 setContent : function(content, loadScripts){
38331 this.bodyEl.update(content, loadScripts);
38335 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38336 * @return {Roo.UpdateManager} The UpdateManager
38338 getUpdateManager : function(){
38339 return this.bodyEl.getUpdateManager();
38343 * Set a URL to be used to load the content for this TabPanelItem.
38344 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38345 * @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)
38346 * @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)
38347 * @return {Roo.UpdateManager} The UpdateManager
38349 setUrl : function(url, params, loadOnce){
38350 if(this.refreshDelegate){
38351 this.un('activate', this.refreshDelegate);
38353 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38354 this.on("activate", this.refreshDelegate);
38355 return this.bodyEl.getUpdateManager();
38359 _handleRefresh : function(url, params, loadOnce){
38360 if(!loadOnce || !this.loaded){
38361 var updater = this.bodyEl.getUpdateManager();
38362 updater.update(url, params, this._setLoaded.createDelegate(this));
38367 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38368 * Will fail silently if the setUrl method has not been called.
38369 * This does not activate the panel, just updates its content.
38371 refresh : function(){
38372 if(this.refreshDelegate){
38373 this.loaded = false;
38374 this.refreshDelegate();
38379 _setLoaded : function(){
38380 this.loaded = true;
38384 closeClick : function(e){
38387 this.fireEvent("beforeclose", this, o);
38388 if(o.cancel !== true){
38389 this.tabPanel.removeTab(this.id);
38393 * The text displayed in the tooltip for the close icon.
38396 closeText : "Close this tab"
38399 * This script refer to:
38400 * Title: International Telephone Input
38401 * Author: Jack O'Connor
38402 * Code version: v12.1.12
38403 * Availability: https://github.com/jackocnr/intl-tel-input.git
38406 Roo.bootstrap.PhoneInputData = function() {
38409 "Afghanistan (افغانستان)",
38414 "Albania (Shqipëri)",
38419 "Algeria (الجزائر)",
38444 "Antigua and Barbuda",
38454 "Armenia (Հայաստան)",
38470 "Austria (Österreich)",
38475 "Azerbaijan (Azərbaycan)",
38485 "Bahrain (البحرين)",
38490 "Bangladesh (বাংলাদেশ)",
38500 "Belarus (Беларусь)",
38505 "Belgium (België)",
38535 "Bosnia and Herzegovina (Босна и Херцеговина)",
38550 "British Indian Ocean Territory",
38555 "British Virgin Islands",
38565 "Bulgaria (България)",
38575 "Burundi (Uburundi)",
38580 "Cambodia (កម្ពុជា)",
38585 "Cameroon (Cameroun)",
38594 ["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"]
38597 "Cape Verde (Kabu Verdi)",
38602 "Caribbean Netherlands",
38613 "Central African Republic (République centrafricaine)",
38633 "Christmas Island",
38639 "Cocos (Keeling) Islands",
38650 "Comoros (جزر القمر)",
38655 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38660 "Congo (Republic) (Congo-Brazzaville)",
38680 "Croatia (Hrvatska)",
38701 "Czech Republic (Česká republika)",
38706 "Denmark (Danmark)",
38721 "Dominican Republic (República Dominicana)",
38725 ["809", "829", "849"]
38743 "Equatorial Guinea (Guinea Ecuatorial)",
38763 "Falkland Islands (Islas Malvinas)",
38768 "Faroe Islands (Føroyar)",
38789 "French Guiana (Guyane française)",
38794 "French Polynesia (Polynésie française)",
38809 "Georgia (საქართველო)",
38814 "Germany (Deutschland)",
38834 "Greenland (Kalaallit Nunaat)",
38871 "Guinea-Bissau (Guiné Bissau)",
38896 "Hungary (Magyarország)",
38901 "Iceland (Ísland)",
38921 "Iraq (العراق)",
38937 "Israel (ישראל)",
38964 "Jordan (الأردن)",
38969 "Kazakhstan (Казахстан)",
38990 "Kuwait (الكويت)",
38995 "Kyrgyzstan (Кыргызстан)",
39005 "Latvia (Latvija)",
39010 "Lebanon (لبنان)",
39025 "Libya (ليبيا)",
39035 "Lithuania (Lietuva)",
39050 "Macedonia (FYROM) (Македонија)",
39055 "Madagascar (Madagasikara)",
39085 "Marshall Islands",
39095 "Mauritania (موريتانيا)",
39100 "Mauritius (Moris)",
39121 "Moldova (Republica Moldova)",
39131 "Mongolia (Монгол)",
39136 "Montenegro (Crna Gora)",
39146 "Morocco (المغرب)",
39152 "Mozambique (Moçambique)",
39157 "Myanmar (Burma) (မြန်မာ)",
39162 "Namibia (Namibië)",
39177 "Netherlands (Nederland)",
39182 "New Caledonia (Nouvelle-Calédonie)",
39217 "North Korea (조선 민주주의 인민 공화국)",
39222 "Northern Mariana Islands",
39238 "Pakistan (پاکستان)",
39248 "Palestine (فلسطين)",
39258 "Papua New Guinea",
39300 "Réunion (La Réunion)",
39306 "Romania (România)",
39322 "Saint Barthélemy",
39333 "Saint Kitts and Nevis",
39343 "Saint Martin (Saint-Martin (partie française))",
39349 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39354 "Saint Vincent and the Grenadines",
39369 "São Tomé and Príncipe (São Tomé e Príncipe)",
39374 "Saudi Arabia (المملكة العربية السعودية)",
39379 "Senegal (Sénégal)",
39409 "Slovakia (Slovensko)",
39414 "Slovenia (Slovenija)",
39424 "Somalia (Soomaaliya)",
39434 "South Korea (대한민국)",
39439 "South Sudan (جنوب السودان)",
39449 "Sri Lanka (ශ්රී ලංකාව)",
39454 "Sudan (السودان)",
39464 "Svalbard and Jan Mayen",
39475 "Sweden (Sverige)",
39480 "Switzerland (Schweiz)",
39485 "Syria (سوريا)",
39530 "Trinidad and Tobago",
39535 "Tunisia (تونس)",
39540 "Turkey (Türkiye)",
39550 "Turks and Caicos Islands",
39560 "U.S. Virgin Islands",
39570 "Ukraine (Україна)",
39575 "United Arab Emirates (الإمارات العربية المتحدة)",
39597 "Uzbekistan (Oʻzbekiston)",
39607 "Vatican City (Città del Vaticano)",
39618 "Vietnam (Việt Nam)",
39623 "Wallis and Futuna (Wallis-et-Futuna)",
39628 "Western Sahara (الصحراء الغربية)",
39634 "Yemen (اليمن)",
39658 * This script refer to:
39659 * Title: International Telephone Input
39660 * Author: Jack O'Connor
39661 * Code version: v12.1.12
39662 * Availability: https://github.com/jackocnr/intl-tel-input.git
39666 * @class Roo.bootstrap.PhoneInput
39667 * @extends Roo.bootstrap.TriggerField
39668 * An input with International dial-code selection
39670 * @cfg {String} defaultDialCode default '+852'
39671 * @cfg {Array} preferedCountries default []
39674 * Create a new PhoneInput.
39675 * @param {Object} config Configuration options
39678 Roo.bootstrap.PhoneInput = function(config) {
39679 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39682 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39684 listWidth: undefined,
39686 selectedClass: 'active',
39688 invalidClass : "has-warning",
39690 validClass: 'has-success',
39692 allowed: '0123456789',
39695 * @cfg {String} defaultDialCode The default dial code when initializing the input
39697 defaultDialCode: '+852',
39700 * @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
39702 preferedCountries: false,
39704 getAutoCreate : function()
39706 var data = Roo.bootstrap.PhoneInputData();
39707 var align = this.labelAlign || this.parentLabelAlign();
39710 this.allCountries = [];
39711 this.dialCodeMapping = [];
39713 for (var i = 0; i < data.length; i++) {
39715 this.allCountries[i] = {
39719 priority: c[3] || 0,
39720 areaCodes: c[4] || null
39722 this.dialCodeMapping[c[2]] = {
39725 priority: c[3] || 0,
39726 areaCodes: c[4] || null
39738 cls : 'form-control tel-input',
39739 autocomplete: 'new-password'
39742 var hiddenInput = {
39745 cls: 'hidden-tel-input'
39749 hiddenInput.name = this.name;
39752 if (this.disabled) {
39753 input.disabled = true;
39756 var flag_container = {
39773 cls: this.hasFeedback ? 'has-feedback' : '',
39779 cls: 'dial-code-holder',
39786 cls: 'roo-select2-container input-group',
39793 if (this.fieldLabel.length) {
39796 tooltip: 'This field is required'
39802 cls: 'control-label',
39808 html: this.fieldLabel
39811 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39817 if(this.indicatorpos == 'right') {
39818 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39825 if(align == 'left') {
39833 if(this.labelWidth > 12){
39834 label.style = "width: " + this.labelWidth + 'px';
39836 if(this.labelWidth < 13 && this.labelmd == 0){
39837 this.labelmd = this.labelWidth;
39839 if(this.labellg > 0){
39840 label.cls += ' col-lg-' + this.labellg;
39841 input.cls += ' col-lg-' + (12 - this.labellg);
39843 if(this.labelmd > 0){
39844 label.cls += ' col-md-' + this.labelmd;
39845 container.cls += ' col-md-' + (12 - this.labelmd);
39847 if(this.labelsm > 0){
39848 label.cls += ' col-sm-' + this.labelsm;
39849 container.cls += ' col-sm-' + (12 - this.labelsm);
39851 if(this.labelxs > 0){
39852 label.cls += ' col-xs-' + this.labelxs;
39853 container.cls += ' col-xs-' + (12 - this.labelxs);
39863 var settings = this;
39865 ['xs','sm','md','lg'].map(function(size){
39866 if (settings[size]) {
39867 cfg.cls += ' col-' + size + '-' + settings[size];
39871 this.store = new Roo.data.Store({
39872 proxy : new Roo.data.MemoryProxy({}),
39873 reader : new Roo.data.JsonReader({
39884 'name' : 'dialCode',
39888 'name' : 'priority',
39892 'name' : 'areaCodes',
39899 if(!this.preferedCountries) {
39900 this.preferedCountries = [
39907 var p = this.preferedCountries.reverse();
39910 for (var i = 0; i < p.length; i++) {
39911 for (var j = 0; j < this.allCountries.length; j++) {
39912 if(this.allCountries[j].iso2 == p[i]) {
39913 var t = this.allCountries[j];
39914 this.allCountries.splice(j,1);
39915 this.allCountries.unshift(t);
39921 this.store.proxy.data = {
39923 data: this.allCountries
39929 initEvents : function()
39932 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39934 this.indicator = this.indicatorEl();
39935 this.flag = this.flagEl();
39936 this.dialCodeHolder = this.dialCodeHolderEl();
39938 this.trigger = this.el.select('div.flag-box',true).first();
39939 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39944 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39945 _this.list.setWidth(lw);
39948 this.list.on('mouseover', this.onViewOver, this);
39949 this.list.on('mousemove', this.onViewMove, this);
39950 this.inputEl().on("keyup", this.onKeyUp, this);
39952 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39954 this.view = new Roo.View(this.list, this.tpl, {
39955 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39958 this.view.on('click', this.onViewClick, this);
39959 this.setValue(this.defaultDialCode);
39962 onTriggerClick : function(e)
39964 Roo.log('trigger click');
39969 if(this.isExpanded()){
39971 this.hasFocus = false;
39973 this.store.load({});
39974 this.hasFocus = true;
39979 isExpanded : function()
39981 return this.list.isVisible();
39984 collapse : function()
39986 if(!this.isExpanded()){
39990 Roo.get(document).un('mousedown', this.collapseIf, this);
39991 Roo.get(document).un('mousewheel', this.collapseIf, this);
39992 this.fireEvent('collapse', this);
39996 expand : function()
40000 if(this.isExpanded() || !this.hasFocus){
40004 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40005 this.list.setWidth(lw);
40008 this.restrictHeight();
40010 Roo.get(document).on('mousedown', this.collapseIf, this);
40011 Roo.get(document).on('mousewheel', this.collapseIf, this);
40013 this.fireEvent('expand', this);
40016 restrictHeight : function()
40018 this.list.alignTo(this.inputEl(), this.listAlign);
40019 this.list.alignTo(this.inputEl(), this.listAlign);
40022 onViewOver : function(e, t)
40024 if(this.inKeyMode){
40027 var item = this.view.findItemFromChild(t);
40030 var index = this.view.indexOf(item);
40031 this.select(index, false);
40036 onViewClick : function(view, doFocus, el, e)
40038 var index = this.view.getSelectedIndexes()[0];
40040 var r = this.store.getAt(index);
40043 this.onSelect(r, index);
40045 if(doFocus !== false && !this.blockFocus){
40046 this.inputEl().focus();
40050 onViewMove : function(e, t)
40052 this.inKeyMode = false;
40055 select : function(index, scrollIntoView)
40057 this.selectedIndex = index;
40058 this.view.select(index);
40059 if(scrollIntoView !== false){
40060 var el = this.view.getNode(index);
40062 this.list.scrollChildIntoView(el, false);
40067 createList : function()
40069 this.list = Roo.get(document.body).createChild({
40071 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40072 style: 'display:none'
40075 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40078 collapseIf : function(e)
40080 var in_combo = e.within(this.el);
40081 var in_list = e.within(this.list);
40082 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40084 if (in_combo || in_list || is_list) {
40090 onSelect : function(record, index)
40092 if(this.fireEvent('beforeselect', this, record, index) !== false){
40094 this.setFlagClass(record.data.iso2);
40095 this.setDialCode(record.data.dialCode);
40096 this.hasFocus = false;
40098 this.fireEvent('select', this, record, index);
40102 flagEl : function()
40104 var flag = this.el.select('div.flag',true).first();
40111 dialCodeHolderEl : function()
40113 var d = this.el.select('input.dial-code-holder',true).first();
40120 setDialCode : function(v)
40122 this.dialCodeHolder.dom.value = '+'+v;
40125 setFlagClass : function(n)
40127 this.flag.dom.className = 'flag '+n;
40130 getValue : function()
40132 var v = this.inputEl().getValue();
40133 if(this.dialCodeHolder) {
40134 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40139 setValue : function(v)
40141 var d = this.getDialCode(v);
40143 //invalid dial code
40144 if(v.length == 0 || !d || d.length == 0) {
40146 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40147 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40153 this.setFlagClass(this.dialCodeMapping[d].iso2);
40154 this.setDialCode(d);
40155 this.inputEl().dom.value = v.replace('+'+d,'');
40156 this.hiddenEl().dom.value = this.getValue();
40161 getDialCode : function(v)
40165 if (v.length == 0) {
40166 return this.dialCodeHolder.dom.value;
40170 if (v.charAt(0) != "+") {
40173 var numericChars = "";
40174 for (var i = 1; i < v.length; i++) {
40175 var c = v.charAt(i);
40178 if (this.dialCodeMapping[numericChars]) {
40179 dialCode = v.substr(1, i);
40181 if (numericChars.length == 4) {
40191 this.setValue(this.defaultDialCode);
40195 hiddenEl : function()
40197 return this.el.select('input.hidden-tel-input',true).first();
40200 onKeyUp : function(e){
40202 var k = e.getKey();
40203 var c = e.getCharCode();
40206 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40207 this.allowed.indexOf(String.fromCharCode(c)) === -1
40212 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40215 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40219 this.setValue(this.getValue());
40224 * @class Roo.bootstrap.MoneyField
40225 * @extends Roo.bootstrap.ComboBox
40226 * Bootstrap MoneyField class
40229 * Create a new MoneyField.
40230 * @param {Object} config Configuration options
40233 Roo.bootstrap.MoneyField = function(config) {
40235 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40239 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40242 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40244 allowDecimals : true,
40246 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40248 decimalSeparator : ".",
40250 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40252 decimalPrecision : 0,
40254 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40256 allowNegative : true,
40258 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40262 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40264 minValue : Number.NEGATIVE_INFINITY,
40266 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40268 maxValue : Number.MAX_VALUE,
40270 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40272 minText : "The minimum value for this field is {0}",
40274 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40276 maxText : "The maximum value for this field is {0}",
40278 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40279 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40281 nanText : "{0} is not a valid number",
40283 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40287 * @cfg {String} defaults currency of the MoneyField
40288 * value should be in lkey
40290 defaultCurrency : false,
40292 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40294 thousandsDelimiter : false,
40304 getAutoCreate : function()
40306 var align = this.labelAlign || this.parentLabelAlign();
40318 cls : 'form-control roo-money-amount-input',
40319 autocomplete: 'new-password'
40322 var hiddenInput = {
40326 cls: 'hidden-number-input'
40330 hiddenInput.name = this.name;
40333 if (this.disabled) {
40334 input.disabled = true;
40337 var clg = 12 - this.inputlg;
40338 var cmd = 12 - this.inputmd;
40339 var csm = 12 - this.inputsm;
40340 var cxs = 12 - this.inputxs;
40344 cls : 'row roo-money-field',
40348 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40352 cls: 'roo-select2-container input-group',
40356 cls : 'form-control roo-money-currency-input',
40357 autocomplete: 'new-password',
40359 name : this.currencyName
40363 cls : 'input-group-addon',
40377 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40381 cls: this.hasFeedback ? 'has-feedback' : '',
40392 if (this.fieldLabel.length) {
40395 tooltip: 'This field is required'
40401 cls: 'control-label',
40407 html: this.fieldLabel
40410 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40416 if(this.indicatorpos == 'right') {
40417 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40424 if(align == 'left') {
40432 if(this.labelWidth > 12){
40433 label.style = "width: " + this.labelWidth + 'px';
40435 if(this.labelWidth < 13 && this.labelmd == 0){
40436 this.labelmd = this.labelWidth;
40438 if(this.labellg > 0){
40439 label.cls += ' col-lg-' + this.labellg;
40440 input.cls += ' col-lg-' + (12 - this.labellg);
40442 if(this.labelmd > 0){
40443 label.cls += ' col-md-' + this.labelmd;
40444 container.cls += ' col-md-' + (12 - this.labelmd);
40446 if(this.labelsm > 0){
40447 label.cls += ' col-sm-' + this.labelsm;
40448 container.cls += ' col-sm-' + (12 - this.labelsm);
40450 if(this.labelxs > 0){
40451 label.cls += ' col-xs-' + this.labelxs;
40452 container.cls += ' col-xs-' + (12 - this.labelxs);
40463 var settings = this;
40465 ['xs','sm','md','lg'].map(function(size){
40466 if (settings[size]) {
40467 cfg.cls += ' col-' + size + '-' + settings[size];
40474 initEvents : function()
40476 this.indicator = this.indicatorEl();
40478 this.initCurrencyEvent();
40480 this.initNumberEvent();
40483 initCurrencyEvent : function()
40486 throw "can not find store for combo";
40489 this.store = Roo.factory(this.store, Roo.data);
40490 this.store.parent = this;
40494 this.triggerEl = this.el.select('.input-group-addon', true).first();
40496 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40501 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40502 _this.list.setWidth(lw);
40505 this.list.on('mouseover', this.onViewOver, this);
40506 this.list.on('mousemove', this.onViewMove, this);
40507 this.list.on('scroll', this.onViewScroll, this);
40510 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40513 this.view = new Roo.View(this.list, this.tpl, {
40514 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40517 this.view.on('click', this.onViewClick, this);
40519 this.store.on('beforeload', this.onBeforeLoad, this);
40520 this.store.on('load', this.onLoad, this);
40521 this.store.on('loadexception', this.onLoadException, this);
40523 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40524 "up" : function(e){
40525 this.inKeyMode = true;
40529 "down" : function(e){
40530 if(!this.isExpanded()){
40531 this.onTriggerClick();
40533 this.inKeyMode = true;
40538 "enter" : function(e){
40541 if(this.fireEvent("specialkey", this, e)){
40542 this.onViewClick(false);
40548 "esc" : function(e){
40552 "tab" : function(e){
40555 if(this.fireEvent("specialkey", this, e)){
40556 this.onViewClick(false);
40564 doRelay : function(foo, bar, hname){
40565 if(hname == 'down' || this.scope.isExpanded()){
40566 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40574 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40578 initNumberEvent : function(e)
40580 this.inputEl().on("keydown" , this.fireKey, this);
40581 this.inputEl().on("focus", this.onFocus, this);
40582 this.inputEl().on("blur", this.onBlur, this);
40584 this.inputEl().relayEvent('keyup', this);
40586 if(this.indicator){
40587 this.indicator.addClass('invisible');
40590 this.originalValue = this.getValue();
40592 if(this.validationEvent == 'keyup'){
40593 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40594 this.inputEl().on('keyup', this.filterValidation, this);
40596 else if(this.validationEvent !== false){
40597 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40600 if(this.selectOnFocus){
40601 this.on("focus", this.preFocus, this);
40604 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40605 this.inputEl().on("keypress", this.filterKeys, this);
40607 this.inputEl().relayEvent('keypress', this);
40610 var allowed = "0123456789";
40612 if(this.allowDecimals){
40613 allowed += this.decimalSeparator;
40616 if(this.allowNegative){
40620 if(this.thousandsDelimiter) {
40624 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40626 var keyPress = function(e){
40628 var k = e.getKey();
40630 var c = e.getCharCode();
40633 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40634 allowed.indexOf(String.fromCharCode(c)) === -1
40640 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40644 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40649 this.inputEl().on("keypress", keyPress, this);
40653 onTriggerClick : function(e)
40660 this.loadNext = false;
40662 if(this.isExpanded()){
40667 this.hasFocus = true;
40669 if(this.triggerAction == 'all') {
40670 this.doQuery(this.allQuery, true);
40674 this.doQuery(this.getRawValue());
40677 getCurrency : function()
40679 var v = this.currencyEl().getValue();
40684 restrictHeight : function()
40686 this.list.alignTo(this.currencyEl(), this.listAlign);
40687 this.list.alignTo(this.currencyEl(), this.listAlign);
40690 onViewClick : function(view, doFocus, el, e)
40692 var index = this.view.getSelectedIndexes()[0];
40694 var r = this.store.getAt(index);
40697 this.onSelect(r, index);
40701 onSelect : function(record, index){
40703 if(this.fireEvent('beforeselect', this, record, index) !== false){
40705 this.setFromCurrencyData(index > -1 ? record.data : false);
40709 this.fireEvent('select', this, record, index);
40713 setFromCurrencyData : function(o)
40717 this.lastCurrency = o;
40719 if (this.currencyField) {
40720 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40722 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40725 this.lastSelectionText = currency;
40727 //setting default currency
40728 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40729 this.setCurrency(this.defaultCurrency);
40733 this.setCurrency(currency);
40736 setFromData : function(o)
40740 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40742 this.setFromCurrencyData(c);
40747 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40749 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40752 this.setValue(value);
40756 setCurrency : function(v)
40758 this.currencyValue = v;
40761 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40766 setValue : function(v)
40768 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40774 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40776 this.inputEl().dom.value = (v == '') ? '' :
40777 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40779 if(!this.allowZero && v === '0') {
40780 this.hiddenEl().dom.value = '';
40781 this.inputEl().dom.value = '';
40788 getRawValue : function()
40790 var v = this.inputEl().getValue();
40795 getValue : function()
40797 return this.fixPrecision(this.parseValue(this.getRawValue()));
40800 parseValue : function(value)
40802 if(this.thousandsDelimiter) {
40804 r = new RegExp(",", "g");
40805 value = value.replace(r, "");
40808 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40809 return isNaN(value) ? '' : value;
40813 fixPrecision : function(value)
40815 if(this.thousandsDelimiter) {
40817 r = new RegExp(",", "g");
40818 value = value.replace(r, "");
40821 var nan = isNaN(value);
40823 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40824 return nan ? '' : value;
40826 return parseFloat(value).toFixed(this.decimalPrecision);
40829 decimalPrecisionFcn : function(v)
40831 return Math.floor(v);
40834 validateValue : function(value)
40836 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40840 var num = this.parseValue(value);
40843 this.markInvalid(String.format(this.nanText, value));
40847 if(num < this.minValue){
40848 this.markInvalid(String.format(this.minText, this.minValue));
40852 if(num > this.maxValue){
40853 this.markInvalid(String.format(this.maxText, this.maxValue));
40860 validate : function()
40862 if(this.disabled || this.allowBlank){
40867 var currency = this.getCurrency();
40869 if(this.validateValue(this.getRawValue()) && currency.length){
40874 this.markInvalid();
40878 getName: function()
40883 beforeBlur : function()
40889 var v = this.parseValue(this.getRawValue());
40896 onBlur : function()
40900 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40901 //this.el.removeClass(this.focusClass);
40904 this.hasFocus = false;
40906 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40910 var v = this.getValue();
40912 if(String(v) !== String(this.startValue)){
40913 this.fireEvent('change', this, v, this.startValue);
40916 this.fireEvent("blur", this);
40919 inputEl : function()
40921 return this.el.select('.roo-money-amount-input', true).first();
40924 currencyEl : function()
40926 return this.el.select('.roo-money-currency-input', true).first();
40929 hiddenEl : function()
40931 return this.el.select('input.hidden-number-input',true).first();