4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
407 * Hide a component - adds 'hidden' class
411 if(!this.getVisibilityEl()){
415 this.getVisibilityEl().addClass('hidden');
428 * @class Roo.bootstrap.Body
429 * @extends Roo.bootstrap.Component
430 * Bootstrap Body class
434 * @param {Object} config The config object
437 Roo.bootstrap.Body = function(config){
439 config = config || {};
441 Roo.bootstrap.Body.superclass.constructor.call(this, config);
442 this.el = Roo.get(config.el ? config.el : document.body );
443 if (this.cls && this.cls.length) {
444 Roo.get(document.body).addClass(this.cls);
448 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
450 is_body : true,// just to make sure it's constructed?
455 onRender : function(ct, position)
457 /* Roo.log("Roo.bootstrap.Body - onRender");
458 if (this.cls && this.cls.length) {
459 Roo.get(document.body).addClass(this.cls);
478 * @class Roo.bootstrap.ButtonGroup
479 * @extends Roo.bootstrap.Component
480 * Bootstrap ButtonGroup class
481 * @cfg {String} size lg | sm | xs (default empty normal)
482 * @cfg {String} align vertical | justified (default none)
483 * @cfg {String} direction up | down (default down)
484 * @cfg {Boolean} toolbar false | true
485 * @cfg {Boolean} btn true | false
490 * @param {Object} config The config object
493 Roo.bootstrap.ButtonGroup = function(config){
494 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
497 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
505 getAutoCreate : function(){
511 cfg.html = this.html || cfg.html;
522 if (['vertical','justified'].indexOf(this.align)!==-1) {
523 cfg.cls = 'btn-group-' + this.align;
525 if (this.align == 'justified') {
526 console.log(this.items);
530 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
531 cfg.cls += ' btn-group-' + this.size;
534 if (this.direction == 'up') {
535 cfg.cls += ' dropup' ;
551 * @class Roo.bootstrap.Button
552 * @extends Roo.bootstrap.Component
553 * Bootstrap Button class
554 * @cfg {String} html The button content
555 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
556 * @cfg {String} size ( lg | sm | xs)
557 * @cfg {String} tag ( a | input | submit)
558 * @cfg {String} href empty or href
559 * @cfg {Boolean} disabled default false;
560 * @cfg {Boolean} isClose default false;
561 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
562 * @cfg {String} badge text for badge
563 * @cfg {String} theme (default|glow)
564 * @cfg {Boolean} inverse dark themed version
565 * @cfg {Boolean} toggle is it a slidy toggle button
566 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
567 * @cfg {String} ontext text for on slidy toggle state
568 * @cfg {String} offtext text for off slidy toggle state
569 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
570 * @cfg {Boolean} removeClass remove the standard class..
571 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
574 * Create a new button
575 * @param {Object} config The config object
579 Roo.bootstrap.Button = function(config){
580 Roo.bootstrap.Button.superclass.constructor.call(this, config);
581 this.weightClass = ["btn-default",
593 * When a butotn is pressed
594 * @param {Roo.bootstrap.Button} btn
595 * @param {Roo.EventObject} e
600 * After the button has been toggles
601 * @param {Roo.bootstrap.Button} btn
602 * @param {Roo.EventObject} e
603 * @param {boolean} pressed (also available as button.pressed)
609 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
627 preventDefault: true,
635 getAutoCreate : function(){
643 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
644 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
651 if (this.toggle == true) {
654 cls: 'slider-frame roo-button',
659 'data-off-text':'OFF',
660 cls: 'slider-button',
666 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667 cfg.cls += ' '+this.weight;
676 cfg["aria-hidden"] = true;
678 cfg.html = "×";
684 if (this.theme==='default') {
685 cfg.cls = 'btn roo-button';
687 //if (this.parentType != 'Navbar') {
688 this.weight = this.weight.length ? this.weight : 'default';
690 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
692 cfg.cls += ' btn-' + this.weight;
694 } else if (this.theme==='glow') {
697 cfg.cls = 'btn-glow roo-button';
699 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701 cfg.cls += ' ' + this.weight;
707 this.cls += ' inverse';
711 if (this.active || this.pressed === true) {
712 cfg.cls += ' active';
716 cfg.disabled = 'disabled';
720 Roo.log('changing to ul' );
722 this.glyphicon = 'caret';
725 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
727 //gsRoo.log(this.parentType);
728 if (this.parentType === 'Navbar' && !this.parent().bar) {
729 Roo.log('changing to li?');
738 href : this.href || '#'
741 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
742 cfg.cls += ' dropdown';
749 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
751 if (this.glyphicon) {
752 cfg.html = ' ' + cfg.html;
757 cls: 'glyphicon glyphicon-' + this.glyphicon
767 // cfg.cls='btn roo-button';
771 var value = cfg.html;
776 cls: 'glyphicon glyphicon-' + this.glyphicon,
795 cfg.cls += ' dropdown';
796 cfg.html = typeof(cfg.html) != 'undefined' ?
797 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
800 if (cfg.tag !== 'a' && this.href !== '') {
801 throw "Tag must be a to set href.";
802 } else if (this.href.length > 0) {
803 cfg.href = this.href;
806 if(this.removeClass){
811 cfg.target = this.target;
816 initEvents: function() {
817 // Roo.log('init events?');
818 // Roo.log(this.el.dom);
821 if (typeof (this.menu) != 'undefined') {
822 this.menu.parentType = this.xtype;
823 this.menu.triggerEl = this.el;
824 this.addxtype(Roo.apply({}, this.menu));
828 if (this.el.hasClass('roo-button')) {
829 this.el.on('click', this.onClick, this);
831 this.el.select('.roo-button').on('click', this.onClick, this);
834 if(this.removeClass){
835 this.el.on('click', this.onClick, this);
838 this.el.enableDisplayMode();
841 onClick : function(e)
847 Roo.log('button on click ');
848 if(this.preventDefault){
852 if (this.pressed === true || this.pressed === false) {
853 this.toggleActive(e);
857 this.fireEvent('click', this, e);
861 * Enables this button
865 this.disabled = false;
866 this.el.removeClass('disabled');
870 * Disable this button
874 this.disabled = true;
875 this.el.addClass('disabled');
878 * sets the active state on/off,
879 * @param {Boolean} state (optional) Force a particular state
881 setActive : function(v) {
883 this.el[v ? 'addClass' : 'removeClass']('active');
887 * toggles the current active state
889 toggleActive : function(e)
891 this.setActive(!this.pressed);
892 this.fireEvent('toggle', this, e, !this.pressed);
895 * get the current active state
896 * @return {boolean} true if it's active
898 isActive : function()
900 return this.el.hasClass('active');
903 * set the text of the first selected button
905 setText : function(str)
907 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
910 * get the text of the first selected button
914 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
925 setWeight : function(str)
927 this.el.removeClass(this.weightClass);
928 this.el.addClass('btn-' + str);
942 * @class Roo.bootstrap.Column
943 * @extends Roo.bootstrap.Component
944 * Bootstrap Column class
945 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
946 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
947 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
948 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
949 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
955 * @cfg {Boolean} hidden (true|false) hide the element
956 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
957 * @cfg {String} fa (ban|check|...) font awesome icon
958 * @cfg {Number} fasize (1|2|....) font awsome size
960 * @cfg {String} icon (info-sign|check|...) glyphicon name
962 * @cfg {String} html content of column.
965 * Create a new Column
966 * @param {Object} config The config object
969 Roo.bootstrap.Column = function(config){
970 Roo.bootstrap.Column.superclass.constructor.call(this, config);
973 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
991 getAutoCreate : function(){
992 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1000 ['xs','sm','md','lg'].map(function(size){
1001 //Roo.log( size + ':' + settings[size]);
1003 if (settings[size+'off'] !== false) {
1004 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1007 if (settings[size] === false) {
1011 if (!settings[size]) { // 0 = hidden
1012 cfg.cls += ' hidden-' + size;
1015 cfg.cls += ' col-' + size + '-' + settings[size];
1020 cfg.cls += ' hidden';
1023 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1024 cfg.cls +=' alert alert-' + this.alert;
1028 if (this.html.length) {
1029 cfg.html = this.html;
1033 if (this.fasize > 1) {
1034 fasize = ' fa-' + this.fasize + 'x';
1036 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1041 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1060 * @class Roo.bootstrap.Container
1061 * @extends Roo.bootstrap.Component
1062 * Bootstrap Container class
1063 * @cfg {Boolean} jumbotron is it a jumbotron element
1064 * @cfg {String} html content of element
1065 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1066 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1067 * @cfg {String} header content of header (for panel)
1068 * @cfg {String} footer content of footer (for panel)
1069 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1070 * @cfg {String} tag (header|aside|section) type of HTML tag.
1071 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1072 * @cfg {String} fa font awesome icon
1073 * @cfg {String} icon (info-sign|check|...) glyphicon name
1074 * @cfg {Boolean} hidden (true|false) hide the element
1075 * @cfg {Boolean} expandable (true|false) default false
1076 * @cfg {Boolean} expanded (true|false) default true
1077 * @cfg {String} rheader contet on the right of header
1078 * @cfg {Boolean} clickable (true|false) default false
1082 * Create a new Container
1083 * @param {Object} config The config object
1086 Roo.bootstrap.Container = function(config){
1087 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1093 * After the panel has been expand
1095 * @param {Roo.bootstrap.Container} this
1100 * After the panel has been collapsed
1102 * @param {Roo.bootstrap.Container} this
1107 * When a element is chick
1108 * @param {Roo.bootstrap.Container} this
1109 * @param {Roo.EventObject} e
1115 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1133 getChildContainer : function() {
1139 if (this.panel.length) {
1140 return this.el.select('.panel-body',true).first();
1147 getAutoCreate : function(){
1150 tag : this.tag || 'div',
1154 if (this.jumbotron) {
1155 cfg.cls = 'jumbotron';
1160 // - this is applied by the parent..
1162 // cfg.cls = this.cls + '';
1165 if (this.sticky.length) {
1167 var bd = Roo.get(document.body);
1168 if (!bd.hasClass('bootstrap-sticky')) {
1169 bd.addClass('bootstrap-sticky');
1170 Roo.select('html',true).setStyle('height', '100%');
1173 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1177 if (this.well.length) {
1178 switch (this.well) {
1181 cfg.cls +=' well well-' +this.well;
1190 cfg.cls += ' hidden';
1194 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1195 cfg.cls +=' alert alert-' + this.alert;
1200 if (this.panel.length) {
1201 cfg.cls += ' panel panel-' + this.panel;
1203 if (this.header.length) {
1207 if(this.expandable){
1209 cfg.cls = cfg.cls + ' expandable';
1213 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1221 cls : 'panel-title',
1222 html : (this.expandable ? ' ' : '') + this.header
1226 cls: 'panel-header-right',
1232 cls : 'panel-heading',
1233 style : this.expandable ? 'cursor: pointer' : '',
1241 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1246 if (this.footer.length) {
1248 cls : 'panel-footer',
1257 body.html = this.html || cfg.html;
1258 // prefix with the icons..
1260 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1263 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1268 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1269 cfg.cls = 'container';
1275 initEvents: function()
1277 if(this.expandable){
1278 var headerEl = this.headerEl();
1281 headerEl.on('click', this.onToggleClick, this);
1286 this.el.on('click', this.onClick, this);
1291 onToggleClick : function()
1293 var headerEl = this.headerEl();
1309 if(this.fireEvent('expand', this)) {
1311 this.expanded = true;
1313 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1315 this.el.select('.panel-body',true).first().removeClass('hide');
1317 var toggleEl = this.toggleEl();
1323 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1328 collapse : function()
1330 if(this.fireEvent('collapse', this)) {
1332 this.expanded = false;
1334 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1335 this.el.select('.panel-body',true).first().addClass('hide');
1337 var toggleEl = this.toggleEl();
1343 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1347 toggleEl : function()
1349 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1353 return this.el.select('.panel-heading .fa',true).first();
1356 headerEl : function()
1358 if(!this.el || !this.panel.length || !this.header.length){
1362 return this.el.select('.panel-heading',true).first()
1367 if(!this.el || !this.panel.length){
1371 return this.el.select('.panel-body',true).first()
1374 titleEl : function()
1376 if(!this.el || !this.panel.length || !this.header.length){
1380 return this.el.select('.panel-title',true).first();
1383 setTitle : function(v)
1385 var titleEl = this.titleEl();
1391 titleEl.dom.innerHTML = v;
1394 getTitle : function()
1397 var titleEl = this.titleEl();
1403 return titleEl.dom.innerHTML;
1406 setRightTitle : function(v)
1408 var t = this.el.select('.panel-header-right',true).first();
1414 t.dom.innerHTML = v;
1417 onClick : function(e)
1421 this.fireEvent('click', this, e);
1434 * @class Roo.bootstrap.Img
1435 * @extends Roo.bootstrap.Component
1436 * Bootstrap Img class
1437 * @cfg {Boolean} imgResponsive false | true
1438 * @cfg {String} border rounded | circle | thumbnail
1439 * @cfg {String} src image source
1440 * @cfg {String} alt image alternative text
1441 * @cfg {String} href a tag href
1442 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1443 * @cfg {String} xsUrl xs image source
1444 * @cfg {String} smUrl sm image source
1445 * @cfg {String} mdUrl md image source
1446 * @cfg {String} lgUrl lg image source
1449 * Create a new Input
1450 * @param {Object} config The config object
1453 Roo.bootstrap.Img = function(config){
1454 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1460 * The img click event for the img.
1461 * @param {Roo.EventObject} e
1467 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1469 imgResponsive: true,
1479 getAutoCreate : function()
1481 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1482 return this.createSingleImg();
1487 cls: 'roo-image-responsive-group',
1492 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1494 if(!_this[size + 'Url']){
1500 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1501 html: _this.html || cfg.html,
1502 src: _this[size + 'Url']
1505 img.cls += ' roo-image-responsive-' + size;
1507 var s = ['xs', 'sm', 'md', 'lg'];
1509 s.splice(s.indexOf(size), 1);
1511 Roo.each(s, function(ss){
1512 img.cls += ' hidden-' + ss;
1515 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1516 cfg.cls += ' img-' + _this.border;
1520 cfg.alt = _this.alt;
1533 a.target = _this.target;
1537 cfg.cn.push((_this.href) ? a : img);
1544 createSingleImg : function()
1548 cls: (this.imgResponsive) ? 'img-responsive' : '',
1550 src : 'about:blank' // just incase src get's set to undefined?!?
1553 cfg.html = this.html || cfg.html;
1555 cfg.src = this.src || cfg.src;
1557 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1558 cfg.cls += ' img-' + this.border;
1575 a.target = this.target;
1580 return (this.href) ? a : cfg;
1583 initEvents: function()
1586 this.el.on('click', this.onClick, this);
1591 onClick : function(e)
1593 Roo.log('img onclick');
1594 this.fireEvent('click', this, e);
1597 * Sets the url of the image - used to update it
1598 * @param {String} url the url of the image
1601 setSrc : function(url)
1605 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1606 this.el.dom.src = url;
1610 this.el.select('img', true).first().dom.src = url;
1626 * @class Roo.bootstrap.Link
1627 * @extends Roo.bootstrap.Component
1628 * Bootstrap Link Class
1629 * @cfg {String} alt image alternative text
1630 * @cfg {String} href a tag href
1631 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1632 * @cfg {String} html the content of the link.
1633 * @cfg {String} anchor name for the anchor link
1634 * @cfg {String} fa - favicon
1636 * @cfg {Boolean} preventDefault (true | false) default false
1640 * Create a new Input
1641 * @param {Object} config The config object
1644 Roo.bootstrap.Link = function(config){
1645 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1651 * The img click event for the img.
1652 * @param {Roo.EventObject} e
1658 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1662 preventDefault: false,
1668 getAutoCreate : function()
1670 var html = this.html || '';
1672 if (this.fa !== false) {
1673 html = '<i class="fa fa-' + this.fa + '"></i>';
1678 // anchor's do not require html/href...
1679 if (this.anchor === false) {
1681 cfg.href = this.href || '#';
1683 cfg.name = this.anchor;
1684 if (this.html !== false || this.fa !== false) {
1687 if (this.href !== false) {
1688 cfg.href = this.href;
1692 if(this.alt !== false){
1697 if(this.target !== false) {
1698 cfg.target = this.target;
1704 initEvents: function() {
1706 if(!this.href || this.preventDefault){
1707 this.el.on('click', this.onClick, this);
1711 onClick : function(e)
1713 if(this.preventDefault){
1716 //Roo.log('img onclick');
1717 this.fireEvent('click', this, e);
1730 * @class Roo.bootstrap.Header
1731 * @extends Roo.bootstrap.Component
1732 * Bootstrap Header class
1733 * @cfg {String} html content of header
1734 * @cfg {Number} level (1|2|3|4|5|6) default 1
1737 * Create a new Header
1738 * @param {Object} config The config object
1742 Roo.bootstrap.Header = function(config){
1743 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1746 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1754 getAutoCreate : function(){
1759 tag: 'h' + (1 *this.level),
1760 html: this.html || ''
1772 * Ext JS Library 1.1.1
1773 * Copyright(c) 2006-2007, Ext JS, LLC.
1775 * Originally Released Under LGPL - original licence link has changed is not relivant.
1778 * <script type="text/javascript">
1782 * @class Roo.bootstrap.MenuMgr
1783 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1786 Roo.bootstrap.MenuMgr = function(){
1787 var menus, active, groups = {}, attached = false, lastShow = new Date();
1789 // private - called when first menu is created
1792 active = new Roo.util.MixedCollection();
1793 Roo.get(document).addKeyListener(27, function(){
1794 if(active.length > 0){
1802 if(active && active.length > 0){
1803 var c = active.clone();
1813 if(active.length < 1){
1814 Roo.get(document).un("mouseup", onMouseDown);
1822 var last = active.last();
1823 lastShow = new Date();
1826 Roo.get(document).on("mouseup", onMouseDown);
1831 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1832 m.parentMenu.activeChild = m;
1833 }else if(last && last.isVisible()){
1834 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1839 function onBeforeHide(m){
1841 m.activeChild.hide();
1843 if(m.autoHideTimer){
1844 clearTimeout(m.autoHideTimer);
1845 delete m.autoHideTimer;
1850 function onBeforeShow(m){
1851 var pm = m.parentMenu;
1852 if(!pm && !m.allowOtherMenus){
1854 }else if(pm && pm.activeChild && active != m){
1855 pm.activeChild.hide();
1859 // private this should really trigger on mouseup..
1860 function onMouseDown(e){
1861 Roo.log("on Mouse Up");
1863 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1864 Roo.log("MenuManager hideAll");
1873 function onBeforeCheck(mi, state){
1875 var g = groups[mi.group];
1876 for(var i = 0, l = g.length; i < l; i++){
1878 g[i].setChecked(false);
1887 * Hides all menus that are currently visible
1889 hideAll : function(){
1894 register : function(menu){
1898 menus[menu.id] = menu;
1899 menu.on("beforehide", onBeforeHide);
1900 menu.on("hide", onHide);
1901 menu.on("beforeshow", onBeforeShow);
1902 menu.on("show", onShow);
1904 if(g && menu.events["checkchange"]){
1908 groups[g].push(menu);
1909 menu.on("checkchange", onCheck);
1914 * Returns a {@link Roo.menu.Menu} object
1915 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1916 * be used to generate and return a new Menu instance.
1918 get : function(menu){
1919 if(typeof menu == "string"){ // menu id
1921 }else if(menu.events){ // menu instance
1924 /*else if(typeof menu.length == 'number'){ // array of menu items?
1925 return new Roo.bootstrap.Menu({items:menu});
1926 }else{ // otherwise, must be a config
1927 return new Roo.bootstrap.Menu(menu);
1934 unregister : function(menu){
1935 delete menus[menu.id];
1936 menu.un("beforehide", onBeforeHide);
1937 menu.un("hide", onHide);
1938 menu.un("beforeshow", onBeforeShow);
1939 menu.un("show", onShow);
1941 if(g && menu.events["checkchange"]){
1942 groups[g].remove(menu);
1943 menu.un("checkchange", onCheck);
1948 registerCheckable : function(menuItem){
1949 var g = menuItem.group;
1954 groups[g].push(menuItem);
1955 menuItem.on("beforecheckchange", onBeforeCheck);
1960 unregisterCheckable : function(menuItem){
1961 var g = menuItem.group;
1963 groups[g].remove(menuItem);
1964 menuItem.un("beforecheckchange", onBeforeCheck);
1976 * @class Roo.bootstrap.Menu
1977 * @extends Roo.bootstrap.Component
1978 * Bootstrap Menu class - container for MenuItems
1979 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1980 * @cfg {bool} hidden if the menu should be hidden when rendered.
1981 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1982 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1986 * @param {Object} config The config object
1990 Roo.bootstrap.Menu = function(config){
1991 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1992 if (this.registerMenu && this.type != 'treeview') {
1993 Roo.bootstrap.MenuMgr.register(this);
1998 * Fires before this menu is displayed
1999 * @param {Roo.menu.Menu} this
2004 * Fires before this menu is hidden
2005 * @param {Roo.menu.Menu} this
2010 * Fires after this menu is displayed
2011 * @param {Roo.menu.Menu} this
2016 * Fires after this menu is hidden
2017 * @param {Roo.menu.Menu} this
2022 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2023 * @param {Roo.menu.Menu} this
2024 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2025 * @param {Roo.EventObject} e
2030 * Fires when the mouse is hovering over this menu
2031 * @param {Roo.menu.Menu} this
2032 * @param {Roo.EventObject} e
2033 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038 * Fires when the mouse exits this menu
2039 * @param {Roo.menu.Menu} this
2040 * @param {Roo.EventObject} e
2041 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2046 * Fires when a menu item contained in this menu is clicked
2047 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2048 * @param {Roo.EventObject} e
2052 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2055 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2059 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2062 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2064 registerMenu : true,
2066 menuItems :false, // stores the menu items..
2076 getChildContainer : function() {
2080 getAutoCreate : function(){
2082 //if (['right'].indexOf(this.align)!==-1) {
2083 // cfg.cn[1].cls += ' pull-right'
2089 cls : 'dropdown-menu' ,
2090 style : 'z-index:1000'
2094 if (this.type === 'submenu') {
2095 cfg.cls = 'submenu active';
2097 if (this.type === 'treeview') {
2098 cfg.cls = 'treeview-menu';
2103 initEvents : function() {
2105 // Roo.log("ADD event");
2106 // Roo.log(this.triggerEl.dom);
2108 this.triggerEl.on('click', this.onTriggerClick, this);
2110 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2112 this.triggerEl.addClass('dropdown-toggle');
2115 this.el.on('touchstart' , this.onTouch, this);
2117 this.el.on('click' , this.onClick, this);
2119 this.el.on("mouseover", this.onMouseOver, this);
2120 this.el.on("mouseout", this.onMouseOut, this);
2124 findTargetItem : function(e)
2126 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2130 //Roo.log(t); Roo.log(t.id);
2132 //Roo.log(this.menuitems);
2133 return this.menuitems.get(t.id);
2135 //return this.items.get(t.menuItemId);
2141 onTouch : function(e)
2143 Roo.log("menu.onTouch");
2144 //e.stopEvent(); this make the user popdown broken
2148 onClick : function(e)
2150 Roo.log("menu.onClick");
2152 var t = this.findTargetItem(e);
2153 if(!t || t.isContainer){
2158 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2159 if(t == this.activeItem && t.shouldDeactivate(e)){
2160 this.activeItem.deactivate();
2161 delete this.activeItem;
2165 this.setActiveItem(t, true);
2173 Roo.log('pass click event');
2177 this.fireEvent("click", this, t, e);
2181 if(!t.href.length || t.href == '#'){
2182 (function() { _this.hide(); }).defer(100);
2187 onMouseOver : function(e){
2188 var t = this.findTargetItem(e);
2191 // if(t.canActivate && !t.disabled){
2192 // this.setActiveItem(t, true);
2196 this.fireEvent("mouseover", this, e, t);
2198 isVisible : function(){
2199 return !this.hidden;
2201 onMouseOut : function(e){
2202 var t = this.findTargetItem(e);
2205 // if(t == this.activeItem && t.shouldDeactivate(e)){
2206 // this.activeItem.deactivate();
2207 // delete this.activeItem;
2210 this.fireEvent("mouseout", this, e, t);
2215 * Displays this menu relative to another element
2216 * @param {String/HTMLElement/Roo.Element} element The element to align to
2217 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2218 * the element (defaults to this.defaultAlign)
2219 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2221 show : function(el, pos, parentMenu){
2222 this.parentMenu = parentMenu;
2226 this.fireEvent("beforeshow", this);
2227 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2230 * Displays this menu at a specific xy position
2231 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2232 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2234 showAt : function(xy, parentMenu, /* private: */_e){
2235 this.parentMenu = parentMenu;
2240 this.fireEvent("beforeshow", this);
2241 //xy = this.el.adjustForConstraints(xy);
2245 this.hideMenuItems();
2246 this.hidden = false;
2247 this.triggerEl.addClass('open');
2249 // reassign x when hitting right
2250 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2251 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2254 // reassign y when hitting bottom
2255 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2256 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2259 // but the list may align on trigger left or trigger top... should it be a properity?
2261 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2266 this.fireEvent("show", this);
2272 this.doFocus.defer(50, this);
2276 doFocus : function(){
2278 this.focusEl.focus();
2283 * Hides this menu and optionally all parent menus
2284 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2286 hide : function(deep)
2289 this.hideMenuItems();
2290 if(this.el && this.isVisible()){
2291 this.fireEvent("beforehide", this);
2292 if(this.activeItem){
2293 this.activeItem.deactivate();
2294 this.activeItem = null;
2296 this.triggerEl.removeClass('open');;
2298 this.fireEvent("hide", this);
2300 if(deep === true && this.parentMenu){
2301 this.parentMenu.hide(true);
2305 onTriggerClick : function(e)
2307 Roo.log('trigger click');
2309 var target = e.getTarget();
2311 Roo.log(target.nodeName.toLowerCase());
2313 if(target.nodeName.toLowerCase() === 'i'){
2319 onTriggerPress : function(e)
2321 Roo.log('trigger press');
2322 //Roo.log(e.getTarget());
2323 // Roo.log(this.triggerEl.dom);
2325 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2326 var pel = Roo.get(e.getTarget());
2327 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2328 Roo.log('is treeview or dropdown?');
2332 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2336 if (this.isVisible()) {
2341 this.show(this.triggerEl, false, false);
2344 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2351 hideMenuItems : function()
2353 Roo.log("hide Menu Items");
2357 //$(backdrop).remove()
2358 this.el.select('.open',true).each(function(aa) {
2360 aa.removeClass('open');
2361 //var parent = getParent($(this))
2362 //var relatedTarget = { relatedTarget: this }
2364 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2365 //if (e.isDefaultPrevented()) return
2366 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2369 addxtypeChild : function (tree, cntr) {
2370 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2372 this.menuitems.add(comp);
2384 this.getEl().dom.innerHTML = '';
2385 this.menuitems.clear();
2399 * @class Roo.bootstrap.MenuItem
2400 * @extends Roo.bootstrap.Component
2401 * Bootstrap MenuItem class
2402 * @cfg {String} html the menu label
2403 * @cfg {String} href the link
2404 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2405 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2406 * @cfg {Boolean} active used on sidebars to highlight active itesm
2407 * @cfg {String} fa favicon to show on left of menu item.
2408 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2412 * Create a new MenuItem
2413 * @param {Object} config The config object
2417 Roo.bootstrap.MenuItem = function(config){
2418 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2423 * The raw click event for the entire grid.
2424 * @param {Roo.bootstrap.MenuItem} this
2425 * @param {Roo.EventObject} e
2431 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2435 preventDefault: false,
2436 isContainer : false,
2440 getAutoCreate : function(){
2442 if(this.isContainer){
2445 cls: 'dropdown-menu-item'
2459 if (this.fa !== false) {
2462 cls : 'fa fa-' + this.fa
2471 cls: 'dropdown-menu-item',
2474 if (this.parent().type == 'treeview') {
2475 cfg.cls = 'treeview-menu';
2478 cfg.cls += ' active';
2483 anc.href = this.href || cfg.cn[0].href ;
2484 ctag.html = this.html || cfg.cn[0].html ;
2488 initEvents: function()
2490 if (this.parent().type == 'treeview') {
2491 this.el.select('a').on('click', this.onClick, this);
2495 this.menu.parentType = this.xtype;
2496 this.menu.triggerEl = this.el;
2497 this.menu = this.addxtype(Roo.apply({}, this.menu));
2501 onClick : function(e)
2503 Roo.log('item on click ');
2505 if(this.preventDefault){
2508 //this.parent().hideMenuItems();
2510 this.fireEvent('click', this, e);
2529 * @class Roo.bootstrap.MenuSeparator
2530 * @extends Roo.bootstrap.Component
2531 * Bootstrap MenuSeparator class
2534 * Create a new MenuItem
2535 * @param {Object} config The config object
2539 Roo.bootstrap.MenuSeparator = function(config){
2540 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2543 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2545 getAutoCreate : function(){
2564 * @class Roo.bootstrap.Modal
2565 * @extends Roo.bootstrap.Component
2566 * Bootstrap Modal class
2567 * @cfg {String} title Title of dialog
2568 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2569 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2570 * @cfg {Boolean} specificTitle default false
2571 * @cfg {Array} buttons Array of buttons or standard button set..
2572 * @cfg {String} buttonPosition (left|right|center) default right
2573 * @cfg {Boolean} animate default true
2574 * @cfg {Boolean} allow_close default true
2575 * @cfg {Boolean} fitwindow default false
2576 * @cfg {String} size (sm|lg) default empty
2580 * Create a new Modal Dialog
2581 * @param {Object} config The config object
2584 Roo.bootstrap.Modal = function(config){
2585 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2590 * The raw btnclick event for the button
2591 * @param {Roo.EventObject} e
2596 * Fire when dialog resize
2597 * @param {Roo.bootstrap.Modal} this
2598 * @param {Roo.EventObject} e
2602 this.buttons = this.buttons || [];
2605 this.tmpl = Roo.factory(this.tmpl);
2610 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2612 title : 'test dialog',
2622 specificTitle: false,
2624 buttonPosition: 'right',
2643 onRender : function(ct, position)
2645 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2648 var cfg = Roo.apply({}, this.getAutoCreate());
2651 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653 //if (!cfg.name.length) {
2657 cfg.cls += ' ' + this.cls;
2660 cfg.style = this.style;
2662 this.el = Roo.get(document.body).createChild(cfg, position);
2664 //var type = this.el.dom.type;
2667 if(this.tabIndex !== undefined){
2668 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2671 this.dialogEl = this.el.select('.modal-dialog',true).first();
2672 this.bodyEl = this.el.select('.modal-body',true).first();
2673 this.closeEl = this.el.select('.modal-header .close', true).first();
2674 this.headerEl = this.el.select('.modal-header',true).first();
2675 this.titleEl = this.el.select('.modal-title',true).first();
2676 this.footerEl = this.el.select('.modal-footer',true).first();
2678 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680 //this.el.addClass("x-dlg-modal");
2682 if (this.buttons.length) {
2683 Roo.each(this.buttons, function(bb) {
2684 var b = Roo.apply({}, bb);
2685 b.xns = b.xns || Roo.bootstrap;
2686 b.xtype = b.xtype || 'Button';
2687 if (typeof(b.listeners) == 'undefined') {
2688 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2691 var btn = Roo.factory(b);
2693 btn.render(this.el.select('.modal-footer div').first());
2697 // render the children.
2700 if(typeof(this.items) != 'undefined'){
2701 var items = this.items;
2704 for(var i =0;i < items.length;i++) {
2705 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2709 this.items = nitems;
2711 // where are these used - they used to be body/close/footer
2715 //this.el.addClass([this.fieldClass, this.cls]);
2719 getAutoCreate : function(){
2724 html : this.html || ''
2729 cls : 'modal-title',
2733 if(this.specificTitle){
2739 if (this.allow_close) {
2751 if(this.size.length){
2752 size = 'modal-' + this.size;
2759 cls: "modal-dialog " + size,
2762 cls : "modal-content",
2765 cls : 'modal-header',
2770 cls : 'modal-footer',
2774 cls: 'btn-' + this.buttonPosition
2791 modal.cls += ' fade';
2797 getChildContainer : function() {
2802 getButtonContainer : function() {
2803 return this.el.select('.modal-footer div',true).first();
2806 initEvents : function()
2808 if (this.allow_close) {
2809 this.closeEl.on('click', this.hide, this);
2811 Roo.EventManager.onWindowResize(this.resize, this, true);
2818 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2819 if (this.fitwindow) {
2820 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2821 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2826 setSize : function(w,h)
2836 if (!this.rendered) {
2840 //this.el.setStyle('display', 'block');
2841 this.el.removeClass('hideing');
2842 this.el.addClass('show');
2844 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2847 this.el.addClass('in');
2850 this.el.addClass('in');
2854 // not sure how we can show data in here..
2856 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2859 Roo.get(document.body).addClass("x-body-masked");
2861 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2862 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2863 this.maskEl.addClass('show');
2867 this.fireEvent('show', this);
2869 // set zindex here - otherwise it appears to be ignored...
2870 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2873 this.items.forEach( function(e) {
2874 e.layout ? e.layout() : false;
2882 if(this.fireEvent("beforehide", this) !== false){
2883 this.maskEl.removeClass('show');
2884 Roo.get(document.body).removeClass("x-body-masked");
2885 this.el.removeClass('in');
2886 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2888 if(this.animate){ // why
2889 this.el.addClass('hideing');
2891 if (!this.el.hasClass('hideing')) {
2892 return; // it's been shown again...
2894 this.el.removeClass('show');
2895 this.el.removeClass('hideing');
2899 this.el.removeClass('show');
2901 this.fireEvent('hide', this);
2904 isVisible : function()
2907 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2911 addButton : function(str, cb)
2915 var b = Roo.apply({}, { html : str } );
2916 b.xns = b.xns || Roo.bootstrap;
2917 b.xtype = b.xtype || 'Button';
2918 if (typeof(b.listeners) == 'undefined') {
2919 b.listeners = { click : cb.createDelegate(this) };
2922 var btn = Roo.factory(b);
2924 btn.render(this.el.select('.modal-footer div').first());
2930 setDefaultButton : function(btn)
2932 //this.el.select('.modal-footer').()
2936 resizeTo: function(w,h)
2940 this.dialogEl.setWidth(w);
2941 if (this.diff === false) {
2942 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2945 this.bodyEl.setHeight(h-this.diff);
2947 this.fireEvent('resize', this);
2950 setContentSize : function(w, h)
2954 onButtonClick: function(btn,e)
2957 this.fireEvent('btnclick', btn.name, e);
2960 * Set the title of the Dialog
2961 * @param {String} str new Title
2963 setTitle: function(str) {
2964 this.titleEl.dom.innerHTML = str;
2967 * Set the body of the Dialog
2968 * @param {String} str new Title
2970 setBody: function(str) {
2971 this.bodyEl.dom.innerHTML = str;
2974 * Set the body of the Dialog using the template
2975 * @param {Obj} data - apply this data to the template and replace the body contents.
2977 applyBody: function(obj)
2980 Roo.log("Error - using apply Body without a template");
2983 this.tmpl.overwrite(this.bodyEl, obj);
2989 Roo.apply(Roo.bootstrap.Modal, {
2991 * Button config that displays a single OK button
3000 * Button config that displays Yes and No buttons
3016 * Button config that displays OK and Cancel buttons
3031 * Button config that displays Yes, No and Cancel buttons
3055 * messagebox - can be used as a replace
3059 * @class Roo.MessageBox
3060 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3064 Roo.Msg.alert('Status', 'Changes saved successfully.');
3066 // Prompt for user data:
3067 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3069 // process text value...
3073 // Show a dialog using config options:
3075 title:'Save Changes?',
3076 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3077 buttons: Roo.Msg.YESNOCANCEL,
3084 Roo.bootstrap.MessageBox = function(){
3085 var dlg, opt, mask, waitTimer;
3086 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3087 var buttons, activeTextEl, bwidth;
3091 var handleButton = function(button){
3093 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3097 var handleHide = function(){
3099 dlg.el.removeClass(opt.cls);
3102 // Roo.TaskMgr.stop(waitTimer);
3103 // waitTimer = null;
3108 var updateButtons = function(b){
3111 buttons["ok"].hide();
3112 buttons["cancel"].hide();
3113 buttons["yes"].hide();
3114 buttons["no"].hide();
3115 //dlg.footer.dom.style.display = 'none';
3118 dlg.footerEl.dom.style.display = '';
3119 for(var k in buttons){
3120 if(typeof buttons[k] != "function"){
3123 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3124 width += buttons[k].el.getWidth()+15;
3134 var handleEsc = function(d, k, e){
3135 if(opt && opt.closable !== false){
3145 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3146 * @return {Roo.BasicDialog} The BasicDialog element
3148 getDialog : function(){
3150 dlg = new Roo.bootstrap.Modal( {
3153 //constraintoviewport:false,
3155 //collapsible : false,
3160 //buttonAlign:"center",
3161 closeClick : function(){
3162 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3165 handleButton("cancel");
3170 dlg.on("hide", handleHide);
3172 //dlg.addKeyListener(27, handleEsc);
3174 this.buttons = buttons;
3175 var bt = this.buttonText;
3176 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3177 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3178 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3179 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3181 bodyEl = dlg.bodyEl.createChild({
3183 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3184 '<textarea class="roo-mb-textarea"></textarea>' +
3185 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3187 msgEl = bodyEl.dom.firstChild;
3188 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3189 textboxEl.enableDisplayMode();
3190 textboxEl.addKeyListener([10,13], function(){
3191 if(dlg.isVisible() && opt && opt.buttons){
3194 }else if(opt.buttons.yes){
3195 handleButton("yes");
3199 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3200 textareaEl.enableDisplayMode();
3201 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3202 progressEl.enableDisplayMode();
3204 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3205 var pf = progressEl.dom.firstChild;
3207 pp = Roo.get(pf.firstChild);
3208 pp.setHeight(pf.offsetHeight);
3216 * Updates the message box body text
3217 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3218 * the XHTML-compliant non-breaking space character '&#160;')
3219 * @return {Roo.MessageBox} This message box
3221 updateText : function(text)
3223 if(!dlg.isVisible() && !opt.width){
3224 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3225 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3227 msgEl.innerHTML = text || ' ';
3229 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3230 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3232 Math.min(opt.width || cw , this.maxWidth),
3233 Math.max(opt.minWidth || this.minWidth, bwidth)
3236 activeTextEl.setWidth(w);
3238 if(dlg.isVisible()){
3239 dlg.fixedcenter = false;
3241 // to big, make it scroll. = But as usual stupid IE does not support
3244 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3245 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3246 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3248 bodyEl.dom.style.height = '';
3249 bodyEl.dom.style.overflowY = '';
3252 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3254 bodyEl.dom.style.overflowX = '';
3257 dlg.setContentSize(w, bodyEl.getHeight());
3258 if(dlg.isVisible()){
3259 dlg.fixedcenter = true;
3265 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3266 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3267 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3268 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3269 * @return {Roo.MessageBox} This message box
3271 updateProgress : function(value, text){
3273 this.updateText(text);
3276 if (pp) { // weird bug on my firefox - for some reason this is not defined
3277 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3278 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3284 * Returns true if the message box is currently displayed
3285 * @return {Boolean} True if the message box is visible, else false
3287 isVisible : function(){
3288 return dlg && dlg.isVisible();
3292 * Hides the message box if it is displayed
3295 if(this.isVisible()){
3301 * Displays a new message box, or reinitializes an existing message box, based on the config options
3302 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3303 * The following config object properties are supported:
3305 Property Type Description
3306 ---------- --------------- ------------------------------------------------------------------------------------
3307 animEl String/Element An id or Element from which the message box should animate as it opens and
3308 closes (defaults to undefined)
3309 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3310 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3311 closable Boolean False to hide the top-right close button (defaults to true). Note that
3312 progress and wait dialogs will ignore this property and always hide the
3313 close button as they can only be closed programmatically.
3314 cls String A custom CSS class to apply to the message box element
3315 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3316 displayed (defaults to 75)
3317 fn Function A callback function to execute after closing the dialog. The arguments to the
3318 function will be btn (the name of the button that was clicked, if applicable,
3319 e.g. "ok"), and text (the value of the active text field, if applicable).
3320 Progress and wait dialogs will ignore this option since they do not respond to
3321 user actions and can only be closed programmatically, so any required function
3322 should be called by the same code after it closes the dialog.
3323 icon String A CSS class that provides a background image to be used as an icon for
3324 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3325 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3326 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3327 modal Boolean False to allow user interaction with the page while the message box is
3328 displayed (defaults to true)
3329 msg String A string that will replace the existing message box body text (defaults
3330 to the XHTML-compliant non-breaking space character ' ')
3331 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3332 progress Boolean True to display a progress bar (defaults to false)
3333 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3334 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3335 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3336 title String The title text
3337 value String The string value to set into the active textbox element if displayed
3338 wait Boolean True to display a progress bar (defaults to false)
3339 width Number The width of the dialog in pixels
3346 msg: 'Please enter your address:',
3348 buttons: Roo.MessageBox.OKCANCEL,
3351 animEl: 'addAddressBtn'
3354 * @param {Object} config Configuration options
3355 * @return {Roo.MessageBox} This message box
3357 show : function(options)
3360 // this causes nightmares if you show one dialog after another
3361 // especially on callbacks..
3363 if(this.isVisible()){
3366 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3367 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3368 Roo.log("New Dialog Message:" + options.msg )
3369 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3370 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3373 var d = this.getDialog();
3375 d.setTitle(opt.title || " ");
3376 d.closeEl.setDisplayed(opt.closable !== false);
3377 activeTextEl = textboxEl;
3378 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3383 textareaEl.setHeight(typeof opt.multiline == "number" ?
3384 opt.multiline : this.defaultTextHeight);
3385 activeTextEl = textareaEl;
3394 progressEl.setDisplayed(opt.progress === true);
3395 this.updateProgress(0);
3396 activeTextEl.dom.value = opt.value || "";
3398 dlg.setDefaultButton(activeTextEl);
3400 var bs = opt.buttons;
3404 }else if(bs && bs.yes){
3405 db = buttons["yes"];
3407 dlg.setDefaultButton(db);
3409 bwidth = updateButtons(opt.buttons);
3410 this.updateText(opt.msg);
3412 d.el.addClass(opt.cls);
3414 d.proxyDrag = opt.proxyDrag === true;
3415 d.modal = opt.modal !== false;
3416 d.mask = opt.modal !== false ? mask : false;
3418 // force it to the end of the z-index stack so it gets a cursor in FF
3419 document.body.appendChild(dlg.el.dom);
3420 d.animateTarget = null;
3421 d.show(options.animEl);
3427 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3428 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3429 * and closing the message box when the process is complete.
3430 * @param {String} title The title bar text
3431 * @param {String} msg The message box body text
3432 * @return {Roo.MessageBox} This message box
3434 progress : function(title, msg){
3441 minWidth: this.minProgressWidth,
3448 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3449 * If a callback function is passed it will be called after the user clicks the button, and the
3450 * id of the button that was clicked will be passed as the only parameter to the callback
3451 * (could also be the top-right close button).
3452 * @param {String} title The title bar text
3453 * @param {String} msg The message box body text
3454 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3455 * @param {Object} scope (optional) The scope of the callback function
3456 * @return {Roo.MessageBox} This message box
3458 alert : function(title, msg, fn, scope)
3473 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3474 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3475 * You are responsible for closing the message box when the process is complete.
3476 * @param {String} msg The message box body text
3477 * @param {String} title (optional) The title bar text
3478 * @return {Roo.MessageBox} This message box
3480 wait : function(msg, title){
3491 waitTimer = Roo.TaskMgr.start({
3493 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3501 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3502 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3503 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3504 * @param {String} title The title bar text
3505 * @param {String} msg The message box body text
3506 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3507 * @param {Object} scope (optional) The scope of the callback function
3508 * @return {Roo.MessageBox} This message box
3510 confirm : function(title, msg, fn, scope){
3514 buttons: this.YESNO,
3523 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3524 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3525 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3526 * (could also be the top-right close button) and the text that was entered will be passed as the two
3527 * parameters to the callback.
3528 * @param {String} title The title bar text
3529 * @param {String} msg The message box body text
3530 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3531 * @param {Object} scope (optional) The scope of the callback function
3532 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3533 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3534 * @return {Roo.MessageBox} This message box
3536 prompt : function(title, msg, fn, scope, multiline){
3540 buttons: this.OKCANCEL,
3545 multiline: multiline,
3552 * Button config that displays a single OK button
3557 * Button config that displays Yes and No buttons
3560 YESNO : {yes:true, no:true},
3562 * Button config that displays OK and Cancel buttons
3565 OKCANCEL : {ok:true, cancel:true},
3567 * Button config that displays Yes, No and Cancel buttons
3570 YESNOCANCEL : {yes:true, no:true, cancel:true},
3573 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3576 defaultTextHeight : 75,
3578 * The maximum width in pixels of the message box (defaults to 600)
3583 * The minimum width in pixels of the message box (defaults to 100)
3588 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3589 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3592 minProgressWidth : 250,
3594 * An object containing the default button text strings that can be overriden for localized language support.
3595 * Supported properties are: ok, cancel, yes and no.
3596 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3609 * Shorthand for {@link Roo.MessageBox}
3611 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3612 Roo.Msg = Roo.Msg || Roo.MessageBox;
3621 * @class Roo.bootstrap.Navbar
3622 * @extends Roo.bootstrap.Component
3623 * Bootstrap Navbar class
3626 * Create a new Navbar
3627 * @param {Object} config The config object
3631 Roo.bootstrap.Navbar = function(config){
3632 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3636 * @event beforetoggle
3637 * Fire before toggle the menu
3638 * @param {Roo.EventObject} e
3640 "beforetoggle" : true
3644 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3653 getAutoCreate : function(){
3656 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3660 initEvents :function ()
3662 //Roo.log(this.el.select('.navbar-toggle',true));
3663 this.el.select('.navbar-toggle',true).on('click', function() {
3664 if(this.fireEvent('beforetoggle', this) !== false){
3665 this.el.select('.navbar-collapse',true).toggleClass('in');
3675 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3677 var size = this.el.getSize();
3678 this.maskEl.setSize(size.width, size.height);
3679 this.maskEl.enableDisplayMode("block");
3688 getChildContainer : function()
3690 if (this.el.select('.collapse').getCount()) {
3691 return this.el.select('.collapse',true).first();
3724 * @class Roo.bootstrap.NavSimplebar
3725 * @extends Roo.bootstrap.Navbar
3726 * Bootstrap Sidebar class
3728 * @cfg {Boolean} inverse is inverted color
3730 * @cfg {String} type (nav | pills | tabs)
3731 * @cfg {Boolean} arrangement stacked | justified
3732 * @cfg {String} align (left | right) alignment
3734 * @cfg {Boolean} main (true|false) main nav bar? default false
3735 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3737 * @cfg {String} tag (header|footer|nav|div) default is nav
3743 * Create a new Sidebar
3744 * @param {Object} config The config object
3748 Roo.bootstrap.NavSimplebar = function(config){
3749 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3752 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3768 getAutoCreate : function(){
3772 tag : this.tag || 'div',
3785 this.type = this.type || 'nav';
3786 if (['tabs','pills'].indexOf(this.type)!==-1) {
3787 cfg.cn[0].cls += ' nav-' + this.type
3791 if (this.type!=='nav') {
3792 Roo.log('nav type must be nav/tabs/pills')
3794 cfg.cn[0].cls += ' navbar-nav'
3800 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3801 cfg.cn[0].cls += ' nav-' + this.arrangement;
3805 if (this.align === 'right') {
3806 cfg.cn[0].cls += ' navbar-right';
3810 cfg.cls += ' navbar-inverse';
3837 * @class Roo.bootstrap.NavHeaderbar
3838 * @extends Roo.bootstrap.NavSimplebar
3839 * Bootstrap Sidebar class
3841 * @cfg {String} brand what is brand
3842 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3843 * @cfg {String} brand_href href of the brand
3844 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3845 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3846 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3847 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3850 * Create a new Sidebar
3851 * @param {Object} config The config object
3855 Roo.bootstrap.NavHeaderbar = function(config){
3856 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3860 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3867 desktopCenter : false,
3870 getAutoCreate : function(){
3873 tag: this.nav || 'nav',
3880 if (this.desktopCenter) {
3881 cn.push({cls : 'container', cn : []});
3888 cls: 'navbar-header',
3893 cls: 'navbar-toggle',
3894 'data-toggle': 'collapse',
3899 html: 'Toggle navigation'
3921 cls: 'collapse navbar-collapse',
3925 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3927 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3928 cfg.cls += ' navbar-' + this.position;
3930 // tag can override this..
3932 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3935 if (this.brand !== '') {
3938 href: this.brand_href ? this.brand_href : '#',
3939 cls: 'navbar-brand',
3947 cfg.cls += ' main-nav';
3955 getHeaderChildContainer : function()
3957 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3958 return this.el.select('.navbar-header',true).first();
3961 return this.getChildContainer();
3965 initEvents : function()
3967 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3969 if (this.autohide) {
3974 Roo.get(document).on('scroll',function(e) {
3975 var ns = Roo.get(document).getScroll().top;
3976 var os = prevScroll;
3980 ft.removeClass('slideDown');
3981 ft.addClass('slideUp');
3984 ft.removeClass('slideUp');
3985 ft.addClass('slideDown');
4006 * @class Roo.bootstrap.NavSidebar
4007 * @extends Roo.bootstrap.Navbar
4008 * Bootstrap Sidebar class
4011 * Create a new Sidebar
4012 * @param {Object} config The config object
4016 Roo.bootstrap.NavSidebar = function(config){
4017 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4020 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4022 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4024 getAutoCreate : function(){
4029 cls: 'sidebar sidebar-nav'
4051 * @class Roo.bootstrap.NavGroup
4052 * @extends Roo.bootstrap.Component
4053 * Bootstrap NavGroup class
4054 * @cfg {String} align (left|right)
4055 * @cfg {Boolean} inverse
4056 * @cfg {String} type (nav|pills|tab) default nav
4057 * @cfg {String} navId - reference Id for navbar.
4061 * Create a new nav group
4062 * @param {Object} config The config object
4065 Roo.bootstrap.NavGroup = function(config){
4066 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4069 Roo.bootstrap.NavGroup.register(this);
4073 * Fires when the active item changes
4074 * @param {Roo.bootstrap.NavGroup} this
4075 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4076 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4083 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4094 getAutoCreate : function()
4096 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4103 if (['tabs','pills'].indexOf(this.type)!==-1) {
4104 cfg.cls += ' nav-' + this.type
4106 if (this.type!=='nav') {
4107 Roo.log('nav type must be nav/tabs/pills')
4109 cfg.cls += ' navbar-nav'
4112 if (this.parent() && this.parent().sidebar) {
4115 cls: 'dashboard-menu sidebar-menu'
4121 if (this.form === true) {
4127 if (this.align === 'right') {
4128 cfg.cls += ' navbar-right';
4130 cfg.cls += ' navbar-left';
4134 if (this.align === 'right') {
4135 cfg.cls += ' navbar-right';
4139 cfg.cls += ' navbar-inverse';
4147 * sets the active Navigation item
4148 * @param {Roo.bootstrap.NavItem} the new current navitem
4150 setActiveItem : function(item)
4153 Roo.each(this.navItems, function(v){
4158 v.setActive(false, true);
4165 item.setActive(true, true);
4166 this.fireEvent('changed', this, item, prev);
4171 * gets the active Navigation item
4172 * @return {Roo.bootstrap.NavItem} the current navitem
4174 getActive : function()
4178 Roo.each(this.navItems, function(v){
4189 indexOfNav : function()
4193 Roo.each(this.navItems, function(v,i){
4204 * adds a Navigation item
4205 * @param {Roo.bootstrap.NavItem} the navitem to add
4207 addItem : function(cfg)
4209 var cn = new Roo.bootstrap.NavItem(cfg);
4211 cn.parentId = this.id;
4212 cn.onRender(this.el, null);
4216 * register a Navigation item
4217 * @param {Roo.bootstrap.NavItem} the navitem to add
4219 register : function(item)
4221 this.navItems.push( item);
4222 item.navId = this.navId;
4227 * clear all the Navigation item
4230 clearAll : function()
4233 this.el.dom.innerHTML = '';
4236 getNavItem: function(tabId)
4239 Roo.each(this.navItems, function(e) {
4240 if (e.tabId == tabId) {
4250 setActiveNext : function()
4252 var i = this.indexOfNav(this.getActive());
4253 if (i > this.navItems.length) {
4256 this.setActiveItem(this.navItems[i+1]);
4258 setActivePrev : function()
4260 var i = this.indexOfNav(this.getActive());
4264 this.setActiveItem(this.navItems[i-1]);
4266 clearWasActive : function(except) {
4267 Roo.each(this.navItems, function(e) {
4268 if (e.tabId != except.tabId && e.was_active) {
4269 e.was_active = false;
4276 getWasActive : function ()
4279 Roo.each(this.navItems, function(e) {
4294 Roo.apply(Roo.bootstrap.NavGroup, {
4298 * register a Navigation Group
4299 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4301 register : function(navgrp)
4303 this.groups[navgrp.navId] = navgrp;
4307 * fetch a Navigation Group based on the navigation ID
4308 * @param {string} the navgroup to add
4309 * @returns {Roo.bootstrap.NavGroup} the navgroup
4311 get: function(navId) {
4312 if (typeof(this.groups[navId]) == 'undefined') {
4314 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4316 return this.groups[navId] ;
4331 * @class Roo.bootstrap.NavItem
4332 * @extends Roo.bootstrap.Component
4333 * Bootstrap Navbar.NavItem class
4334 * @cfg {String} href link to
4335 * @cfg {String} html content of button
4336 * @cfg {String} badge text inside badge
4337 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4338 * @cfg {String} glyphicon name of glyphicon
4339 * @cfg {String} icon name of font awesome icon
4340 * @cfg {Boolean} active Is item active
4341 * @cfg {Boolean} disabled Is item disabled
4343 * @cfg {Boolean} preventDefault (true | false) default false
4344 * @cfg {String} tabId the tab that this item activates.
4345 * @cfg {String} tagtype (a|span) render as a href or span?
4346 * @cfg {Boolean} animateRef (true|false) link to element default false
4349 * Create a new Navbar Item
4350 * @param {Object} config The config object
4352 Roo.bootstrap.NavItem = function(config){
4353 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4358 * The raw click event for the entire grid.
4359 * @param {Roo.EventObject} e
4364 * Fires when the active item active state changes
4365 * @param {Roo.bootstrap.NavItem} this
4366 * @param {boolean} state the new state
4372 * Fires when scroll to element
4373 * @param {Roo.bootstrap.NavItem} this
4374 * @param {Object} options
4375 * @param {Roo.EventObject} e
4383 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4391 preventDefault : false,
4398 getAutoCreate : function(){
4407 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4409 if (this.disabled) {
4410 cfg.cls += ' disabled';
4413 if (this.href || this.html || this.glyphicon || this.icon) {
4417 href : this.href || "#",
4418 html: this.html || ''
4423 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4426 if(this.glyphicon) {
4427 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4432 cfg.cn[0].html += " <span class='caret'></span>";
4436 if (this.badge !== '') {
4438 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4446 initEvents: function()
4448 if (typeof (this.menu) != 'undefined') {
4449 this.menu.parentType = this.xtype;
4450 this.menu.triggerEl = this.el;
4451 this.menu = this.addxtype(Roo.apply({}, this.menu));
4454 this.el.select('a',true).on('click', this.onClick, this);
4456 if(this.tagtype == 'span'){
4457 this.el.select('span',true).on('click', this.onClick, this);
4460 // at this point parent should be available..
4461 this.parent().register(this);
4464 onClick : function(e)
4466 if (e.getTarget('.dropdown-menu-item')) {
4467 // did you click on a menu itemm.... - then don't trigger onclick..
4472 this.preventDefault ||
4475 Roo.log("NavItem - prevent Default?");
4479 if (this.disabled) {
4483 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4484 if (tg && tg.transition) {
4485 Roo.log("waiting for the transitionend");
4491 //Roo.log("fire event clicked");
4492 if(this.fireEvent('click', this, e) === false){
4496 if(this.tagtype == 'span'){
4500 //Roo.log(this.href);
4501 var ael = this.el.select('a',true).first();
4504 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4505 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4506 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4507 return; // ignore... - it's a 'hash' to another page.
4509 Roo.log("NavItem - prevent Default?");
4511 this.scrollToElement(e);
4515 var p = this.parent();
4517 if (['tabs','pills'].indexOf(p.type)!==-1) {
4518 if (typeof(p.setActiveItem) !== 'undefined') {
4519 p.setActiveItem(this);
4523 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4524 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4525 // remove the collapsed menu expand...
4526 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4530 isActive: function () {
4533 setActive : function(state, fire, is_was_active)
4535 if (this.active && !state && this.navId) {
4536 this.was_active = true;
4537 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4539 nv.clearWasActive(this);
4543 this.active = state;
4546 this.el.removeClass('active');
4547 } else if (!this.el.hasClass('active')) {
4548 this.el.addClass('active');
4551 this.fireEvent('changed', this, state);
4554 // show a panel if it's registered and related..
4556 if (!this.navId || !this.tabId || !state || is_was_active) {
4560 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4564 var pan = tg.getPanelByName(this.tabId);
4568 // if we can not flip to new panel - go back to old nav highlight..
4569 if (false == tg.showPanel(pan)) {
4570 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4572 var onav = nv.getWasActive();
4574 onav.setActive(true, false, true);
4583 // this should not be here...
4584 setDisabled : function(state)
4586 this.disabled = state;
4588 this.el.removeClass('disabled');
4589 } else if (!this.el.hasClass('disabled')) {
4590 this.el.addClass('disabled');
4596 * Fetch the element to display the tooltip on.
4597 * @return {Roo.Element} defaults to this.el
4599 tooltipEl : function()
4601 return this.el.select('' + this.tagtype + '', true).first();
4604 scrollToElement : function(e)
4606 var c = document.body;
4609 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4611 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4612 c = document.documentElement;
4615 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4621 var o = target.calcOffsetsTo(c);
4628 this.fireEvent('scrollto', this, options, e);
4630 Roo.get(c).scrollTo('top', options.value, true);
4643 * <span> icon </span>
4644 * <span> text </span>
4645 * <span>badge </span>
4649 * @class Roo.bootstrap.NavSidebarItem
4650 * @extends Roo.bootstrap.NavItem
4651 * Bootstrap Navbar.NavSidebarItem class
4652 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4653 * {Boolean} open is the menu open
4654 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4655 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4656 * {String} buttonSize (sm|md|lg)the extra classes for the button
4657 * {Boolean} showArrow show arrow next to the text (default true)
4659 * Create a new Navbar Button
4660 * @param {Object} config The config object
4662 Roo.bootstrap.NavSidebarItem = function(config){
4663 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4668 * The raw click event for the entire grid.
4669 * @param {Roo.EventObject} e
4674 * Fires when the active item active state changes
4675 * @param {Roo.bootstrap.NavSidebarItem} this
4676 * @param {boolean} state the new state
4684 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4686 badgeWeight : 'default',
4692 buttonWeight : 'default',
4698 getAutoCreate : function(){
4703 href : this.href || '#',
4709 if(this.buttonView){
4712 href : this.href || '#',
4713 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4726 cfg.cls += ' active';
4729 if (this.disabled) {
4730 cfg.cls += ' disabled';
4733 cfg.cls += ' open x-open';
4736 if (this.glyphicon || this.icon) {
4737 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4738 a.cn.push({ tag : 'i', cls : c }) ;
4741 if(!this.buttonView){
4744 html : this.html || ''
4751 if (this.badge !== '') {
4752 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4758 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4761 a.cls += ' dropdown-toggle treeview' ;
4767 initEvents : function()
4769 if (typeof (this.menu) != 'undefined') {
4770 this.menu.parentType = this.xtype;
4771 this.menu.triggerEl = this.el;
4772 this.menu = this.addxtype(Roo.apply({}, this.menu));
4775 this.el.on('click', this.onClick, this);
4777 if(this.badge !== ''){
4778 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4783 onClick : function(e)
4790 if(this.preventDefault){
4794 this.fireEvent('click', this);
4797 disable : function()
4799 this.setDisabled(true);
4804 this.setDisabled(false);
4807 setDisabled : function(state)
4809 if(this.disabled == state){
4813 this.disabled = state;
4816 this.el.addClass('disabled');
4820 this.el.removeClass('disabled');
4825 setActive : function(state)
4827 if(this.active == state){
4831 this.active = state;
4834 this.el.addClass('active');
4838 this.el.removeClass('active');
4843 isActive: function ()
4848 setBadge : function(str)
4854 this.badgeEl.dom.innerHTML = str;
4871 * @class Roo.bootstrap.Row
4872 * @extends Roo.bootstrap.Component
4873 * Bootstrap Row class (contains columns...)
4877 * @param {Object} config The config object
4880 Roo.bootstrap.Row = function(config){
4881 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4884 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4886 getAutoCreate : function(){
4905 * @class Roo.bootstrap.Element
4906 * @extends Roo.bootstrap.Component
4907 * Bootstrap Element class
4908 * @cfg {String} html contents of the element
4909 * @cfg {String} tag tag of the element
4910 * @cfg {String} cls class of the element
4911 * @cfg {Boolean} preventDefault (true|false) default false
4912 * @cfg {Boolean} clickable (true|false) default false
4915 * Create a new Element
4916 * @param {Object} config The config object
4919 Roo.bootstrap.Element = function(config){
4920 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4926 * When a element is chick
4927 * @param {Roo.bootstrap.Element} this
4928 * @param {Roo.EventObject} e
4934 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4939 preventDefault: false,
4942 getAutoCreate : function(){
4946 // cls: this.cls, double assign in parent class Component.js :: onRender
4953 initEvents: function()
4955 Roo.bootstrap.Element.superclass.initEvents.call(this);
4958 this.el.on('click', this.onClick, this);
4963 onClick : function(e)
4965 if(this.preventDefault){
4969 this.fireEvent('click', this, e);
4972 getValue : function()
4974 return this.el.dom.innerHTML;
4977 setValue : function(value)
4979 this.el.dom.innerHTML = value;
4994 * @class Roo.bootstrap.Pagination
4995 * @extends Roo.bootstrap.Component
4996 * Bootstrap Pagination class
4997 * @cfg {String} size xs | sm | md | lg
4998 * @cfg {Boolean} inverse false | true
5001 * Create a new Pagination
5002 * @param {Object} config The config object
5005 Roo.bootstrap.Pagination = function(config){
5006 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5009 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5015 getAutoCreate : function(){
5021 cfg.cls += ' inverse';
5027 cfg.cls += " " + this.cls;
5045 * @class Roo.bootstrap.PaginationItem
5046 * @extends Roo.bootstrap.Component
5047 * Bootstrap PaginationItem class
5048 * @cfg {String} html text
5049 * @cfg {String} href the link
5050 * @cfg {Boolean} preventDefault (true | false) default true
5051 * @cfg {Boolean} active (true | false) default false
5052 * @cfg {Boolean} disabled default false
5056 * Create a new PaginationItem
5057 * @param {Object} config The config object
5061 Roo.bootstrap.PaginationItem = function(config){
5062 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5067 * The raw click event for the entire grid.
5068 * @param {Roo.EventObject} e
5074 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5078 preventDefault: true,
5083 getAutoCreate : function(){
5089 href : this.href ? this.href : '#',
5090 html : this.html ? this.html : ''
5100 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5104 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5110 initEvents: function() {
5112 this.el.on('click', this.onClick, this);
5115 onClick : function(e)
5117 Roo.log('PaginationItem on click ');
5118 if(this.preventDefault){
5126 this.fireEvent('click', this, e);
5142 * @class Roo.bootstrap.Slider
5143 * @extends Roo.bootstrap.Component
5144 * Bootstrap Slider class
5147 * Create a new Slider
5148 * @param {Object} config The config object
5151 Roo.bootstrap.Slider = function(config){
5152 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5155 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5157 getAutoCreate : function(){
5161 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5165 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5177 * Ext JS Library 1.1.1
5178 * Copyright(c) 2006-2007, Ext JS, LLC.
5180 * Originally Released Under LGPL - original licence link has changed is not relivant.
5183 * <script type="text/javascript">
5188 * @class Roo.grid.ColumnModel
5189 * @extends Roo.util.Observable
5190 * This is the default implementation of a ColumnModel used by the Grid. It defines
5191 * the columns in the grid.
5194 var colModel = new Roo.grid.ColumnModel([
5195 {header: "Ticker", width: 60, sortable: true, locked: true},
5196 {header: "Company Name", width: 150, sortable: true},
5197 {header: "Market Cap.", width: 100, sortable: true},
5198 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5199 {header: "Employees", width: 100, sortable: true, resizable: false}
5204 * The config options listed for this class are options which may appear in each
5205 * individual column definition.
5206 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5208 * @param {Object} config An Array of column config objects. See this class's
5209 * config objects for details.
5211 Roo.grid.ColumnModel = function(config){
5213 * The config passed into the constructor
5215 this.config = config;
5218 // if no id, create one
5219 // if the column does not have a dataIndex mapping,
5220 // map it to the order it is in the config
5221 for(var i = 0, len = config.length; i < len; i++){
5223 if(typeof c.dataIndex == "undefined"){
5226 if(typeof c.renderer == "string"){
5227 c.renderer = Roo.util.Format[c.renderer];
5229 if(typeof c.id == "undefined"){
5232 if(c.editor && c.editor.xtype){
5233 c.editor = Roo.factory(c.editor, Roo.grid);
5235 if(c.editor && c.editor.isFormField){
5236 c.editor = new Roo.grid.GridEditor(c.editor);
5238 this.lookup[c.id] = c;
5242 * The width of columns which have no width specified (defaults to 100)
5245 this.defaultWidth = 100;
5248 * Default sortable of columns which have no sortable specified (defaults to false)
5251 this.defaultSortable = false;
5255 * @event widthchange
5256 * Fires when the width of a column changes.
5257 * @param {ColumnModel} this
5258 * @param {Number} columnIndex The column index
5259 * @param {Number} newWidth The new width
5261 "widthchange": true,
5263 * @event headerchange
5264 * Fires when the text of a header changes.
5265 * @param {ColumnModel} this
5266 * @param {Number} columnIndex The column index
5267 * @param {Number} newText The new header text
5269 "headerchange": true,
5271 * @event hiddenchange
5272 * Fires when a column is hidden or "unhidden".
5273 * @param {ColumnModel} this
5274 * @param {Number} columnIndex The column index
5275 * @param {Boolean} hidden true if hidden, false otherwise
5277 "hiddenchange": true,
5279 * @event columnmoved
5280 * Fires when a column is moved.
5281 * @param {ColumnModel} this
5282 * @param {Number} oldIndex
5283 * @param {Number} newIndex
5285 "columnmoved" : true,
5287 * @event columlockchange
5288 * Fires when a column's locked state is changed
5289 * @param {ColumnModel} this
5290 * @param {Number} colIndex
5291 * @param {Boolean} locked true if locked
5293 "columnlockchange" : true
5295 Roo.grid.ColumnModel.superclass.constructor.call(this);
5297 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5299 * @cfg {String} header The header text to display in the Grid view.
5302 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5303 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5304 * specified, the column's index is used as an index into the Record's data Array.
5307 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5308 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5311 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5312 * Defaults to the value of the {@link #defaultSortable} property.
5313 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5316 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5319 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5322 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5325 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5328 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5329 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5330 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5331 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5334 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5337 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5340 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5343 * @cfg {String} cursor (Optional)
5346 * @cfg {String} tooltip (Optional)
5349 * @cfg {Number} xs (Optional)
5352 * @cfg {Number} sm (Optional)
5355 * @cfg {Number} md (Optional)
5358 * @cfg {Number} lg (Optional)
5361 * Returns the id of the column at the specified index.
5362 * @param {Number} index The column index
5363 * @return {String} the id
5365 getColumnId : function(index){
5366 return this.config[index].id;
5370 * Returns the column for a specified id.
5371 * @param {String} id The column id
5372 * @return {Object} the column
5374 getColumnById : function(id){
5375 return this.lookup[id];
5380 * Returns the column for a specified dataIndex.
5381 * @param {String} dataIndex The column dataIndex
5382 * @return {Object|Boolean} the column or false if not found
5384 getColumnByDataIndex: function(dataIndex){
5385 var index = this.findColumnIndex(dataIndex);
5386 return index > -1 ? this.config[index] : false;
5390 * Returns the index for a specified column id.
5391 * @param {String} id The column id
5392 * @return {Number} the index, or -1 if not found
5394 getIndexById : function(id){
5395 for(var i = 0, len = this.config.length; i < len; i++){
5396 if(this.config[i].id == id){
5404 * Returns the index for a specified column dataIndex.
5405 * @param {String} dataIndex The column dataIndex
5406 * @return {Number} the index, or -1 if not found
5409 findColumnIndex : function(dataIndex){
5410 for(var i = 0, len = this.config.length; i < len; i++){
5411 if(this.config[i].dataIndex == dataIndex){
5419 moveColumn : function(oldIndex, newIndex){
5420 var c = this.config[oldIndex];
5421 this.config.splice(oldIndex, 1);
5422 this.config.splice(newIndex, 0, c);
5423 this.dataMap = null;
5424 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5427 isLocked : function(colIndex){
5428 return this.config[colIndex].locked === true;
5431 setLocked : function(colIndex, value, suppressEvent){
5432 if(this.isLocked(colIndex) == value){
5435 this.config[colIndex].locked = value;
5437 this.fireEvent("columnlockchange", this, colIndex, value);
5441 getTotalLockedWidth : function(){
5443 for(var i = 0; i < this.config.length; i++){
5444 if(this.isLocked(i) && !this.isHidden(i)){
5445 this.totalWidth += this.getColumnWidth(i);
5451 getLockedCount : function(){
5452 for(var i = 0, len = this.config.length; i < len; i++){
5453 if(!this.isLocked(i)){
5458 return this.config.length;
5462 * Returns the number of columns.
5465 getColumnCount : function(visibleOnly){
5466 if(visibleOnly === true){
5468 for(var i = 0, len = this.config.length; i < len; i++){
5469 if(!this.isHidden(i)){
5475 return this.config.length;
5479 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5480 * @param {Function} fn
5481 * @param {Object} scope (optional)
5482 * @return {Array} result
5484 getColumnsBy : function(fn, scope){
5486 for(var i = 0, len = this.config.length; i < len; i++){
5487 var c = this.config[i];
5488 if(fn.call(scope||this, c, i) === true){
5496 * Returns true if the specified column is sortable.
5497 * @param {Number} col The column index
5500 isSortable : function(col){
5501 if(typeof this.config[col].sortable == "undefined"){
5502 return this.defaultSortable;
5504 return this.config[col].sortable;
5508 * Returns the rendering (formatting) function defined for the column.
5509 * @param {Number} col The column index.
5510 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5512 getRenderer : function(col){
5513 if(!this.config[col].renderer){
5514 return Roo.grid.ColumnModel.defaultRenderer;
5516 return this.config[col].renderer;
5520 * Sets the rendering (formatting) function for a column.
5521 * @param {Number} col The column index
5522 * @param {Function} fn The function to use to process the cell's raw data
5523 * to return HTML markup for the grid view. The render function is called with
5524 * the following parameters:<ul>
5525 * <li>Data value.</li>
5526 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5527 * <li>css A CSS style string to apply to the table cell.</li>
5528 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5529 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5530 * <li>Row index</li>
5531 * <li>Column index</li>
5532 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5534 setRenderer : function(col, fn){
5535 this.config[col].renderer = fn;
5539 * Returns the width for the specified column.
5540 * @param {Number} col The column index
5543 getColumnWidth : function(col){
5544 return this.config[col].width * 1 || this.defaultWidth;
5548 * Sets the width for a column.
5549 * @param {Number} col The column index
5550 * @param {Number} width The new width
5552 setColumnWidth : function(col, width, suppressEvent){
5553 this.config[col].width = width;
5554 this.totalWidth = null;
5556 this.fireEvent("widthchange", this, col, width);
5561 * Returns the total width of all columns.
5562 * @param {Boolean} includeHidden True to include hidden column widths
5565 getTotalWidth : function(includeHidden){
5566 if(!this.totalWidth){
5567 this.totalWidth = 0;
5568 for(var i = 0, len = this.config.length; i < len; i++){
5569 if(includeHidden || !this.isHidden(i)){
5570 this.totalWidth += this.getColumnWidth(i);
5574 return this.totalWidth;
5578 * Returns the header for the specified column.
5579 * @param {Number} col The column index
5582 getColumnHeader : function(col){
5583 return this.config[col].header;
5587 * Sets the header for a column.
5588 * @param {Number} col The column index
5589 * @param {String} header The new header
5591 setColumnHeader : function(col, header){
5592 this.config[col].header = header;
5593 this.fireEvent("headerchange", this, col, header);
5597 * Returns the tooltip for the specified column.
5598 * @param {Number} col The column index
5601 getColumnTooltip : function(col){
5602 return this.config[col].tooltip;
5605 * Sets the tooltip for a column.
5606 * @param {Number} col The column index
5607 * @param {String} tooltip The new tooltip
5609 setColumnTooltip : function(col, tooltip){
5610 this.config[col].tooltip = tooltip;
5614 * Returns the dataIndex for the specified column.
5615 * @param {Number} col The column index
5618 getDataIndex : function(col){
5619 return this.config[col].dataIndex;
5623 * Sets the dataIndex for a column.
5624 * @param {Number} col The column index
5625 * @param {Number} dataIndex The new dataIndex
5627 setDataIndex : function(col, dataIndex){
5628 this.config[col].dataIndex = dataIndex;
5634 * Returns true if the cell is editable.
5635 * @param {Number} colIndex The column index
5636 * @param {Number} rowIndex The row index - this is nto actually used..?
5639 isCellEditable : function(colIndex, rowIndex){
5640 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5644 * Returns the editor defined for the cell/column.
5645 * return false or null to disable editing.
5646 * @param {Number} colIndex The column index
5647 * @param {Number} rowIndex The row index
5650 getCellEditor : function(colIndex, rowIndex){
5651 return this.config[colIndex].editor;
5655 * Sets if a column is editable.
5656 * @param {Number} col The column index
5657 * @param {Boolean} editable True if the column is editable
5659 setEditable : function(col, editable){
5660 this.config[col].editable = editable;
5665 * Returns true if the column is hidden.
5666 * @param {Number} colIndex The column index
5669 isHidden : function(colIndex){
5670 return this.config[colIndex].hidden;
5675 * Returns true if the column width cannot be changed
5677 isFixed : function(colIndex){
5678 return this.config[colIndex].fixed;
5682 * Returns true if the column can be resized
5685 isResizable : function(colIndex){
5686 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5689 * Sets if a column is hidden.
5690 * @param {Number} colIndex The column index
5691 * @param {Boolean} hidden True if the column is hidden
5693 setHidden : function(colIndex, hidden){
5694 this.config[colIndex].hidden = hidden;
5695 this.totalWidth = null;
5696 this.fireEvent("hiddenchange", this, colIndex, hidden);
5700 * Sets the editor for a column.
5701 * @param {Number} col The column index
5702 * @param {Object} editor The editor object
5704 setEditor : function(col, editor){
5705 this.config[col].editor = editor;
5709 Roo.grid.ColumnModel.defaultRenderer = function(value)
5711 if(typeof value == "object") {
5714 if(typeof value == "string" && value.length < 1){
5718 return String.format("{0}", value);
5721 // Alias for backwards compatibility
5722 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5725 * Ext JS Library 1.1.1
5726 * Copyright(c) 2006-2007, Ext JS, LLC.
5728 * Originally Released Under LGPL - original licence link has changed is not relivant.
5731 * <script type="text/javascript">
5735 * @class Roo.LoadMask
5736 * A simple utility class for generically masking elements while loading data. If the element being masked has
5737 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5738 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5739 * element's UpdateManager load indicator and will be destroyed after the initial load.
5741 * Create a new LoadMask
5742 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5743 * @param {Object} config The config object
5745 Roo.LoadMask = function(el, config){
5746 this.el = Roo.get(el);
5747 Roo.apply(this, config);
5749 this.store.on('beforeload', this.onBeforeLoad, this);
5750 this.store.on('load', this.onLoad, this);
5751 this.store.on('loadexception', this.onLoadException, this);
5752 this.removeMask = false;
5754 var um = this.el.getUpdateManager();
5755 um.showLoadIndicator = false; // disable the default indicator
5756 um.on('beforeupdate', this.onBeforeLoad, this);
5757 um.on('update', this.onLoad, this);
5758 um.on('failure', this.onLoad, this);
5759 this.removeMask = true;
5763 Roo.LoadMask.prototype = {
5765 * @cfg {Boolean} removeMask
5766 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5767 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5771 * The text to display in a centered loading message box (defaults to 'Loading...')
5775 * @cfg {String} msgCls
5776 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5778 msgCls : 'x-mask-loading',
5781 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5787 * Disables the mask to prevent it from being displayed
5789 disable : function(){
5790 this.disabled = true;
5794 * Enables the mask so that it can be displayed
5796 enable : function(){
5797 this.disabled = false;
5800 onLoadException : function()
5804 if (typeof(arguments[3]) != 'undefined') {
5805 Roo.MessageBox.alert("Error loading",arguments[3]);
5809 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5810 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5817 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5822 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5826 onBeforeLoad : function(){
5828 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5833 destroy : function(){
5835 this.store.un('beforeload', this.onBeforeLoad, this);
5836 this.store.un('load', this.onLoad, this);
5837 this.store.un('loadexception', this.onLoadException, this);
5839 var um = this.el.getUpdateManager();
5840 um.un('beforeupdate', this.onBeforeLoad, this);
5841 um.un('update', this.onLoad, this);
5842 um.un('failure', this.onLoad, this);
5853 * @class Roo.bootstrap.Table
5854 * @extends Roo.bootstrap.Component
5855 * Bootstrap Table class
5856 * @cfg {String} cls table class
5857 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5858 * @cfg {String} bgcolor Specifies the background color for a table
5859 * @cfg {Number} border Specifies whether the table cells should have borders or not
5860 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5861 * @cfg {Number} cellspacing Specifies the space between cells
5862 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5863 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5864 * @cfg {String} sortable Specifies that the table should be sortable
5865 * @cfg {String} summary Specifies a summary of the content of a table
5866 * @cfg {Number} width Specifies the width of a table
5867 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5869 * @cfg {boolean} striped Should the rows be alternative striped
5870 * @cfg {boolean} bordered Add borders to the table
5871 * @cfg {boolean} hover Add hover highlighting
5872 * @cfg {boolean} condensed Format condensed
5873 * @cfg {boolean} responsive Format condensed
5874 * @cfg {Boolean} loadMask (true|false) default false
5875 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5876 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5877 * @cfg {Boolean} rowSelection (true|false) default false
5878 * @cfg {Boolean} cellSelection (true|false) default false
5879 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5880 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5881 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5885 * Create a new Table
5886 * @param {Object} config The config object
5889 Roo.bootstrap.Table = function(config){
5890 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5895 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5896 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5897 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5898 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5900 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5902 this.sm.grid = this;
5903 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5904 this.sm = this.selModel;
5905 this.sm.xmodule = this.xmodule || false;
5908 if (this.cm && typeof(this.cm.config) == 'undefined') {
5909 this.colModel = new Roo.grid.ColumnModel(this.cm);
5910 this.cm = this.colModel;
5911 this.cm.xmodule = this.xmodule || false;
5914 this.store= Roo.factory(this.store, Roo.data);
5915 this.ds = this.store;
5916 this.ds.xmodule = this.xmodule || false;
5919 if (this.footer && this.store) {
5920 this.footer.dataSource = this.ds;
5921 this.footer = Roo.factory(this.footer);
5928 * Fires when a cell is clicked
5929 * @param {Roo.bootstrap.Table} this
5930 * @param {Roo.Element} el
5931 * @param {Number} rowIndex
5932 * @param {Number} columnIndex
5933 * @param {Roo.EventObject} e
5937 * @event celldblclick
5938 * Fires when a cell is double clicked
5939 * @param {Roo.bootstrap.Table} this
5940 * @param {Roo.Element} el
5941 * @param {Number} rowIndex
5942 * @param {Number} columnIndex
5943 * @param {Roo.EventObject} e
5945 "celldblclick" : true,
5948 * Fires when a row is clicked
5949 * @param {Roo.bootstrap.Table} this
5950 * @param {Roo.Element} el
5951 * @param {Number} rowIndex
5952 * @param {Roo.EventObject} e
5956 * @event rowdblclick
5957 * Fires when a row is double clicked
5958 * @param {Roo.bootstrap.Table} this
5959 * @param {Roo.Element} el
5960 * @param {Number} rowIndex
5961 * @param {Roo.EventObject} e
5963 "rowdblclick" : true,
5966 * Fires when a mouseover occur
5967 * @param {Roo.bootstrap.Table} this
5968 * @param {Roo.Element} el
5969 * @param {Number} rowIndex
5970 * @param {Number} columnIndex
5971 * @param {Roo.EventObject} e
5976 * Fires when a mouseout occur
5977 * @param {Roo.bootstrap.Table} this
5978 * @param {Roo.Element} el
5979 * @param {Number} rowIndex
5980 * @param {Number} columnIndex
5981 * @param {Roo.EventObject} e
5986 * Fires when a row is rendered, so you can change add a style to it.
5987 * @param {Roo.bootstrap.Table} this
5988 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5992 * @event rowsrendered
5993 * Fires when all the rows have been rendered
5994 * @param {Roo.bootstrap.Table} this
5996 'rowsrendered' : true,
5998 * @event contextmenu
5999 * The raw contextmenu event for the entire grid.
6000 * @param {Roo.EventObject} e
6002 "contextmenu" : true,
6004 * @event rowcontextmenu
6005 * Fires when a row is right clicked
6006 * @param {Roo.bootstrap.Table} this
6007 * @param {Number} rowIndex
6008 * @param {Roo.EventObject} e
6010 "rowcontextmenu" : true,
6012 * @event cellcontextmenu
6013 * Fires when a cell is right clicked
6014 * @param {Roo.bootstrap.Table} this
6015 * @param {Number} rowIndex
6016 * @param {Number} cellIndex
6017 * @param {Roo.EventObject} e
6019 "cellcontextmenu" : true,
6021 * @event headercontextmenu
6022 * Fires when a header is right clicked
6023 * @param {Roo.bootstrap.Table} this
6024 * @param {Number} columnIndex
6025 * @param {Roo.EventObject} e
6027 "headercontextmenu" : true
6031 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6057 rowSelection : false,
6058 cellSelection : false,
6061 // Roo.Element - the tbody
6063 // Roo.Element - thead element
6066 container: false, // used by gridpanel...
6072 getAutoCreate : function()
6074 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6081 if (this.scrollBody) {
6082 cfg.cls += ' table-body-fixed';
6085 cfg.cls += ' table-striped';
6089 cfg.cls += ' table-hover';
6091 if (this.bordered) {
6092 cfg.cls += ' table-bordered';
6094 if (this.condensed) {
6095 cfg.cls += ' table-condensed';
6097 if (this.responsive) {
6098 cfg.cls += ' table-responsive';
6102 cfg.cls+= ' ' +this.cls;
6105 // this lot should be simplifed...
6118 ].forEach(function(k) {
6126 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6129 if(this.store || this.cm){
6130 if(this.headerShow){
6131 cfg.cn.push(this.renderHeader());
6134 cfg.cn.push(this.renderBody());
6136 if(this.footerShow){
6137 cfg.cn.push(this.renderFooter());
6139 // where does this come from?
6140 //cfg.cls+= ' TableGrid';
6143 return { cn : [ cfg ] };
6146 initEvents : function()
6148 if(!this.store || !this.cm){
6151 if (this.selModel) {
6152 this.selModel.initEvents();
6156 //Roo.log('initEvents with ds!!!!');
6158 this.mainBody = this.el.select('tbody', true).first();
6159 this.mainHead = this.el.select('thead', true).first();
6166 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6167 e.on('click', _this.sort, _this);
6170 this.mainBody.on("click", this.onClick, this);
6171 this.mainBody.on("dblclick", this.onDblClick, this);
6173 // why is this done????? = it breaks dialogs??
6174 //this.parent().el.setStyle('position', 'relative');
6178 this.footer.parentId = this.id;
6179 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6182 this.el.select('tfoot tr td').first().addClass('hide');
6187 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6190 this.store.on('load', this.onLoad, this);
6191 this.store.on('beforeload', this.onBeforeLoad, this);
6192 this.store.on('update', this.onUpdate, this);
6193 this.store.on('add', this.onAdd, this);
6194 this.store.on("clear", this.clear, this);
6196 this.el.on("contextmenu", this.onContextMenu, this);
6198 this.mainBody.on('scroll', this.onBodyScroll, this);
6200 this.cm.on("headerchange", this.onHeaderChange, this);
6202 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6206 onContextMenu : function(e, t)
6208 this.processEvent("contextmenu", e);
6211 processEvent : function(name, e)
6213 if (name != 'touchstart' ) {
6214 this.fireEvent(name, e);
6217 var t = e.getTarget();
6219 var cell = Roo.get(t);
6225 if(cell.findParent('tfoot', false, true)){
6229 if(cell.findParent('thead', false, true)){
6231 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6232 cell = Roo.get(t).findParent('th', false, true);
6234 Roo.log("failed to find th in thead?");
6235 Roo.log(e.getTarget());
6240 var cellIndex = cell.dom.cellIndex;
6242 var ename = name == 'touchstart' ? 'click' : name;
6243 this.fireEvent("header" + ename, this, cellIndex, e);
6248 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6249 cell = Roo.get(t).findParent('td', false, true);
6251 Roo.log("failed to find th in tbody?");
6252 Roo.log(e.getTarget());
6257 var row = cell.findParent('tr', false, true);
6258 var cellIndex = cell.dom.cellIndex;
6259 var rowIndex = row.dom.rowIndex - 1;
6263 this.fireEvent("row" + name, this, rowIndex, e);
6267 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6273 onMouseover : function(e, el)
6275 var cell = Roo.get(el);
6281 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6282 cell = cell.findParent('td', false, true);
6285 var row = cell.findParent('tr', false, true);
6286 var cellIndex = cell.dom.cellIndex;
6287 var rowIndex = row.dom.rowIndex - 1; // start from 0
6289 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6293 onMouseout : function(e, el)
6295 var cell = Roo.get(el);
6301 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6302 cell = cell.findParent('td', false, true);
6305 var row = cell.findParent('tr', false, true);
6306 var cellIndex = cell.dom.cellIndex;
6307 var rowIndex = row.dom.rowIndex - 1; // start from 0
6309 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6313 onClick : function(e, el)
6315 var cell = Roo.get(el);
6317 if(!cell || (!this.cellSelection && !this.rowSelection)){
6321 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6322 cell = cell.findParent('td', false, true);
6325 if(!cell || typeof(cell) == 'undefined'){
6329 var row = cell.findParent('tr', false, true);
6331 if(!row || typeof(row) == 'undefined'){
6335 var cellIndex = cell.dom.cellIndex;
6336 var rowIndex = this.getRowIndex(row);
6338 // why??? - should these not be based on SelectionModel?
6339 if(this.cellSelection){
6340 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6343 if(this.rowSelection){
6344 this.fireEvent('rowclick', this, row, rowIndex, e);
6350 onDblClick : function(e,el)
6352 var cell = Roo.get(el);
6354 if(!cell || (!this.cellSelection && !this.rowSelection)){
6358 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6359 cell = cell.findParent('td', false, true);
6362 if(!cell || typeof(cell) == 'undefined'){
6366 var row = cell.findParent('tr', false, true);
6368 if(!row || typeof(row) == 'undefined'){
6372 var cellIndex = cell.dom.cellIndex;
6373 var rowIndex = this.getRowIndex(row);
6375 if(this.cellSelection){
6376 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6379 if(this.rowSelection){
6380 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6384 sort : function(e,el)
6386 var col = Roo.get(el);
6388 if(!col.hasClass('sortable')){
6392 var sort = col.attr('sort');
6395 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6399 this.store.sortInfo = {field : sort, direction : dir};
6402 Roo.log("calling footer first");
6403 this.footer.onClick('first');
6406 this.store.load({ params : { start : 0 } });
6410 renderHeader : function()
6418 this.totalWidth = 0;
6420 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6422 var config = cm.config[i];
6426 cls : 'x-hcol-' + i,
6428 html: cm.getColumnHeader(i)
6433 if(typeof(config.sortable) != 'undefined' && config.sortable){
6435 c.html = '<i class="glyphicon"></i>' + c.html;
6438 if(typeof(config.lgHeader) != 'undefined'){
6439 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6442 if(typeof(config.mdHeader) != 'undefined'){
6443 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6446 if(typeof(config.smHeader) != 'undefined'){
6447 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6450 if(typeof(config.xsHeader) != 'undefined'){
6451 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6458 if(typeof(config.tooltip) != 'undefined'){
6459 c.tooltip = config.tooltip;
6462 if(typeof(config.colspan) != 'undefined'){
6463 c.colspan = config.colspan;
6466 if(typeof(config.hidden) != 'undefined' && config.hidden){
6467 c.style += ' display:none;';
6470 if(typeof(config.dataIndex) != 'undefined'){
6471 c.sort = config.dataIndex;
6476 if(typeof(config.align) != 'undefined' && config.align.length){
6477 c.style += ' text-align:' + config.align + ';';
6480 if(typeof(config.width) != 'undefined'){
6481 c.style += ' width:' + config.width + 'px;';
6482 this.totalWidth += config.width;
6484 this.totalWidth += 100; // assume minimum of 100 per column?
6487 if(typeof(config.cls) != 'undefined'){
6488 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6491 ['xs','sm','md','lg'].map(function(size){
6493 if(typeof(config[size]) == 'undefined'){
6497 if (!config[size]) { // 0 = hidden
6498 c.cls += ' hidden-' + size;
6502 c.cls += ' col-' + size + '-' + config[size];
6512 renderBody : function()
6522 colspan : this.cm.getColumnCount()
6532 renderFooter : function()
6542 colspan : this.cm.getColumnCount()
6556 // Roo.log('ds onload');
6561 var ds = this.store;
6563 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6564 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6565 if (_this.store.sortInfo) {
6567 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6568 e.select('i', true).addClass(['glyphicon-arrow-up']);
6571 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6572 e.select('i', true).addClass(['glyphicon-arrow-down']);
6577 var tbody = this.mainBody;
6579 if(ds.getCount() > 0){
6580 ds.data.each(function(d,rowIndex){
6581 var row = this.renderRow(cm, ds, rowIndex);
6583 tbody.createChild(row);
6587 if(row.cellObjects.length){
6588 Roo.each(row.cellObjects, function(r){
6589 _this.renderCellObject(r);
6596 Roo.each(this.el.select('tbody td', true).elements, function(e){
6597 e.on('mouseover', _this.onMouseover, _this);
6600 Roo.each(this.el.select('tbody td', true).elements, function(e){
6601 e.on('mouseout', _this.onMouseout, _this);
6603 this.fireEvent('rowsrendered', this);
6609 onUpdate : function(ds,record)
6611 this.refreshRow(record);
6615 onRemove : function(ds, record, index, isUpdate){
6616 if(isUpdate !== true){
6617 this.fireEvent("beforerowremoved", this, index, record);
6619 var bt = this.mainBody.dom;
6621 var rows = this.el.select('tbody > tr', true).elements;
6623 if(typeof(rows[index]) != 'undefined'){
6624 bt.removeChild(rows[index].dom);
6627 // if(bt.rows[index]){
6628 // bt.removeChild(bt.rows[index]);
6631 if(isUpdate !== true){
6632 //this.stripeRows(index);
6633 //this.syncRowHeights(index, index);
6635 this.fireEvent("rowremoved", this, index, record);
6639 onAdd : function(ds, records, rowIndex)
6641 //Roo.log('on Add called');
6642 // - note this does not handle multiple adding very well..
6643 var bt = this.mainBody.dom;
6644 for (var i =0 ; i < records.length;i++) {
6645 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6646 //Roo.log(records[i]);
6647 //Roo.log(this.store.getAt(rowIndex+i));
6648 this.insertRow(this.store, rowIndex + i, false);
6655 refreshRow : function(record){
6656 var ds = this.store, index;
6657 if(typeof record == 'number'){
6659 record = ds.getAt(index);
6661 index = ds.indexOf(record);
6663 this.insertRow(ds, index, true);
6665 this.onRemove(ds, record, index+1, true);
6667 //this.syncRowHeights(index, index);
6669 this.fireEvent("rowupdated", this, index, record);
6672 insertRow : function(dm, rowIndex, isUpdate){
6675 this.fireEvent("beforerowsinserted", this, rowIndex);
6677 //var s = this.getScrollState();
6678 var row = this.renderRow(this.cm, this.store, rowIndex);
6679 // insert before rowIndex..
6680 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6684 if(row.cellObjects.length){
6685 Roo.each(row.cellObjects, function(r){
6686 _this.renderCellObject(r);
6691 this.fireEvent("rowsinserted", this, rowIndex);
6692 //this.syncRowHeights(firstRow, lastRow);
6693 //this.stripeRows(firstRow);
6700 getRowDom : function(rowIndex)
6702 var rows = this.el.select('tbody > tr', true).elements;
6704 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6707 // returns the object tree for a tr..
6710 renderRow : function(cm, ds, rowIndex)
6712 var d = ds.getAt(rowIndex);
6716 cls : 'x-row-' + rowIndex,
6720 var cellObjects = [];
6722 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6723 var config = cm.config[i];
6725 var renderer = cm.getRenderer(i);
6729 if(typeof(renderer) !== 'undefined'){
6730 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6732 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6733 // and are rendered into the cells after the row is rendered - using the id for the element.
6735 if(typeof(value) === 'object'){
6745 rowIndex : rowIndex,
6750 this.fireEvent('rowclass', this, rowcfg);
6754 cls : rowcfg.rowClass + ' x-col-' + i,
6756 html: (typeof(value) === 'object') ? '' : value
6763 if(typeof(config.colspan) != 'undefined'){
6764 td.colspan = config.colspan;
6767 if(typeof(config.hidden) != 'undefined' && config.hidden){
6768 td.style += ' display:none;';
6771 if(typeof(config.align) != 'undefined' && config.align.length){
6772 td.style += ' text-align:' + config.align + ';';
6774 if(typeof(config.valign) != 'undefined' && config.valign.length){
6775 td.style += ' vertical-align:' + config.valign + ';';
6778 if(typeof(config.width) != 'undefined'){
6779 td.style += ' width:' + config.width + 'px;';
6782 if(typeof(config.cursor) != 'undefined'){
6783 td.style += ' cursor:' + config.cursor + ';';
6786 if(typeof(config.cls) != 'undefined'){
6787 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6790 ['xs','sm','md','lg'].map(function(size){
6792 if(typeof(config[size]) == 'undefined'){
6796 if (!config[size]) { // 0 = hidden
6797 td.cls += ' hidden-' + size;
6801 td.cls += ' col-' + size + '-' + config[size];
6809 row.cellObjects = cellObjects;
6817 onBeforeLoad : function()
6826 this.el.select('tbody', true).first().dom.innerHTML = '';
6829 * Show or hide a row.
6830 * @param {Number} rowIndex to show or hide
6831 * @param {Boolean} state hide
6833 setRowVisibility : function(rowIndex, state)
6835 var bt = this.mainBody.dom;
6837 var rows = this.el.select('tbody > tr', true).elements;
6839 if(typeof(rows[rowIndex]) == 'undefined'){
6842 rows[rowIndex].dom.style.display = state ? '' : 'none';
6846 getSelectionModel : function(){
6848 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6850 return this.selModel;
6853 * Render the Roo.bootstrap object from renderder
6855 renderCellObject : function(r)
6859 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6861 var t = r.cfg.render(r.container);
6864 Roo.each(r.cfg.cn, function(c){
6866 container: t.getChildContainer(),
6869 _this.renderCellObject(child);
6874 getRowIndex : function(row)
6878 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6889 * Returns the grid's underlying element = used by panel.Grid
6890 * @return {Element} The element
6892 getGridEl : function(){
6896 * Forces a resize - used by panel.Grid
6897 * @return {Element} The element
6899 autoSize : function()
6901 //var ctr = Roo.get(this.container.dom.parentElement);
6902 var ctr = Roo.get(this.el.dom);
6904 var thd = this.getGridEl().select('thead',true).first();
6905 var tbd = this.getGridEl().select('tbody', true).first();
6906 var tfd = this.getGridEl().select('tfoot', true).first();
6908 var cw = ctr.getWidth();
6912 tbd.setSize(ctr.getWidth(),
6913 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6915 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6918 cw = Math.max(cw, this.totalWidth);
6919 this.getGridEl().select('tr',true).setWidth(cw);
6920 // resize 'expandable coloumn?
6922 return; // we doe not have a view in this design..
6925 onBodyScroll: function()
6927 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6929 this.mainHead.setStyle({
6930 'position' : 'relative',
6931 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6937 var scrollHeight = this.mainBody.dom.scrollHeight;
6939 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6941 var height = this.mainBody.getHeight();
6943 if(scrollHeight - height == scrollTop) {
6945 var total = this.ds.getTotalCount();
6947 if(this.footer.cursor + this.footer.pageSize < total){
6949 this.footer.ds.load({
6951 start : this.footer.cursor + this.footer.pageSize,
6952 limit : this.footer.pageSize
6962 onHeaderChange : function()
6964 var header = this.renderHeader();
6965 var table = this.el.select('table', true).first();
6967 this.mainHead.remove();
6968 this.mainHead = table.createChild(header, this.mainBody, false);
6971 onHiddenChange : function(colModel, colIndex, hidden)
6973 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6974 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6976 this.CSS.updateRule(thSelector, "display", "");
6977 this.CSS.updateRule(tdSelector, "display", "");
6980 this.CSS.updateRule(thSelector, "display", "none");
6981 this.CSS.updateRule(tdSelector, "display", "none");
6984 this.onHeaderChange();
7001 * @class Roo.bootstrap.TableCell
7002 * @extends Roo.bootstrap.Component
7003 * Bootstrap TableCell class
7004 * @cfg {String} html cell contain text
7005 * @cfg {String} cls cell class
7006 * @cfg {String} tag cell tag (td|th) default td
7007 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7008 * @cfg {String} align Aligns the content in a cell
7009 * @cfg {String} axis Categorizes cells
7010 * @cfg {String} bgcolor Specifies the background color of a cell
7011 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7012 * @cfg {Number} colspan Specifies the number of columns a cell should span
7013 * @cfg {String} headers Specifies one or more header cells a cell is related to
7014 * @cfg {Number} height Sets the height of a cell
7015 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7016 * @cfg {Number} rowspan Sets the number of rows a cell should span
7017 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7018 * @cfg {String} valign Vertical aligns the content in a cell
7019 * @cfg {Number} width Specifies the width of a cell
7022 * Create a new TableCell
7023 * @param {Object} config The config object
7026 Roo.bootstrap.TableCell = function(config){
7027 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7030 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7050 getAutoCreate : function(){
7051 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7071 cfg.align=this.align
7077 cfg.bgcolor=this.bgcolor
7080 cfg.charoff=this.charoff
7083 cfg.colspan=this.colspan
7086 cfg.headers=this.headers
7089 cfg.height=this.height
7092 cfg.nowrap=this.nowrap
7095 cfg.rowspan=this.rowspan
7098 cfg.scope=this.scope
7101 cfg.valign=this.valign
7104 cfg.width=this.width
7123 * @class Roo.bootstrap.TableRow
7124 * @extends Roo.bootstrap.Component
7125 * Bootstrap TableRow class
7126 * @cfg {String} cls row class
7127 * @cfg {String} align Aligns the content in a table row
7128 * @cfg {String} bgcolor Specifies a background color for a table row
7129 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7130 * @cfg {String} valign Vertical aligns the content in a table row
7133 * Create a new TableRow
7134 * @param {Object} config The config object
7137 Roo.bootstrap.TableRow = function(config){
7138 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7141 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7149 getAutoCreate : function(){
7150 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7160 cfg.align = this.align;
7163 cfg.bgcolor = this.bgcolor;
7166 cfg.charoff = this.charoff;
7169 cfg.valign = this.valign;
7187 * @class Roo.bootstrap.TableBody
7188 * @extends Roo.bootstrap.Component
7189 * Bootstrap TableBody class
7190 * @cfg {String} cls element class
7191 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7192 * @cfg {String} align Aligns the content inside the element
7193 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7194 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7197 * Create a new TableBody
7198 * @param {Object} config The config object
7201 Roo.bootstrap.TableBody = function(config){
7202 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7205 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7213 getAutoCreate : function(){
7214 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7228 cfg.align = this.align;
7231 cfg.charoff = this.charoff;
7234 cfg.valign = this.valign;
7241 // initEvents : function()
7248 // this.store = Roo.factory(this.store, Roo.data);
7249 // this.store.on('load', this.onLoad, this);
7251 // this.store.load();
7255 // onLoad: function ()
7257 // this.fireEvent('load', this);
7267 * Ext JS Library 1.1.1
7268 * Copyright(c) 2006-2007, Ext JS, LLC.
7270 * Originally Released Under LGPL - original licence link has changed is not relivant.
7273 * <script type="text/javascript">
7276 // as we use this in bootstrap.
7277 Roo.namespace('Roo.form');
7279 * @class Roo.form.Action
7280 * Internal Class used to handle form actions
7282 * @param {Roo.form.BasicForm} el The form element or its id
7283 * @param {Object} config Configuration options
7288 // define the action interface
7289 Roo.form.Action = function(form, options){
7291 this.options = options || {};
7294 * Client Validation Failed
7297 Roo.form.Action.CLIENT_INVALID = 'client';
7299 * Server Validation Failed
7302 Roo.form.Action.SERVER_INVALID = 'server';
7304 * Connect to Server Failed
7307 Roo.form.Action.CONNECT_FAILURE = 'connect';
7309 * Reading Data from Server Failed
7312 Roo.form.Action.LOAD_FAILURE = 'load';
7314 Roo.form.Action.prototype = {
7316 failureType : undefined,
7317 response : undefined,
7321 run : function(options){
7326 success : function(response){
7331 handleResponse : function(response){
7335 // default connection failure
7336 failure : function(response){
7338 this.response = response;
7339 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7340 this.form.afterAction(this, false);
7343 processResponse : function(response){
7344 this.response = response;
7345 if(!response.responseText){
7348 this.result = this.handleResponse(response);
7352 // utility functions used internally
7353 getUrl : function(appendParams){
7354 var url = this.options.url || this.form.url || this.form.el.dom.action;
7356 var p = this.getParams();
7358 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7364 getMethod : function(){
7365 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7368 getParams : function(){
7369 var bp = this.form.baseParams;
7370 var p = this.options.params;
7372 if(typeof p == "object"){
7373 p = Roo.urlEncode(Roo.applyIf(p, bp));
7374 }else if(typeof p == 'string' && bp){
7375 p += '&' + Roo.urlEncode(bp);
7378 p = Roo.urlEncode(bp);
7383 createCallback : function(){
7385 success: this.success,
7386 failure: this.failure,
7388 timeout: (this.form.timeout*1000),
7389 upload: this.form.fileUpload ? this.success : undefined
7394 Roo.form.Action.Submit = function(form, options){
7395 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7398 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7401 haveProgress : false,
7402 uploadComplete : false,
7404 // uploadProgress indicator.
7405 uploadProgress : function()
7407 if (!this.form.progressUrl) {
7411 if (!this.haveProgress) {
7412 Roo.MessageBox.progress("Uploading", "Uploading");
7414 if (this.uploadComplete) {
7415 Roo.MessageBox.hide();
7419 this.haveProgress = true;
7421 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7423 var c = new Roo.data.Connection();
7425 url : this.form.progressUrl,
7430 success : function(req){
7431 //console.log(data);
7435 rdata = Roo.decode(req.responseText)
7437 Roo.log("Invalid data from server..");
7441 if (!rdata || !rdata.success) {
7443 Roo.MessageBox.alert(Roo.encode(rdata));
7446 var data = rdata.data;
7448 if (this.uploadComplete) {
7449 Roo.MessageBox.hide();
7454 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7455 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7458 this.uploadProgress.defer(2000,this);
7461 failure: function(data) {
7462 Roo.log('progress url failed ');
7473 // run get Values on the form, so it syncs any secondary forms.
7474 this.form.getValues();
7476 var o = this.options;
7477 var method = this.getMethod();
7478 var isPost = method == 'POST';
7479 if(o.clientValidation === false || this.form.isValid()){
7481 if (this.form.progressUrl) {
7482 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7483 (new Date() * 1) + '' + Math.random());
7488 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7489 form:this.form.el.dom,
7490 url:this.getUrl(!isPost),
7492 params:isPost ? this.getParams() : null,
7493 isUpload: this.form.fileUpload
7496 this.uploadProgress();
7498 }else if (o.clientValidation !== false){ // client validation failed
7499 this.failureType = Roo.form.Action.CLIENT_INVALID;
7500 this.form.afterAction(this, false);
7504 success : function(response)
7506 this.uploadComplete= true;
7507 if (this.haveProgress) {
7508 Roo.MessageBox.hide();
7512 var result = this.processResponse(response);
7513 if(result === true || result.success){
7514 this.form.afterAction(this, true);
7518 this.form.markInvalid(result.errors);
7519 this.failureType = Roo.form.Action.SERVER_INVALID;
7521 this.form.afterAction(this, false);
7523 failure : function(response)
7525 this.uploadComplete= true;
7526 if (this.haveProgress) {
7527 Roo.MessageBox.hide();
7530 this.response = response;
7531 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7532 this.form.afterAction(this, false);
7535 handleResponse : function(response){
7536 if(this.form.errorReader){
7537 var rs = this.form.errorReader.read(response);
7540 for(var i = 0, len = rs.records.length; i < len; i++) {
7541 var r = rs.records[i];
7545 if(errors.length < 1){
7549 success : rs.success,
7555 ret = Roo.decode(response.responseText);
7559 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7569 Roo.form.Action.Load = function(form, options){
7570 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7571 this.reader = this.form.reader;
7574 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7579 Roo.Ajax.request(Roo.apply(
7580 this.createCallback(), {
7581 method:this.getMethod(),
7582 url:this.getUrl(false),
7583 params:this.getParams()
7587 success : function(response){
7589 var result = this.processResponse(response);
7590 if(result === true || !result.success || !result.data){
7591 this.failureType = Roo.form.Action.LOAD_FAILURE;
7592 this.form.afterAction(this, false);
7595 this.form.clearInvalid();
7596 this.form.setValues(result.data);
7597 this.form.afterAction(this, true);
7600 handleResponse : function(response){
7601 if(this.form.reader){
7602 var rs = this.form.reader.read(response);
7603 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7605 success : rs.success,
7609 return Roo.decode(response.responseText);
7613 Roo.form.Action.ACTION_TYPES = {
7614 'load' : Roo.form.Action.Load,
7615 'submit' : Roo.form.Action.Submit
7624 * @class Roo.bootstrap.Form
7625 * @extends Roo.bootstrap.Component
7626 * Bootstrap Form class
7627 * @cfg {String} method GET | POST (default POST)
7628 * @cfg {String} labelAlign top | left (default top)
7629 * @cfg {String} align left | right - for navbars
7630 * @cfg {Boolean} loadMask load mask when submit (default true)
7635 * @param {Object} config The config object
7639 Roo.bootstrap.Form = function(config){
7641 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7643 Roo.bootstrap.Form.popover.apply();
7647 * @event clientvalidation
7648 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7649 * @param {Form} this
7650 * @param {Boolean} valid true if the form has passed client-side validation
7652 clientvalidation: true,
7654 * @event beforeaction
7655 * Fires before any action is performed. Return false to cancel the action.
7656 * @param {Form} this
7657 * @param {Action} action The action to be performed
7661 * @event actionfailed
7662 * Fires when an action fails.
7663 * @param {Form} this
7664 * @param {Action} action The action that failed
7666 actionfailed : true,
7668 * @event actioncomplete
7669 * Fires when an action is completed.
7670 * @param {Form} this
7671 * @param {Action} action The action that completed
7673 actioncomplete : true
7677 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7680 * @cfg {String} method
7681 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7686 * The URL to use for form actions if one isn't supplied in the action options.
7689 * @cfg {Boolean} fileUpload
7690 * Set to true if this form is a file upload.
7694 * @cfg {Object} baseParams
7695 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7699 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7703 * @cfg {Sting} align (left|right) for navbar forms
7708 activeAction : null,
7711 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7712 * element by passing it or its id or mask the form itself by passing in true.
7715 waitMsgTarget : false,
7720 * @cfg {Boolean} errorMask (true|false) default false
7725 * @cfg {Number} maskOffset Default 100
7730 * @cfg {Boolean} maskBody
7734 getAutoCreate : function(){
7738 method : this.method || 'POST',
7739 id : this.id || Roo.id(),
7742 if (this.parent().xtype.match(/^Nav/)) {
7743 cfg.cls = 'navbar-form navbar-' + this.align;
7747 if (this.labelAlign == 'left' ) {
7748 cfg.cls += ' form-horizontal';
7754 initEvents : function()
7756 this.el.on('submit', this.onSubmit, this);
7757 // this was added as random key presses on the form where triggering form submit.
7758 this.el.on('keypress', function(e) {
7759 if (e.getCharCode() != 13) {
7762 // we might need to allow it for textareas.. and some other items.
7763 // check e.getTarget().
7765 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7769 Roo.log("keypress blocked");
7777 onSubmit : function(e){
7782 * Returns true if client-side validation on the form is successful.
7785 isValid : function(){
7786 var items = this.getItems();
7790 items.each(function(f){
7798 if(!target && f.el.isVisible(true)){
7804 if(this.errorMask && !valid){
7805 Roo.bootstrap.Form.popover.mask(this, target);
7812 * Returns true if any fields in this form have changed since their original load.
7815 isDirty : function(){
7817 var items = this.getItems();
7818 items.each(function(f){
7828 * Performs a predefined action (submit or load) or custom actions you define on this form.
7829 * @param {String} actionName The name of the action type
7830 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7831 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7832 * accept other config options):
7834 Property Type Description
7835 ---------------- --------------- ----------------------------------------------------------------------------------
7836 url String The url for the action (defaults to the form's url)
7837 method String The form method to use (defaults to the form's method, or POST if not defined)
7838 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7839 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7840 validate the form on the client (defaults to false)
7842 * @return {BasicForm} this
7844 doAction : function(action, options){
7845 if(typeof action == 'string'){
7846 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7848 if(this.fireEvent('beforeaction', this, action) !== false){
7849 this.beforeAction(action);
7850 action.run.defer(100, action);
7856 beforeAction : function(action){
7857 var o = action.options;
7862 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7864 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7867 // not really supported yet.. ??
7869 //if(this.waitMsgTarget === true){
7870 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7871 //}else if(this.waitMsgTarget){
7872 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7873 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7875 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7881 afterAction : function(action, success){
7882 this.activeAction = null;
7883 var o = action.options;
7888 Roo.get(document.body).unmask();
7894 //if(this.waitMsgTarget === true){
7895 // this.el.unmask();
7896 //}else if(this.waitMsgTarget){
7897 // this.waitMsgTarget.unmask();
7899 // Roo.MessageBox.updateProgress(1);
7900 // Roo.MessageBox.hide();
7907 Roo.callback(o.success, o.scope, [this, action]);
7908 this.fireEvent('actioncomplete', this, action);
7912 // failure condition..
7913 // we have a scenario where updates need confirming.
7914 // eg. if a locking scenario exists..
7915 // we look for { errors : { needs_confirm : true }} in the response.
7917 (typeof(action.result) != 'undefined') &&
7918 (typeof(action.result.errors) != 'undefined') &&
7919 (typeof(action.result.errors.needs_confirm) != 'undefined')
7922 Roo.log("not supported yet");
7925 Roo.MessageBox.confirm(
7926 "Change requires confirmation",
7927 action.result.errorMsg,
7932 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7942 Roo.callback(o.failure, o.scope, [this, action]);
7943 // show an error message if no failed handler is set..
7944 if (!this.hasListener('actionfailed')) {
7945 Roo.log("need to add dialog support");
7947 Roo.MessageBox.alert("Error",
7948 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7949 action.result.errorMsg :
7950 "Saving Failed, please check your entries or try again"
7955 this.fireEvent('actionfailed', this, action);
7960 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7961 * @param {String} id The value to search for
7964 findField : function(id){
7965 var items = this.getItems();
7966 var field = items.get(id);
7968 items.each(function(f){
7969 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7976 return field || null;
7979 * Mark fields in this form invalid in bulk.
7980 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7981 * @return {BasicForm} this
7983 markInvalid : function(errors){
7984 if(errors instanceof Array){
7985 for(var i = 0, len = errors.length; i < len; i++){
7986 var fieldError = errors[i];
7987 var f = this.findField(fieldError.id);
7989 f.markInvalid(fieldError.msg);
7995 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7996 field.markInvalid(errors[id]);
8000 //Roo.each(this.childForms || [], function (f) {
8001 // f.markInvalid(errors);
8008 * Set values for fields in this form in bulk.
8009 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8010 * @return {BasicForm} this
8012 setValues : function(values){
8013 if(values instanceof Array){ // array of objects
8014 for(var i = 0, len = values.length; i < len; i++){
8016 var f = this.findField(v.id);
8018 f.setValue(v.value);
8019 if(this.trackResetOnLoad){
8020 f.originalValue = f.getValue();
8024 }else{ // object hash
8027 if(typeof values[id] != 'function' && (field = this.findField(id))){
8029 if (field.setFromData &&
8031 field.displayField &&
8032 // combos' with local stores can
8033 // be queried via setValue()
8034 // to set their value..
8035 (field.store && !field.store.isLocal)
8039 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8040 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8041 field.setFromData(sd);
8043 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8045 field.setFromData(values);
8048 field.setValue(values[id]);
8052 if(this.trackResetOnLoad){
8053 field.originalValue = field.getValue();
8059 //Roo.each(this.childForms || [], function (f) {
8060 // f.setValues(values);
8067 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8068 * they are returned as an array.
8069 * @param {Boolean} asString
8072 getValues : function(asString){
8073 //if (this.childForms) {
8074 // copy values from the child forms
8075 // Roo.each(this.childForms, function (f) {
8076 // this.setValues(f.getValues());
8082 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8083 if(asString === true){
8086 return Roo.urlDecode(fs);
8090 * Returns the fields in this form as an object with key/value pairs.
8091 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8094 getFieldValues : function(with_hidden)
8096 var items = this.getItems();
8098 items.each(function(f){
8104 var v = f.getValue();
8106 if (f.inputType =='radio') {
8107 if (typeof(ret[f.getName()]) == 'undefined') {
8108 ret[f.getName()] = ''; // empty..
8111 if (!f.el.dom.checked) {
8119 if(f.xtype == 'MoneyField'){
8120 ret[f.currencyName] = f.getCurrency();
8123 // not sure if this supported any more..
8124 if ((typeof(v) == 'object') && f.getRawValue) {
8125 v = f.getRawValue() ; // dates..
8127 // combo boxes where name != hiddenName...
8128 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8129 ret[f.name] = f.getRawValue();
8131 ret[f.getName()] = v;
8138 * Clears all invalid messages in this form.
8139 * @return {BasicForm} this
8141 clearInvalid : function(){
8142 var items = this.getItems();
8144 items.each(function(f){
8153 * @return {BasicForm} this
8156 var items = this.getItems();
8157 items.each(function(f){
8161 Roo.each(this.childForms || [], function (f) {
8169 getItems : function()
8171 var r=new Roo.util.MixedCollection(false, function(o){
8172 return o.id || (o.id = Roo.id());
8174 var iter = function(el) {
8181 Roo.each(el.items,function(e) {
8190 hideFields : function(items)
8192 Roo.each(items, function(i){
8194 var f = this.findField(i);
8200 if(f.xtype == 'DateField'){
8201 f.setVisible(false);
8210 showFields : function(items)
8212 Roo.each(items, function(i){
8214 var f = this.findField(i);
8220 if(f.xtype == 'DateField'){
8232 Roo.apply(Roo.bootstrap.Form, {
8259 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8260 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8261 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8262 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8265 this.maskEl.top.enableDisplayMode("block");
8266 this.maskEl.left.enableDisplayMode("block");
8267 this.maskEl.bottom.enableDisplayMode("block");
8268 this.maskEl.right.enableDisplayMode("block");
8270 this.toolTip = new Roo.bootstrap.Tooltip({
8271 cls : 'roo-form-error-popover',
8273 'left' : ['r-l', [-2,0], 'right'],
8274 'right' : ['l-r', [2,0], 'left'],
8275 'bottom' : ['tl-bl', [0,2], 'top'],
8276 'top' : [ 'bl-tl', [0,-2], 'bottom']
8280 this.toolTip.render(Roo.get(document.body));
8282 this.toolTip.el.enableDisplayMode("block");
8284 Roo.get(document.body).on('click', function(){
8288 Roo.get(document.body).on('touchstart', function(){
8292 this.isApplied = true
8295 mask : function(form, target)
8299 this.target = target;
8301 if(!this.form.errorMask || !target.el){
8305 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8307 Roo.log(scrollable);
8309 var ot = this.target.el.calcOffsetsTo(scrollable);
8311 var scrollTo = ot[1] - this.form.maskOffset;
8313 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8315 scrollable.scrollTo('top', scrollTo);
8317 var box = this.target.el.getBox();
8319 var zIndex = Roo.bootstrap.Modal.zIndex++;
8322 this.maskEl.top.setStyle('position', 'absolute');
8323 this.maskEl.top.setStyle('z-index', zIndex);
8324 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8325 this.maskEl.top.setLeft(0);
8326 this.maskEl.top.setTop(0);
8327 this.maskEl.top.show();
8329 this.maskEl.left.setStyle('position', 'absolute');
8330 this.maskEl.left.setStyle('z-index', zIndex);
8331 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8332 this.maskEl.left.setLeft(0);
8333 this.maskEl.left.setTop(box.y - this.padding);
8334 this.maskEl.left.show();
8336 this.maskEl.bottom.setStyle('position', 'absolute');
8337 this.maskEl.bottom.setStyle('z-index', zIndex);
8338 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8339 this.maskEl.bottom.setLeft(0);
8340 this.maskEl.bottom.setTop(box.bottom + this.padding);
8341 this.maskEl.bottom.show();
8343 this.maskEl.right.setStyle('position', 'absolute');
8344 this.maskEl.right.setStyle('z-index', zIndex);
8345 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8346 this.maskEl.right.setLeft(box.right + this.padding);
8347 this.maskEl.right.setTop(box.y - this.padding);
8348 this.maskEl.right.show();
8350 this.toolTip.bindEl = this.target.el;
8352 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8354 var tip = this.target.blankText;
8356 if(this.target.getValue() !== '' ) {
8358 if (this.target.invalidText.length) {
8359 tip = this.target.invalidText;
8360 } else if (this.target.regexText.length){
8361 tip = this.target.regexText;
8365 this.toolTip.show(tip);
8367 this.intervalID = window.setInterval(function() {
8368 Roo.bootstrap.Form.popover.unmask();
8371 window.onwheel = function(){ return false;};
8373 (function(){ this.isMasked = true; }).defer(500, this);
8379 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8383 this.maskEl.top.setStyle('position', 'absolute');
8384 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8385 this.maskEl.top.hide();
8387 this.maskEl.left.setStyle('position', 'absolute');
8388 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8389 this.maskEl.left.hide();
8391 this.maskEl.bottom.setStyle('position', 'absolute');
8392 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8393 this.maskEl.bottom.hide();
8395 this.maskEl.right.setStyle('position', 'absolute');
8396 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8397 this.maskEl.right.hide();
8399 this.toolTip.hide();
8401 this.toolTip.el.hide();
8403 window.onwheel = function(){ return true;};
8405 if(this.intervalID){
8406 window.clearInterval(this.intervalID);
8407 this.intervalID = false;
8410 this.isMasked = false;
8420 * Ext JS Library 1.1.1
8421 * Copyright(c) 2006-2007, Ext JS, LLC.
8423 * Originally Released Under LGPL - original licence link has changed is not relivant.
8426 * <script type="text/javascript">
8429 * @class Roo.form.VTypes
8430 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8433 Roo.form.VTypes = function(){
8434 // closure these in so they are only created once.
8435 var alpha = /^[a-zA-Z_]+$/;
8436 var alphanum = /^[a-zA-Z0-9_]+$/;
8437 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8438 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8440 // All these messages and functions are configurable
8443 * The function used to validate email addresses
8444 * @param {String} value The email address
8446 'email' : function(v){
8447 return email.test(v);
8450 * The error text to display when the email validation function returns false
8453 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8455 * The keystroke filter mask to be applied on email input
8458 'emailMask' : /[a-z0-9_\.\-@]/i,
8461 * The function used to validate URLs
8462 * @param {String} value The URL
8464 'url' : function(v){
8468 * The error text to display when the url validation function returns false
8471 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8474 * The function used to validate alpha values
8475 * @param {String} value The value
8477 'alpha' : function(v){
8478 return alpha.test(v);
8481 * The error text to display when the alpha validation function returns false
8484 'alphaText' : 'This field should only contain letters and _',
8486 * The keystroke filter mask to be applied on alpha input
8489 'alphaMask' : /[a-z_]/i,
8492 * The function used to validate alphanumeric values
8493 * @param {String} value The value
8495 'alphanum' : function(v){
8496 return alphanum.test(v);
8499 * The error text to display when the alphanumeric validation function returns false
8502 'alphanumText' : 'This field should only contain letters, numbers and _',
8504 * The keystroke filter mask to be applied on alphanumeric input
8507 'alphanumMask' : /[a-z0-9_]/i
8517 * @class Roo.bootstrap.Input
8518 * @extends Roo.bootstrap.Component
8519 * Bootstrap Input class
8520 * @cfg {Boolean} disabled is it disabled
8521 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8522 * @cfg {String} name name of the input
8523 * @cfg {string} fieldLabel - the label associated
8524 * @cfg {string} placeholder - placeholder to put in text.
8525 * @cfg {string} before - input group add on before
8526 * @cfg {string} after - input group add on after
8527 * @cfg {string} size - (lg|sm) or leave empty..
8528 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8529 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8530 * @cfg {Number} md colspan out of 12 for computer-sized screens
8531 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8532 * @cfg {string} value default value of the input
8533 * @cfg {Number} labelWidth set the width of label
8534 * @cfg {Number} labellg set the width of label (1-12)
8535 * @cfg {Number} labelmd set the width of label (1-12)
8536 * @cfg {Number} labelsm set the width of label (1-12)
8537 * @cfg {Number} labelxs set the width of label (1-12)
8538 * @cfg {String} labelAlign (top|left)
8539 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8540 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8541 * @cfg {String} indicatorpos (left|right) default left
8542 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8543 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8545 * @cfg {String} align (left|center|right) Default left
8546 * @cfg {Boolean} forceFeedback (true|false) Default false
8549 * Create a new Input
8550 * @param {Object} config The config object
8553 Roo.bootstrap.Input = function(config){
8555 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8560 * Fires when this field receives input focus.
8561 * @param {Roo.form.Field} this
8566 * Fires when this field loses input focus.
8567 * @param {Roo.form.Field} this
8572 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8573 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8574 * @param {Roo.form.Field} this
8575 * @param {Roo.EventObject} e The event object
8580 * Fires just before the field blurs if the field value has changed.
8581 * @param {Roo.form.Field} this
8582 * @param {Mixed} newValue The new value
8583 * @param {Mixed} oldValue The original value
8588 * Fires after the field has been marked as invalid.
8589 * @param {Roo.form.Field} this
8590 * @param {String} msg The validation message
8595 * Fires after the field has been validated with no errors.
8596 * @param {Roo.form.Field} this
8601 * Fires after the key up
8602 * @param {Roo.form.Field} this
8603 * @param {Roo.EventObject} e The event Object
8609 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8611 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8612 automatic validation (defaults to "keyup").
8614 validationEvent : "keyup",
8616 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8618 validateOnBlur : true,
8620 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8622 validationDelay : 250,
8624 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8626 focusClass : "x-form-focus", // not needed???
8630 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8632 invalidClass : "has-warning",
8635 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8637 validClass : "has-success",
8640 * @cfg {Boolean} hasFeedback (true|false) default true
8645 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8647 invalidFeedbackClass : "glyphicon-warning-sign",
8650 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8652 validFeedbackClass : "glyphicon-ok",
8655 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8657 selectOnFocus : false,
8660 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8664 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8669 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8671 disableKeyFilter : false,
8674 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8678 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8682 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8684 blankText : "Please complete this mandatory field",
8687 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8691 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8693 maxLength : Number.MAX_VALUE,
8695 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8697 minLengthText : "The minimum length for this field is {0}",
8699 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8701 maxLengthText : "The maximum length for this field is {0}",
8705 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8706 * If available, this function will be called only after the basic validators all return true, and will be passed the
8707 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8711 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8712 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8713 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8717 * @cfg {String} regexText -- Depricated - use Invalid Text
8722 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8728 autocomplete: false,
8747 formatedValue : false,
8748 forceFeedback : false,
8750 indicatorpos : 'left',
8760 parentLabelAlign : function()
8763 while (parent.parent()) {
8764 parent = parent.parent();
8765 if (typeof(parent.labelAlign) !='undefined') {
8766 return parent.labelAlign;
8773 getAutoCreate : function()
8775 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8781 if(this.inputType != 'hidden'){
8782 cfg.cls = 'form-group' //input-group
8788 type : this.inputType,
8790 cls : 'form-control',
8791 placeholder : this.placeholder || '',
8792 autocomplete : this.autocomplete || 'new-password'
8795 if(this.capture.length){
8796 input.capture = this.capture;
8799 if(this.accept.length){
8800 input.accept = this.accept + "/*";
8804 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8807 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8808 input.maxLength = this.maxLength;
8811 if (this.disabled) {
8812 input.disabled=true;
8815 if (this.readOnly) {
8816 input.readonly=true;
8820 input.name = this.name;
8824 input.cls += ' input-' + this.size;
8828 ['xs','sm','md','lg'].map(function(size){
8829 if (settings[size]) {
8830 cfg.cls += ' col-' + size + '-' + settings[size];
8834 var inputblock = input;
8838 cls: 'glyphicon form-control-feedback'
8841 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8844 cls : 'has-feedback',
8852 if (this.before || this.after) {
8855 cls : 'input-group',
8859 if (this.before && typeof(this.before) == 'string') {
8861 inputblock.cn.push({
8863 cls : 'roo-input-before input-group-addon',
8867 if (this.before && typeof(this.before) == 'object') {
8868 this.before = Roo.factory(this.before);
8870 inputblock.cn.push({
8872 cls : 'roo-input-before input-group-' +
8873 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8877 inputblock.cn.push(input);
8879 if (this.after && typeof(this.after) == 'string') {
8880 inputblock.cn.push({
8882 cls : 'roo-input-after input-group-addon',
8886 if (this.after && typeof(this.after) == 'object') {
8887 this.after = Roo.factory(this.after);
8889 inputblock.cn.push({
8891 cls : 'roo-input-after input-group-' +
8892 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8896 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8897 inputblock.cls += ' has-feedback';
8898 inputblock.cn.push(feedback);
8902 if (align ==='left' && this.fieldLabel.length) {
8904 cfg.cls += ' roo-form-group-label-left';
8909 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8910 tooltip : 'This field is required'
8915 cls : 'control-label',
8916 html : this.fieldLabel
8927 var labelCfg = cfg.cn[1];
8928 var contentCfg = cfg.cn[2];
8930 if(this.indicatorpos == 'right'){
8935 cls : 'control-label',
8939 html : this.fieldLabel
8943 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8944 tooltip : 'This field is required'
8957 labelCfg = cfg.cn[0];
8958 contentCfg = cfg.cn[1];
8962 if(this.labelWidth > 12){
8963 labelCfg.style = "width: " + this.labelWidth + 'px';
8966 if(this.labelWidth < 13 && this.labelmd == 0){
8967 this.labelmd = this.labelWidth;
8970 if(this.labellg > 0){
8971 labelCfg.cls += ' col-lg-' + this.labellg;
8972 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8975 if(this.labelmd > 0){
8976 labelCfg.cls += ' col-md-' + this.labelmd;
8977 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8980 if(this.labelsm > 0){
8981 labelCfg.cls += ' col-sm-' + this.labelsm;
8982 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8985 if(this.labelxs > 0){
8986 labelCfg.cls += ' col-xs-' + this.labelxs;
8987 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8991 } else if ( this.fieldLabel.length) {
8996 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8997 tooltip : 'This field is required'
9001 //cls : 'input-group-addon',
9002 html : this.fieldLabel
9010 if(this.indicatorpos == 'right'){
9015 //cls : 'input-group-addon',
9016 html : this.fieldLabel
9021 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9022 tooltip : 'This field is required'
9042 if (this.parentType === 'Navbar' && this.parent().bar) {
9043 cfg.cls += ' navbar-form';
9046 if (this.parentType === 'NavGroup') {
9047 cfg.cls += ' navbar-form';
9055 * return the real input element.
9057 inputEl: function ()
9059 return this.el.select('input.form-control',true).first();
9062 tooltipEl : function()
9064 return this.inputEl();
9067 indicatorEl : function()
9069 var indicator = this.el.select('i.roo-required-indicator',true).first();
9079 setDisabled : function(v)
9081 var i = this.inputEl().dom;
9083 i.removeAttribute('disabled');
9087 i.setAttribute('disabled','true');
9089 initEvents : function()
9092 this.inputEl().on("keydown" , this.fireKey, this);
9093 this.inputEl().on("focus", this.onFocus, this);
9094 this.inputEl().on("blur", this.onBlur, this);
9096 this.inputEl().relayEvent('keyup', this);
9098 this.indicator = this.indicatorEl();
9101 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9104 // reference to original value for reset
9105 this.originalValue = this.getValue();
9106 //Roo.form.TextField.superclass.initEvents.call(this);
9107 if(this.validationEvent == 'keyup'){
9108 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9109 this.inputEl().on('keyup', this.filterValidation, this);
9111 else if(this.validationEvent !== false){
9112 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9115 if(this.selectOnFocus){
9116 this.on("focus", this.preFocus, this);
9119 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9120 this.inputEl().on("keypress", this.filterKeys, this);
9122 this.inputEl().relayEvent('keypress', this);
9125 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9126 this.el.on("click", this.autoSize, this);
9129 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9130 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9133 if (typeof(this.before) == 'object') {
9134 this.before.render(this.el.select('.roo-input-before',true).first());
9136 if (typeof(this.after) == 'object') {
9137 this.after.render(this.el.select('.roo-input-after',true).first());
9140 this.inputEl().on('change', this.onChange, this);
9143 filterValidation : function(e){
9144 if(!e.isNavKeyPress()){
9145 this.validationTask.delay(this.validationDelay);
9149 * Validates the field value
9150 * @return {Boolean} True if the value is valid, else false
9152 validate : function(){
9153 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9154 if(this.disabled || this.validateValue(this.getRawValue())){
9165 * Validates a value according to the field's validation rules and marks the field as invalid
9166 * if the validation fails
9167 * @param {Mixed} value The value to validate
9168 * @return {Boolean} True if the value is valid, else false
9170 validateValue : function(value)
9172 if(this.getVisibilityEl().hasClass('hidden')){
9176 if(value.length < 1) { // if it's blank
9177 if(this.allowBlank){
9183 if(value.length < this.minLength){
9186 if(value.length > this.maxLength){
9190 var vt = Roo.form.VTypes;
9191 if(!vt[this.vtype](value, this)){
9195 if(typeof this.validator == "function"){
9196 var msg = this.validator(value);
9200 if (typeof(msg) == 'string') {
9201 this.invalidText = msg;
9205 if(this.regex && !this.regex.test(value)){
9213 fireKey : function(e){
9214 //Roo.log('field ' + e.getKey());
9215 if(e.isNavKeyPress()){
9216 this.fireEvent("specialkey", this, e);
9219 focus : function (selectText){
9221 this.inputEl().focus();
9222 if(selectText === true){
9223 this.inputEl().dom.select();
9229 onFocus : function(){
9230 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9231 // this.el.addClass(this.focusClass);
9234 this.hasFocus = true;
9235 this.startValue = this.getValue();
9236 this.fireEvent("focus", this);
9240 beforeBlur : Roo.emptyFn,
9244 onBlur : function(){
9246 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9247 //this.el.removeClass(this.focusClass);
9249 this.hasFocus = false;
9250 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9253 var v = this.getValue();
9254 if(String(v) !== String(this.startValue)){
9255 this.fireEvent('change', this, v, this.startValue);
9257 this.fireEvent("blur", this);
9260 onChange : function(e)
9262 var v = this.getValue();
9263 if(String(v) !== String(this.startValue)){
9264 this.fireEvent('change', this, v, this.startValue);
9270 * Resets the current field value to the originally loaded value and clears any validation messages
9273 this.setValue(this.originalValue);
9277 * Returns the name of the field
9278 * @return {Mixed} name The name field
9280 getName: function(){
9284 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9285 * @return {Mixed} value The field value
9287 getValue : function(){
9289 var v = this.inputEl().getValue();
9294 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9295 * @return {Mixed} value The field value
9297 getRawValue : function(){
9298 var v = this.inputEl().getValue();
9304 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9305 * @param {Mixed} value The value to set
9307 setRawValue : function(v){
9308 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9311 selectText : function(start, end){
9312 var v = this.getRawValue();
9314 start = start === undefined ? 0 : start;
9315 end = end === undefined ? v.length : end;
9316 var d = this.inputEl().dom;
9317 if(d.setSelectionRange){
9318 d.setSelectionRange(start, end);
9319 }else if(d.createTextRange){
9320 var range = d.createTextRange();
9321 range.moveStart("character", start);
9322 range.moveEnd("character", v.length-end);
9329 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9330 * @param {Mixed} value The value to set
9332 setValue : function(v){
9335 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9341 processValue : function(value){
9342 if(this.stripCharsRe){
9343 var newValue = value.replace(this.stripCharsRe, '');
9344 if(newValue !== value){
9345 this.setRawValue(newValue);
9352 preFocus : function(){
9354 if(this.selectOnFocus){
9355 this.inputEl().dom.select();
9358 filterKeys : function(e){
9360 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9363 var c = e.getCharCode(), cc = String.fromCharCode(c);
9364 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9367 if(!this.maskRe.test(cc)){
9372 * Clear any invalid styles/messages for this field
9374 clearInvalid : function(){
9376 if(!this.el || this.preventMark){ // not rendered
9381 this.el.removeClass(this.invalidClass);
9383 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9385 var feedback = this.el.select('.form-control-feedback', true).first();
9388 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9393 this.fireEvent('valid', this);
9397 * Mark this field as valid
9399 markValid : function()
9401 if(!this.el || this.preventMark){ // not rendered...
9405 this.el.removeClass([this.invalidClass, this.validClass]);
9407 var feedback = this.el.select('.form-control-feedback', true).first();
9410 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9414 this.indicator.removeClass('visible');
9415 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9422 if(this.allowBlank && !this.getRawValue().length){
9426 this.el.addClass(this.validClass);
9428 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9430 var feedback = this.el.select('.form-control-feedback', true).first();
9433 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9434 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9439 this.fireEvent('valid', this);
9443 * Mark this field as invalid
9444 * @param {String} msg The validation message
9446 markInvalid : function(msg)
9448 if(!this.el || this.preventMark){ // not rendered
9452 this.el.removeClass([this.invalidClass, this.validClass]);
9454 var feedback = this.el.select('.form-control-feedback', true).first();
9457 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9464 if(this.allowBlank && !this.getRawValue().length){
9469 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9470 this.indicator.addClass('visible');
9473 this.el.addClass(this.invalidClass);
9475 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9477 var feedback = this.el.select('.form-control-feedback', true).first();
9480 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9482 if(this.getValue().length || this.forceFeedback){
9483 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9490 this.fireEvent('invalid', this, msg);
9493 SafariOnKeyDown : function(event)
9495 // this is a workaround for a password hang bug on chrome/ webkit.
9496 if (this.inputEl().dom.type != 'password') {
9500 var isSelectAll = false;
9502 if(this.inputEl().dom.selectionEnd > 0){
9503 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9505 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9506 event.preventDefault();
9511 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9513 event.preventDefault();
9514 // this is very hacky as keydown always get's upper case.
9516 var cc = String.fromCharCode(event.getCharCode());
9517 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9521 adjustWidth : function(tag, w){
9522 tag = tag.toLowerCase();
9523 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9524 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9528 if(tag == 'textarea'){
9531 }else if(Roo.isOpera){
9535 if(tag == 'textarea'){
9543 setFieldLabel : function(v)
9550 var ar = this.el.select('label > span',true);
9552 if (ar.elements.length) {
9553 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9554 this.fieldLabel = v;
9558 var br = this.el.select('label',true);
9560 if(br.elements.length) {
9561 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9562 this.fieldLabel = v;
9566 Roo.log('Cannot Found any of label > span || label in input');
9570 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9571 this.fieldLabel = v;
9586 * @class Roo.bootstrap.TextArea
9587 * @extends Roo.bootstrap.Input
9588 * Bootstrap TextArea class
9589 * @cfg {Number} cols Specifies the visible width of a text area
9590 * @cfg {Number} rows Specifies the visible number of lines in a text area
9591 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9592 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9593 * @cfg {string} html text
9596 * Create a new TextArea
9597 * @param {Object} config The config object
9600 Roo.bootstrap.TextArea = function(config){
9601 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9605 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9615 getAutoCreate : function(){
9617 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9623 if(this.inputType != 'hidden'){
9624 cfg.cls = 'form-group' //input-group
9632 value : this.value || '',
9633 html: this.html || '',
9634 cls : 'form-control',
9635 placeholder : this.placeholder || ''
9639 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9640 input.maxLength = this.maxLength;
9644 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9648 input.cols = this.cols;
9651 if (this.readOnly) {
9652 input.readonly = true;
9656 input.name = this.name;
9660 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9664 ['xs','sm','md','lg'].map(function(size){
9665 if (settings[size]) {
9666 cfg.cls += ' col-' + size + '-' + settings[size];
9670 var inputblock = input;
9672 if(this.hasFeedback && !this.allowBlank){
9676 cls: 'glyphicon form-control-feedback'
9680 cls : 'has-feedback',
9689 if (this.before || this.after) {
9692 cls : 'input-group',
9696 inputblock.cn.push({
9698 cls : 'input-group-addon',
9703 inputblock.cn.push(input);
9705 if(this.hasFeedback && !this.allowBlank){
9706 inputblock.cls += ' has-feedback';
9707 inputblock.cn.push(feedback);
9711 inputblock.cn.push({
9713 cls : 'input-group-addon',
9720 if (align ==='left' && this.fieldLabel.length) {
9725 cls : 'control-label',
9726 html : this.fieldLabel
9737 if(this.labelWidth > 12){
9738 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9741 if(this.labelWidth < 13 && this.labelmd == 0){
9742 this.labelmd = this.labelWidth;
9745 if(this.labellg > 0){
9746 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9747 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9750 if(this.labelmd > 0){
9751 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9752 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9755 if(this.labelsm > 0){
9756 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9757 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9760 if(this.labelxs > 0){
9761 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9762 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9765 } else if ( this.fieldLabel.length) {
9770 //cls : 'input-group-addon',
9771 html : this.fieldLabel
9789 if (this.disabled) {
9790 input.disabled=true;
9797 * return the real textarea element.
9799 inputEl: function ()
9801 return this.el.select('textarea.form-control',true).first();
9805 * Clear any invalid styles/messages for this field
9807 clearInvalid : function()
9810 if(!this.el || this.preventMark){ // not rendered
9814 var label = this.el.select('label', true).first();
9815 var icon = this.el.select('i.fa-star', true).first();
9821 this.el.removeClass(this.invalidClass);
9823 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9825 var feedback = this.el.select('.form-control-feedback', true).first();
9828 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9833 this.fireEvent('valid', this);
9837 * Mark this field as valid
9839 markValid : function()
9841 if(!this.el || this.preventMark){ // not rendered
9845 this.el.removeClass([this.invalidClass, this.validClass]);
9847 var feedback = this.el.select('.form-control-feedback', true).first();
9850 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9853 if(this.disabled || this.allowBlank){
9857 var label = this.el.select('label', true).first();
9858 var icon = this.el.select('i.fa-star', true).first();
9864 this.el.addClass(this.validClass);
9866 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9868 var feedback = this.el.select('.form-control-feedback', true).first();
9871 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9872 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9877 this.fireEvent('valid', this);
9881 * Mark this field as invalid
9882 * @param {String} msg The validation message
9884 markInvalid : function(msg)
9886 if(!this.el || this.preventMark){ // not rendered
9890 this.el.removeClass([this.invalidClass, this.validClass]);
9892 var feedback = this.el.select('.form-control-feedback', true).first();
9895 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9898 if(this.disabled || this.allowBlank){
9902 var label = this.el.select('label', true).first();
9903 var icon = this.el.select('i.fa-star', true).first();
9905 if(!this.getValue().length && label && !icon){
9906 this.el.createChild({
9908 cls : 'text-danger fa fa-lg fa-star',
9909 tooltip : 'This field is required',
9910 style : 'margin-right:5px;'
9914 this.el.addClass(this.invalidClass);
9916 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9918 var feedback = this.el.select('.form-control-feedback', true).first();
9921 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9923 if(this.getValue().length || this.forceFeedback){
9924 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9931 this.fireEvent('invalid', this, msg);
9939 * trigger field - base class for combo..
9944 * @class Roo.bootstrap.TriggerField
9945 * @extends Roo.bootstrap.Input
9946 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9947 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9948 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9949 * for which you can provide a custom implementation. For example:
9951 var trigger = new Roo.bootstrap.TriggerField();
9952 trigger.onTriggerClick = myTriggerFn;
9953 trigger.applyTo('my-field');
9956 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9957 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9958 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9959 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9960 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9963 * Create a new TriggerField.
9964 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9965 * to the base TextField)
9967 Roo.bootstrap.TriggerField = function(config){
9968 this.mimicing = false;
9969 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9972 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9974 * @cfg {String} triggerClass A CSS class to apply to the trigger
9977 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9982 * @cfg {Boolean} removable (true|false) special filter default false
9986 /** @cfg {Boolean} grow @hide */
9987 /** @cfg {Number} growMin @hide */
9988 /** @cfg {Number} growMax @hide */
9994 autoSize: Roo.emptyFn,
10001 actionMode : 'wrap',
10006 getAutoCreate : function(){
10008 var align = this.labelAlign || this.parentLabelAlign();
10013 cls: 'form-group' //input-group
10020 type : this.inputType,
10021 cls : 'form-control',
10022 autocomplete: 'new-password',
10023 placeholder : this.placeholder || ''
10027 input.name = this.name;
10030 input.cls += ' input-' + this.size;
10033 if (this.disabled) {
10034 input.disabled=true;
10037 var inputblock = input;
10039 if(this.hasFeedback && !this.allowBlank){
10043 cls: 'glyphicon form-control-feedback'
10046 if(this.removable && !this.editable && !this.tickable){
10048 cls : 'has-feedback',
10054 cls : 'roo-combo-removable-btn close'
10061 cls : 'has-feedback',
10070 if(this.removable && !this.editable && !this.tickable){
10072 cls : 'roo-removable',
10078 cls : 'roo-combo-removable-btn close'
10085 if (this.before || this.after) {
10088 cls : 'input-group',
10092 inputblock.cn.push({
10094 cls : 'input-group-addon',
10099 inputblock.cn.push(input);
10101 if(this.hasFeedback && !this.allowBlank){
10102 inputblock.cls += ' has-feedback';
10103 inputblock.cn.push(feedback);
10107 inputblock.cn.push({
10109 cls : 'input-group-addon',
10122 cls: 'form-hidden-field'
10136 cls: 'form-hidden-field'
10140 cls: 'roo-select2-choices',
10144 cls: 'roo-select2-search-field',
10157 cls: 'roo-select2-container input-group',
10162 // cls: 'typeahead typeahead-long dropdown-menu',
10163 // style: 'display:none'
10168 if(!this.multiple && this.showToggleBtn){
10174 if (this.caret != false) {
10177 cls: 'fa fa-' + this.caret
10184 cls : 'input-group-addon btn dropdown-toggle',
10189 cls: 'combobox-clear',
10203 combobox.cls += ' roo-select2-container-multi';
10206 if (align ==='left' && this.fieldLabel.length) {
10208 cfg.cls += ' roo-form-group-label-left';
10213 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10214 tooltip : 'This field is required'
10219 cls : 'control-label',
10220 html : this.fieldLabel
10232 var labelCfg = cfg.cn[1];
10233 var contentCfg = cfg.cn[2];
10235 if(this.indicatorpos == 'right'){
10240 cls : 'control-label',
10244 html : this.fieldLabel
10248 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10249 tooltip : 'This field is required'
10262 labelCfg = cfg.cn[0];
10263 contentCfg = cfg.cn[1];
10266 if(this.labelWidth > 12){
10267 labelCfg.style = "width: " + this.labelWidth + 'px';
10270 if(this.labelWidth < 13 && this.labelmd == 0){
10271 this.labelmd = this.labelWidth;
10274 if(this.labellg > 0){
10275 labelCfg.cls += ' col-lg-' + this.labellg;
10276 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10279 if(this.labelmd > 0){
10280 labelCfg.cls += ' col-md-' + this.labelmd;
10281 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10284 if(this.labelsm > 0){
10285 labelCfg.cls += ' col-sm-' + this.labelsm;
10286 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10289 if(this.labelxs > 0){
10290 labelCfg.cls += ' col-xs-' + this.labelxs;
10291 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10294 } else if ( this.fieldLabel.length) {
10295 // Roo.log(" label");
10299 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10300 tooltip : 'This field is required'
10304 //cls : 'input-group-addon',
10305 html : this.fieldLabel
10313 if(this.indicatorpos == 'right'){
10321 html : this.fieldLabel
10325 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10326 tooltip : 'This field is required'
10339 // Roo.log(" no label && no align");
10346 ['xs','sm','md','lg'].map(function(size){
10347 if (settings[size]) {
10348 cfg.cls += ' col-' + size + '-' + settings[size];
10359 onResize : function(w, h){
10360 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10361 // if(typeof w == 'number'){
10362 // var x = w - this.trigger.getWidth();
10363 // this.inputEl().setWidth(this.adjustWidth('input', x));
10364 // this.trigger.setStyle('left', x+'px');
10369 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10372 getResizeEl : function(){
10373 return this.inputEl();
10377 getPositionEl : function(){
10378 return this.inputEl();
10382 alignErrorIcon : function(){
10383 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10387 initEvents : function(){
10391 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10392 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10393 if(!this.multiple && this.showToggleBtn){
10394 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10395 if(this.hideTrigger){
10396 this.trigger.setDisplayed(false);
10398 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10402 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10405 if(this.removable && !this.editable && !this.tickable){
10406 var close = this.closeTriggerEl();
10409 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10410 close.on('click', this.removeBtnClick, this, close);
10414 //this.trigger.addClassOnOver('x-form-trigger-over');
10415 //this.trigger.addClassOnClick('x-form-trigger-click');
10418 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10422 closeTriggerEl : function()
10424 var close = this.el.select('.roo-combo-removable-btn', true).first();
10425 return close ? close : false;
10428 removeBtnClick : function(e, h, el)
10430 e.preventDefault();
10432 if(this.fireEvent("remove", this) !== false){
10434 this.fireEvent("afterremove", this)
10438 createList : function()
10440 this.list = Roo.get(document.body).createChild({
10442 cls: 'typeahead typeahead-long dropdown-menu',
10443 style: 'display:none'
10446 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10451 initTrigger : function(){
10456 onDestroy : function(){
10458 this.trigger.removeAllListeners();
10459 // this.trigger.remove();
10462 // this.wrap.remove();
10464 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10468 onFocus : function(){
10469 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10471 if(!this.mimicing){
10472 this.wrap.addClass('x-trigger-wrap-focus');
10473 this.mimicing = true;
10474 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10475 if(this.monitorTab){
10476 this.el.on("keydown", this.checkTab, this);
10483 checkTab : function(e){
10484 if(e.getKey() == e.TAB){
10485 this.triggerBlur();
10490 onBlur : function(){
10495 mimicBlur : function(e, t){
10497 if(!this.wrap.contains(t) && this.validateBlur()){
10498 this.triggerBlur();
10504 triggerBlur : function(){
10505 this.mimicing = false;
10506 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10507 if(this.monitorTab){
10508 this.el.un("keydown", this.checkTab, this);
10510 //this.wrap.removeClass('x-trigger-wrap-focus');
10511 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10515 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10516 validateBlur : function(e, t){
10521 onDisable : function(){
10522 this.inputEl().dom.disabled = true;
10523 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10525 // this.wrap.addClass('x-item-disabled');
10530 onEnable : function(){
10531 this.inputEl().dom.disabled = false;
10532 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10534 // this.el.removeClass('x-item-disabled');
10539 onShow : function(){
10540 var ae = this.getActionEl();
10543 ae.dom.style.display = '';
10544 ae.dom.style.visibility = 'visible';
10550 onHide : function(){
10551 var ae = this.getActionEl();
10552 ae.dom.style.display = 'none';
10556 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10557 * by an implementing function.
10559 * @param {EventObject} e
10561 onTriggerClick : Roo.emptyFn
10565 * Ext JS Library 1.1.1
10566 * Copyright(c) 2006-2007, Ext JS, LLC.
10568 * Originally Released Under LGPL - original licence link has changed is not relivant.
10571 * <script type="text/javascript">
10576 * @class Roo.data.SortTypes
10578 * Defines the default sorting (casting?) comparison functions used when sorting data.
10580 Roo.data.SortTypes = {
10582 * Default sort that does nothing
10583 * @param {Mixed} s The value being converted
10584 * @return {Mixed} The comparison value
10586 none : function(s){
10591 * The regular expression used to strip tags
10595 stripTagsRE : /<\/?[^>]+>/gi,
10598 * Strips all HTML tags to sort on text only
10599 * @param {Mixed} s The value being converted
10600 * @return {String} The comparison value
10602 asText : function(s){
10603 return String(s).replace(this.stripTagsRE, "");
10607 * Strips all HTML tags to sort on text only - Case insensitive
10608 * @param {Mixed} s The value being converted
10609 * @return {String} The comparison value
10611 asUCText : function(s){
10612 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10616 * Case insensitive string
10617 * @param {Mixed} s The value being converted
10618 * @return {String} The comparison value
10620 asUCString : function(s) {
10621 return String(s).toUpperCase();
10626 * @param {Mixed} s The value being converted
10627 * @return {Number} The comparison value
10629 asDate : function(s) {
10633 if(s instanceof Date){
10634 return s.getTime();
10636 return Date.parse(String(s));
10641 * @param {Mixed} s The value being converted
10642 * @return {Float} The comparison value
10644 asFloat : function(s) {
10645 var val = parseFloat(String(s).replace(/,/g, ""));
10654 * @param {Mixed} s The value being converted
10655 * @return {Number} The comparison value
10657 asInt : function(s) {
10658 var val = parseInt(String(s).replace(/,/g, ""));
10666 * Ext JS Library 1.1.1
10667 * Copyright(c) 2006-2007, Ext JS, LLC.
10669 * Originally Released Under LGPL - original licence link has changed is not relivant.
10672 * <script type="text/javascript">
10676 * @class Roo.data.Record
10677 * Instances of this class encapsulate both record <em>definition</em> information, and record
10678 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10679 * to access Records cached in an {@link Roo.data.Store} object.<br>
10681 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10682 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10685 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10687 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10688 * {@link #create}. The parameters are the same.
10689 * @param {Array} data An associative Array of data values keyed by the field name.
10690 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10691 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10692 * not specified an integer id is generated.
10694 Roo.data.Record = function(data, id){
10695 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10700 * Generate a constructor for a specific record layout.
10701 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10702 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10703 * Each field definition object may contain the following properties: <ul>
10704 * <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,
10705 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10706 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10707 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10708 * is being used, then this is a string containing the javascript expression to reference the data relative to
10709 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10710 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10711 * this may be omitted.</p></li>
10712 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10713 * <ul><li>auto (Default, implies no conversion)</li>
10718 * <li>date</li></ul></p></li>
10719 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10720 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10721 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10722 * by the Reader into an object that will be stored in the Record. It is passed the
10723 * following parameters:<ul>
10724 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10726 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10728 * <br>usage:<br><pre><code>
10729 var TopicRecord = Roo.data.Record.create(
10730 {name: 'title', mapping: 'topic_title'},
10731 {name: 'author', mapping: 'username'},
10732 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10733 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10734 {name: 'lastPoster', mapping: 'user2'},
10735 {name: 'excerpt', mapping: 'post_text'}
10738 var myNewRecord = new TopicRecord({
10739 title: 'Do my job please',
10742 lastPost: new Date(),
10743 lastPoster: 'Animal',
10744 excerpt: 'No way dude!'
10746 myStore.add(myNewRecord);
10751 Roo.data.Record.create = function(o){
10752 var f = function(){
10753 f.superclass.constructor.apply(this, arguments);
10755 Roo.extend(f, Roo.data.Record);
10756 var p = f.prototype;
10757 p.fields = new Roo.util.MixedCollection(false, function(field){
10760 for(var i = 0, len = o.length; i < len; i++){
10761 p.fields.add(new Roo.data.Field(o[i]));
10763 f.getField = function(name){
10764 return p.fields.get(name);
10769 Roo.data.Record.AUTO_ID = 1000;
10770 Roo.data.Record.EDIT = 'edit';
10771 Roo.data.Record.REJECT = 'reject';
10772 Roo.data.Record.COMMIT = 'commit';
10774 Roo.data.Record.prototype = {
10776 * Readonly flag - true if this record has been modified.
10785 join : function(store){
10786 this.store = store;
10790 * Set the named field to the specified value.
10791 * @param {String} name The name of the field to set.
10792 * @param {Object} value The value to set the field to.
10794 set : function(name, value){
10795 if(this.data[name] == value){
10799 if(!this.modified){
10800 this.modified = {};
10802 if(typeof this.modified[name] == 'undefined'){
10803 this.modified[name] = this.data[name];
10805 this.data[name] = value;
10806 if(!this.editing && this.store){
10807 this.store.afterEdit(this);
10812 * Get the value of the named field.
10813 * @param {String} name The name of the field to get the value of.
10814 * @return {Object} The value of the field.
10816 get : function(name){
10817 return this.data[name];
10821 beginEdit : function(){
10822 this.editing = true;
10823 this.modified = {};
10827 cancelEdit : function(){
10828 this.editing = false;
10829 delete this.modified;
10833 endEdit : function(){
10834 this.editing = false;
10835 if(this.dirty && this.store){
10836 this.store.afterEdit(this);
10841 * Usually called by the {@link Roo.data.Store} which owns the Record.
10842 * Rejects all changes made to the Record since either creation, or the last commit operation.
10843 * Modified fields are reverted to their original values.
10845 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10846 * of reject operations.
10848 reject : function(){
10849 var m = this.modified;
10851 if(typeof m[n] != "function"){
10852 this.data[n] = m[n];
10855 this.dirty = false;
10856 delete this.modified;
10857 this.editing = false;
10859 this.store.afterReject(this);
10864 * Usually called by the {@link Roo.data.Store} which owns the Record.
10865 * Commits all changes made to the Record since either creation, or the last commit operation.
10867 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10868 * of commit operations.
10870 commit : function(){
10871 this.dirty = false;
10872 delete this.modified;
10873 this.editing = false;
10875 this.store.afterCommit(this);
10880 hasError : function(){
10881 return this.error != null;
10885 clearError : function(){
10890 * Creates a copy of this record.
10891 * @param {String} id (optional) A new record id if you don't want to use this record's id
10894 copy : function(newId) {
10895 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10899 * Ext JS Library 1.1.1
10900 * Copyright(c) 2006-2007, Ext JS, LLC.
10902 * Originally Released Under LGPL - original licence link has changed is not relivant.
10905 * <script type="text/javascript">
10911 * @class Roo.data.Store
10912 * @extends Roo.util.Observable
10913 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10914 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10916 * 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
10917 * has no knowledge of the format of the data returned by the Proxy.<br>
10919 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10920 * instances from the data object. These records are cached and made available through accessor functions.
10922 * Creates a new Store.
10923 * @param {Object} config A config object containing the objects needed for the Store to access data,
10924 * and read the data into Records.
10926 Roo.data.Store = function(config){
10927 this.data = new Roo.util.MixedCollection(false);
10928 this.data.getKey = function(o){
10931 this.baseParams = {};
10933 this.paramNames = {
10938 "multisort" : "_multisort"
10941 if(config && config.data){
10942 this.inlineData = config.data;
10943 delete config.data;
10946 Roo.apply(this, config);
10948 if(this.reader){ // reader passed
10949 this.reader = Roo.factory(this.reader, Roo.data);
10950 this.reader.xmodule = this.xmodule || false;
10951 if(!this.recordType){
10952 this.recordType = this.reader.recordType;
10954 if(this.reader.onMetaChange){
10955 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10959 if(this.recordType){
10960 this.fields = this.recordType.prototype.fields;
10962 this.modified = [];
10966 * @event datachanged
10967 * Fires when the data cache has changed, and a widget which is using this Store
10968 * as a Record cache should refresh its view.
10969 * @param {Store} this
10971 datachanged : true,
10973 * @event metachange
10974 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10975 * @param {Store} this
10976 * @param {Object} meta The JSON metadata
10981 * Fires when Records have been added to the Store
10982 * @param {Store} this
10983 * @param {Roo.data.Record[]} records The array of Records added
10984 * @param {Number} index The index at which the record(s) were added
10989 * Fires when a Record has been removed from the Store
10990 * @param {Store} this
10991 * @param {Roo.data.Record} record The Record that was removed
10992 * @param {Number} index The index at which the record was removed
10997 * Fires when a Record has been updated
10998 * @param {Store} this
10999 * @param {Roo.data.Record} record The Record that was updated
11000 * @param {String} operation The update operation being performed. Value may be one of:
11002 Roo.data.Record.EDIT
11003 Roo.data.Record.REJECT
11004 Roo.data.Record.COMMIT
11010 * Fires when the data cache has been cleared.
11011 * @param {Store} this
11015 * @event beforeload
11016 * Fires before a request is made for a new data object. If the beforeload handler returns false
11017 * the load action will be canceled.
11018 * @param {Store} this
11019 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11023 * @event beforeloadadd
11024 * Fires after a new set of Records has been loaded.
11025 * @param {Store} this
11026 * @param {Roo.data.Record[]} records The Records that were loaded
11027 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11029 beforeloadadd : true,
11032 * Fires after a new set of Records has been loaded, before they are added to the store.
11033 * @param {Store} this
11034 * @param {Roo.data.Record[]} records The Records that were loaded
11035 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11036 * @params {Object} return from reader
11040 * @event loadexception
11041 * Fires if an exception occurs in the Proxy during loading.
11042 * Called with the signature of the Proxy's "loadexception" event.
11043 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11046 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11047 * @param {Object} load options
11048 * @param {Object} jsonData from your request (normally this contains the Exception)
11050 loadexception : true
11054 this.proxy = Roo.factory(this.proxy, Roo.data);
11055 this.proxy.xmodule = this.xmodule || false;
11056 this.relayEvents(this.proxy, ["loadexception"]);
11058 this.sortToggle = {};
11059 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11061 Roo.data.Store.superclass.constructor.call(this);
11063 if(this.inlineData){
11064 this.loadData(this.inlineData);
11065 delete this.inlineData;
11069 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11071 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11072 * without a remote query - used by combo/forms at present.
11076 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11079 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11082 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11083 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11086 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11087 * on any HTTP request
11090 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11093 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11097 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11098 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11100 remoteSort : false,
11103 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11104 * loaded or when a record is removed. (defaults to false).
11106 pruneModifiedRecords : false,
11109 lastOptions : null,
11112 * Add Records to the Store and fires the add event.
11113 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11115 add : function(records){
11116 records = [].concat(records);
11117 for(var i = 0, len = records.length; i < len; i++){
11118 records[i].join(this);
11120 var index = this.data.length;
11121 this.data.addAll(records);
11122 this.fireEvent("add", this, records, index);
11126 * Remove a Record from the Store and fires the remove event.
11127 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11129 remove : function(record){
11130 var index = this.data.indexOf(record);
11131 this.data.removeAt(index);
11133 if(this.pruneModifiedRecords){
11134 this.modified.remove(record);
11136 this.fireEvent("remove", this, record, index);
11140 * Remove all Records from the Store and fires the clear event.
11142 removeAll : function(){
11144 if(this.pruneModifiedRecords){
11145 this.modified = [];
11147 this.fireEvent("clear", this);
11151 * Inserts Records to the Store at the given index and fires the add event.
11152 * @param {Number} index The start index at which to insert the passed Records.
11153 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11155 insert : function(index, records){
11156 records = [].concat(records);
11157 for(var i = 0, len = records.length; i < len; i++){
11158 this.data.insert(index, records[i]);
11159 records[i].join(this);
11161 this.fireEvent("add", this, records, index);
11165 * Get the index within the cache of the passed Record.
11166 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11167 * @return {Number} The index of the passed Record. Returns -1 if not found.
11169 indexOf : function(record){
11170 return this.data.indexOf(record);
11174 * Get the index within the cache of the Record with the passed id.
11175 * @param {String} id The id of the Record to find.
11176 * @return {Number} The index of the Record. Returns -1 if not found.
11178 indexOfId : function(id){
11179 return this.data.indexOfKey(id);
11183 * Get the Record with the specified id.
11184 * @param {String} id The id of the Record to find.
11185 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11187 getById : function(id){
11188 return this.data.key(id);
11192 * Get the Record at the specified index.
11193 * @param {Number} index The index of the Record to find.
11194 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11196 getAt : function(index){
11197 return this.data.itemAt(index);
11201 * Returns a range of Records between specified indices.
11202 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11203 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11204 * @return {Roo.data.Record[]} An array of Records
11206 getRange : function(start, end){
11207 return this.data.getRange(start, end);
11211 storeOptions : function(o){
11212 o = Roo.apply({}, o);
11215 this.lastOptions = o;
11219 * Loads the Record cache from the configured Proxy using the configured Reader.
11221 * If using remote paging, then the first load call must specify the <em>start</em>
11222 * and <em>limit</em> properties in the options.params property to establish the initial
11223 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11225 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11226 * and this call will return before the new data has been loaded. Perform any post-processing
11227 * in a callback function, or in a "load" event handler.</strong>
11229 * @param {Object} options An object containing properties which control loading options:<ul>
11230 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11231 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11232 * passed the following arguments:<ul>
11233 * <li>r : Roo.data.Record[]</li>
11234 * <li>options: Options object from the load call</li>
11235 * <li>success: Boolean success indicator</li></ul></li>
11236 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11237 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11240 load : function(options){
11241 options = options || {};
11242 if(this.fireEvent("beforeload", this, options) !== false){
11243 this.storeOptions(options);
11244 var p = Roo.apply(options.params || {}, this.baseParams);
11245 // if meta was not loaded from remote source.. try requesting it.
11246 if (!this.reader.metaFromRemote) {
11247 p._requestMeta = 1;
11249 if(this.sortInfo && this.remoteSort){
11250 var pn = this.paramNames;
11251 p[pn["sort"]] = this.sortInfo.field;
11252 p[pn["dir"]] = this.sortInfo.direction;
11254 if (this.multiSort) {
11255 var pn = this.paramNames;
11256 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11259 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11264 * Reloads the Record cache from the configured Proxy using the configured Reader and
11265 * the options from the last load operation performed.
11266 * @param {Object} options (optional) An object containing properties which may override the options
11267 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11268 * the most recently used options are reused).
11270 reload : function(options){
11271 this.load(Roo.applyIf(options||{}, this.lastOptions));
11275 // Called as a callback by the Reader during a load operation.
11276 loadRecords : function(o, options, success){
11277 if(!o || success === false){
11278 if(success !== false){
11279 this.fireEvent("load", this, [], options, o);
11281 if(options.callback){
11282 options.callback.call(options.scope || this, [], options, false);
11286 // if data returned failure - throw an exception.
11287 if (o.success === false) {
11288 // show a message if no listener is registered.
11289 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11290 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11292 // loadmask wil be hooked into this..
11293 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11296 var r = o.records, t = o.totalRecords || r.length;
11298 this.fireEvent("beforeloadadd", this, r, options, o);
11300 if(!options || options.add !== true){
11301 if(this.pruneModifiedRecords){
11302 this.modified = [];
11304 for(var i = 0, len = r.length; i < len; i++){
11308 this.data = this.snapshot;
11309 delete this.snapshot;
11312 this.data.addAll(r);
11313 this.totalLength = t;
11315 this.fireEvent("datachanged", this);
11317 this.totalLength = Math.max(t, this.data.length+r.length);
11321 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11323 var e = new Roo.data.Record({});
11325 e.set(this.parent.displayField, this.parent.emptyTitle);
11326 e.set(this.parent.valueField, '');
11331 this.fireEvent("load", this, r, options, o);
11332 if(options.callback){
11333 options.callback.call(options.scope || this, r, options, true);
11339 * Loads data from a passed data block. A Reader which understands the format of the data
11340 * must have been configured in the constructor.
11341 * @param {Object} data The data block from which to read the Records. The format of the data expected
11342 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11343 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11345 loadData : function(o, append){
11346 var r = this.reader.readRecords(o);
11347 this.loadRecords(r, {add: append}, true);
11351 * Gets the number of cached records.
11353 * <em>If using paging, this may not be the total size of the dataset. If the data object
11354 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11355 * the data set size</em>
11357 getCount : function(){
11358 return this.data.length || 0;
11362 * Gets the total number of records in the dataset as returned by the server.
11364 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11365 * the dataset size</em>
11367 getTotalCount : function(){
11368 return this.totalLength || 0;
11372 * Returns the sort state of the Store as an object with two properties:
11374 field {String} The name of the field by which the Records are sorted
11375 direction {String} The sort order, "ASC" or "DESC"
11378 getSortState : function(){
11379 return this.sortInfo;
11383 applySort : function(){
11384 if(this.sortInfo && !this.remoteSort){
11385 var s = this.sortInfo, f = s.field;
11386 var st = this.fields.get(f).sortType;
11387 var fn = function(r1, r2){
11388 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11389 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11391 this.data.sort(s.direction, fn);
11392 if(this.snapshot && this.snapshot != this.data){
11393 this.snapshot.sort(s.direction, fn);
11399 * Sets the default sort column and order to be used by the next load operation.
11400 * @param {String} fieldName The name of the field to sort by.
11401 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11403 setDefaultSort : function(field, dir){
11404 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11408 * Sort the Records.
11409 * If remote sorting is used, the sort is performed on the server, and the cache is
11410 * reloaded. If local sorting is used, the cache is sorted internally.
11411 * @param {String} fieldName The name of the field to sort by.
11412 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11414 sort : function(fieldName, dir){
11415 var f = this.fields.get(fieldName);
11417 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11419 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11420 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11425 this.sortToggle[f.name] = dir;
11426 this.sortInfo = {field: f.name, direction: dir};
11427 if(!this.remoteSort){
11429 this.fireEvent("datachanged", this);
11431 this.load(this.lastOptions);
11436 * Calls the specified function for each of the Records in the cache.
11437 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11438 * Returning <em>false</em> aborts and exits the iteration.
11439 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11441 each : function(fn, scope){
11442 this.data.each(fn, scope);
11446 * Gets all records modified since the last commit. Modified records are persisted across load operations
11447 * (e.g., during paging).
11448 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11450 getModifiedRecords : function(){
11451 return this.modified;
11455 createFilterFn : function(property, value, anyMatch){
11456 if(!value.exec){ // not a regex
11457 value = String(value);
11458 if(value.length == 0){
11461 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11463 return function(r){
11464 return value.test(r.data[property]);
11469 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11470 * @param {String} property A field on your records
11471 * @param {Number} start The record index to start at (defaults to 0)
11472 * @param {Number} end The last record index to include (defaults to length - 1)
11473 * @return {Number} The sum
11475 sum : function(property, start, end){
11476 var rs = this.data.items, v = 0;
11477 start = start || 0;
11478 end = (end || end === 0) ? end : rs.length-1;
11480 for(var i = start; i <= end; i++){
11481 v += (rs[i].data[property] || 0);
11487 * Filter the records by a specified property.
11488 * @param {String} field A field on your records
11489 * @param {String/RegExp} value Either a string that the field
11490 * should start with or a RegExp to test against the field
11491 * @param {Boolean} anyMatch True to match any part not just the beginning
11493 filter : function(property, value, anyMatch){
11494 var fn = this.createFilterFn(property, value, anyMatch);
11495 return fn ? this.filterBy(fn) : this.clearFilter();
11499 * Filter by a function. The specified function will be called with each
11500 * record in this data source. If the function returns true the record is included,
11501 * otherwise it is filtered.
11502 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11503 * @param {Object} scope (optional) The scope of the function (defaults to this)
11505 filterBy : function(fn, scope){
11506 this.snapshot = this.snapshot || this.data;
11507 this.data = this.queryBy(fn, scope||this);
11508 this.fireEvent("datachanged", this);
11512 * Query the records by a specified property.
11513 * @param {String} field A field on your records
11514 * @param {String/RegExp} value Either a string that the field
11515 * should start with or a RegExp to test against the field
11516 * @param {Boolean} anyMatch True to match any part not just the beginning
11517 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11519 query : function(property, value, anyMatch){
11520 var fn = this.createFilterFn(property, value, anyMatch);
11521 return fn ? this.queryBy(fn) : this.data.clone();
11525 * Query by a function. The specified function will be called with each
11526 * record in this data source. If the function returns true the record is included
11528 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11529 * @param {Object} scope (optional) The scope of the function (defaults to this)
11530 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11532 queryBy : function(fn, scope){
11533 var data = this.snapshot || this.data;
11534 return data.filterBy(fn, scope||this);
11538 * Collects unique values for a particular dataIndex from this store.
11539 * @param {String} dataIndex The property to collect
11540 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11541 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11542 * @return {Array} An array of the unique values
11544 collect : function(dataIndex, allowNull, bypassFilter){
11545 var d = (bypassFilter === true && this.snapshot) ?
11546 this.snapshot.items : this.data.items;
11547 var v, sv, r = [], l = {};
11548 for(var i = 0, len = d.length; i < len; i++){
11549 v = d[i].data[dataIndex];
11551 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11560 * Revert to a view of the Record cache with no filtering applied.
11561 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11563 clearFilter : function(suppressEvent){
11564 if(this.snapshot && this.snapshot != this.data){
11565 this.data = this.snapshot;
11566 delete this.snapshot;
11567 if(suppressEvent !== true){
11568 this.fireEvent("datachanged", this);
11574 afterEdit : function(record){
11575 if(this.modified.indexOf(record) == -1){
11576 this.modified.push(record);
11578 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11582 afterReject : function(record){
11583 this.modified.remove(record);
11584 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11588 afterCommit : function(record){
11589 this.modified.remove(record);
11590 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11594 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11595 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11597 commitChanges : function(){
11598 var m = this.modified.slice(0);
11599 this.modified = [];
11600 for(var i = 0, len = m.length; i < len; i++){
11606 * Cancel outstanding changes on all changed records.
11608 rejectChanges : function(){
11609 var m = this.modified.slice(0);
11610 this.modified = [];
11611 for(var i = 0, len = m.length; i < len; i++){
11616 onMetaChange : function(meta, rtype, o){
11617 this.recordType = rtype;
11618 this.fields = rtype.prototype.fields;
11619 delete this.snapshot;
11620 this.sortInfo = meta.sortInfo || this.sortInfo;
11621 this.modified = [];
11622 this.fireEvent('metachange', this, this.reader.meta);
11625 moveIndex : function(data, type)
11627 var index = this.indexOf(data);
11629 var newIndex = index + type;
11633 this.insert(newIndex, data);
11638 * Ext JS Library 1.1.1
11639 * Copyright(c) 2006-2007, Ext JS, LLC.
11641 * Originally Released Under LGPL - original licence link has changed is not relivant.
11644 * <script type="text/javascript">
11648 * @class Roo.data.SimpleStore
11649 * @extends Roo.data.Store
11650 * Small helper class to make creating Stores from Array data easier.
11651 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11652 * @cfg {Array} fields An array of field definition objects, or field name strings.
11653 * @cfg {Array} data The multi-dimensional array of data
11655 * @param {Object} config
11657 Roo.data.SimpleStore = function(config){
11658 Roo.data.SimpleStore.superclass.constructor.call(this, {
11660 reader: new Roo.data.ArrayReader({
11663 Roo.data.Record.create(config.fields)
11665 proxy : new Roo.data.MemoryProxy(config.data)
11669 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11671 * Ext JS Library 1.1.1
11672 * Copyright(c) 2006-2007, Ext JS, LLC.
11674 * Originally Released Under LGPL - original licence link has changed is not relivant.
11677 * <script type="text/javascript">
11682 * @extends Roo.data.Store
11683 * @class Roo.data.JsonStore
11684 * Small helper class to make creating Stores for JSON data easier. <br/>
11686 var store = new Roo.data.JsonStore({
11687 url: 'get-images.php',
11689 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11692 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11693 * JsonReader and HttpProxy (unless inline data is provided).</b>
11694 * @cfg {Array} fields An array of field definition objects, or field name strings.
11696 * @param {Object} config
11698 Roo.data.JsonStore = function(c){
11699 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11700 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11701 reader: new Roo.data.JsonReader(c, c.fields)
11704 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11706 * Ext JS Library 1.1.1
11707 * Copyright(c) 2006-2007, Ext JS, LLC.
11709 * Originally Released Under LGPL - original licence link has changed is not relivant.
11712 * <script type="text/javascript">
11716 Roo.data.Field = function(config){
11717 if(typeof config == "string"){
11718 config = {name: config};
11720 Roo.apply(this, config);
11723 this.type = "auto";
11726 var st = Roo.data.SortTypes;
11727 // named sortTypes are supported, here we look them up
11728 if(typeof this.sortType == "string"){
11729 this.sortType = st[this.sortType];
11732 // set default sortType for strings and dates
11733 if(!this.sortType){
11736 this.sortType = st.asUCString;
11739 this.sortType = st.asDate;
11742 this.sortType = st.none;
11747 var stripRe = /[\$,%]/g;
11749 // prebuilt conversion function for this field, instead of
11750 // switching every time we're reading a value
11752 var cv, dateFormat = this.dateFormat;
11757 cv = function(v){ return v; };
11760 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11764 return v !== undefined && v !== null && v !== '' ?
11765 parseInt(String(v).replace(stripRe, ""), 10) : '';
11770 return v !== undefined && v !== null && v !== '' ?
11771 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11776 cv = function(v){ return v === true || v === "true" || v == 1; };
11783 if(v instanceof Date){
11787 if(dateFormat == "timestamp"){
11788 return new Date(v*1000);
11790 return Date.parseDate(v, dateFormat);
11792 var parsed = Date.parse(v);
11793 return parsed ? new Date(parsed) : null;
11802 Roo.data.Field.prototype = {
11810 * Ext JS Library 1.1.1
11811 * Copyright(c) 2006-2007, Ext JS, LLC.
11813 * Originally Released Under LGPL - original licence link has changed is not relivant.
11816 * <script type="text/javascript">
11819 // Base class for reading structured data from a data source. This class is intended to be
11820 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11823 * @class Roo.data.DataReader
11824 * Base class for reading structured data from a data source. This class is intended to be
11825 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11828 Roo.data.DataReader = function(meta, recordType){
11832 this.recordType = recordType instanceof Array ?
11833 Roo.data.Record.create(recordType) : recordType;
11836 Roo.data.DataReader.prototype = {
11838 * Create an empty record
11839 * @param {Object} data (optional) - overlay some values
11840 * @return {Roo.data.Record} record created.
11842 newRow : function(d) {
11844 this.recordType.prototype.fields.each(function(c) {
11846 case 'int' : da[c.name] = 0; break;
11847 case 'date' : da[c.name] = new Date(); break;
11848 case 'float' : da[c.name] = 0.0; break;
11849 case 'boolean' : da[c.name] = false; break;
11850 default : da[c.name] = ""; break;
11854 return new this.recordType(Roo.apply(da, d));
11859 * Ext JS Library 1.1.1
11860 * Copyright(c) 2006-2007, Ext JS, LLC.
11862 * Originally Released Under LGPL - original licence link has changed is not relivant.
11865 * <script type="text/javascript">
11869 * @class Roo.data.DataProxy
11870 * @extends Roo.data.Observable
11871 * This class is an abstract base class for implementations which provide retrieval of
11872 * unformatted data objects.<br>
11874 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11875 * (of the appropriate type which knows how to parse the data object) to provide a block of
11876 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11878 * Custom implementations must implement the load method as described in
11879 * {@link Roo.data.HttpProxy#load}.
11881 Roo.data.DataProxy = function(){
11884 * @event beforeload
11885 * Fires before a network request is made to retrieve a data object.
11886 * @param {Object} This DataProxy object.
11887 * @param {Object} params The params parameter to the load function.
11892 * Fires before the load method's callback is called.
11893 * @param {Object} This DataProxy object.
11894 * @param {Object} o The data object.
11895 * @param {Object} arg The callback argument object passed to the load function.
11899 * @event loadexception
11900 * Fires if an Exception occurs during data retrieval.
11901 * @param {Object} This DataProxy object.
11902 * @param {Object} o The data object.
11903 * @param {Object} arg The callback argument object passed to the load function.
11904 * @param {Object} e The Exception.
11906 loadexception : true
11908 Roo.data.DataProxy.superclass.constructor.call(this);
11911 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11914 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11918 * Ext JS Library 1.1.1
11919 * Copyright(c) 2006-2007, Ext JS, LLC.
11921 * Originally Released Under LGPL - original licence link has changed is not relivant.
11924 * <script type="text/javascript">
11927 * @class Roo.data.MemoryProxy
11928 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11929 * to the Reader when its load method is called.
11931 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11933 Roo.data.MemoryProxy = function(data){
11937 Roo.data.MemoryProxy.superclass.constructor.call(this);
11941 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11944 * Load data from the requested source (in this case an in-memory
11945 * data object passed to the constructor), read the data object into
11946 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11947 * process that block using the passed callback.
11948 * @param {Object} params This parameter is not used by the MemoryProxy class.
11949 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11950 * object into a block of Roo.data.Records.
11951 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11952 * The function must be passed <ul>
11953 * <li>The Record block object</li>
11954 * <li>The "arg" argument from the load function</li>
11955 * <li>A boolean success indicator</li>
11957 * @param {Object} scope The scope in which to call the callback
11958 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11960 load : function(params, reader, callback, scope, arg){
11961 params = params || {};
11964 result = reader.readRecords(this.data);
11966 this.fireEvent("loadexception", this, arg, null, e);
11967 callback.call(scope, null, arg, false);
11970 callback.call(scope, result, arg, true);
11974 update : function(params, records){
11979 * Ext JS Library 1.1.1
11980 * Copyright(c) 2006-2007, Ext JS, LLC.
11982 * Originally Released Under LGPL - original licence link has changed is not relivant.
11985 * <script type="text/javascript">
11988 * @class Roo.data.HttpProxy
11989 * @extends Roo.data.DataProxy
11990 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11991 * configured to reference a certain URL.<br><br>
11993 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11994 * from which the running page was served.<br><br>
11996 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11998 * Be aware that to enable the browser to parse an XML document, the server must set
11999 * the Content-Type header in the HTTP response to "text/xml".
12001 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12002 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12003 * will be used to make the request.
12005 Roo.data.HttpProxy = function(conn){
12006 Roo.data.HttpProxy.superclass.constructor.call(this);
12007 // is conn a conn config or a real conn?
12009 this.useAjax = !conn || !conn.events;
12013 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12014 // thse are take from connection...
12017 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12020 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12021 * extra parameters to each request made by this object. (defaults to undefined)
12024 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12025 * to each request made by this object. (defaults to undefined)
12028 * @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)
12031 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12034 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12040 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12044 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12045 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12046 * a finer-grained basis than the DataProxy events.
12048 getConnection : function(){
12049 return this.useAjax ? Roo.Ajax : this.conn;
12053 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12054 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12055 * process that block using the passed callback.
12056 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12057 * for the request to the remote server.
12058 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12059 * object into a block of Roo.data.Records.
12060 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12061 * The function must be passed <ul>
12062 * <li>The Record block object</li>
12063 * <li>The "arg" argument from the load function</li>
12064 * <li>A boolean success indicator</li>
12066 * @param {Object} scope The scope in which to call the callback
12067 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12069 load : function(params, reader, callback, scope, arg){
12070 if(this.fireEvent("beforeload", this, params) !== false){
12072 params : params || {},
12074 callback : callback,
12079 callback : this.loadResponse,
12083 Roo.applyIf(o, this.conn);
12084 if(this.activeRequest){
12085 Roo.Ajax.abort(this.activeRequest);
12087 this.activeRequest = Roo.Ajax.request(o);
12089 this.conn.request(o);
12092 callback.call(scope||this, null, arg, false);
12097 loadResponse : function(o, success, response){
12098 delete this.activeRequest;
12100 this.fireEvent("loadexception", this, o, response);
12101 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12106 result = o.reader.read(response);
12108 this.fireEvent("loadexception", this, o, response, e);
12109 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12113 this.fireEvent("load", this, o, o.request.arg);
12114 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12118 update : function(dataSet){
12123 updateResponse : function(dataSet){
12128 * Ext JS Library 1.1.1
12129 * Copyright(c) 2006-2007, Ext JS, LLC.
12131 * Originally Released Under LGPL - original licence link has changed is not relivant.
12134 * <script type="text/javascript">
12138 * @class Roo.data.ScriptTagProxy
12139 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12140 * other than the originating domain of the running page.<br><br>
12142 * <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
12143 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12145 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12146 * source code that is used as the source inside a <script> tag.<br><br>
12148 * In order for the browser to process the returned data, the server must wrap the data object
12149 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12150 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12151 * depending on whether the callback name was passed:
12154 boolean scriptTag = false;
12155 String cb = request.getParameter("callback");
12158 response.setContentType("text/javascript");
12160 response.setContentType("application/x-json");
12162 Writer out = response.getWriter();
12164 out.write(cb + "(");
12166 out.print(dataBlock.toJsonString());
12173 * @param {Object} config A configuration object.
12175 Roo.data.ScriptTagProxy = function(config){
12176 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12177 Roo.apply(this, config);
12178 this.head = document.getElementsByTagName("head")[0];
12181 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12183 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12185 * @cfg {String} url The URL from which to request the data object.
12188 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12192 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12193 * the server the name of the callback function set up by the load call to process the returned data object.
12194 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12195 * javascript output which calls this named function passing the data object as its only parameter.
12197 callbackParam : "callback",
12199 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12200 * name to the request.
12205 * Load data from the configured URL, read the data object into
12206 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12207 * process that block using the passed callback.
12208 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12209 * for the request to the remote server.
12210 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12211 * object into a block of Roo.data.Records.
12212 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12213 * The function must be passed <ul>
12214 * <li>The Record block object</li>
12215 * <li>The "arg" argument from the load function</li>
12216 * <li>A boolean success indicator</li>
12218 * @param {Object} scope The scope in which to call the callback
12219 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12221 load : function(params, reader, callback, scope, arg){
12222 if(this.fireEvent("beforeload", this, params) !== false){
12224 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12226 var url = this.url;
12227 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12229 url += "&_dc=" + (new Date().getTime());
12231 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12234 cb : "stcCallback"+transId,
12235 scriptId : "stcScript"+transId,
12239 callback : callback,
12245 window[trans.cb] = function(o){
12246 conn.handleResponse(o, trans);
12249 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12251 if(this.autoAbort !== false){
12255 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12257 var script = document.createElement("script");
12258 script.setAttribute("src", url);
12259 script.setAttribute("type", "text/javascript");
12260 script.setAttribute("id", trans.scriptId);
12261 this.head.appendChild(script);
12263 this.trans = trans;
12265 callback.call(scope||this, null, arg, false);
12270 isLoading : function(){
12271 return this.trans ? true : false;
12275 * Abort the current server request.
12277 abort : function(){
12278 if(this.isLoading()){
12279 this.destroyTrans(this.trans);
12284 destroyTrans : function(trans, isLoaded){
12285 this.head.removeChild(document.getElementById(trans.scriptId));
12286 clearTimeout(trans.timeoutId);
12288 window[trans.cb] = undefined;
12290 delete window[trans.cb];
12293 // if hasn't been loaded, wait for load to remove it to prevent script error
12294 window[trans.cb] = function(){
12295 window[trans.cb] = undefined;
12297 delete window[trans.cb];
12304 handleResponse : function(o, trans){
12305 this.trans = false;
12306 this.destroyTrans(trans, true);
12309 result = trans.reader.readRecords(o);
12311 this.fireEvent("loadexception", this, o, trans.arg, e);
12312 trans.callback.call(trans.scope||window, null, trans.arg, false);
12315 this.fireEvent("load", this, o, trans.arg);
12316 trans.callback.call(trans.scope||window, result, trans.arg, true);
12320 handleFailure : function(trans){
12321 this.trans = false;
12322 this.destroyTrans(trans, false);
12323 this.fireEvent("loadexception", this, null, trans.arg);
12324 trans.callback.call(trans.scope||window, null, trans.arg, false);
12328 * Ext JS Library 1.1.1
12329 * Copyright(c) 2006-2007, Ext JS, LLC.
12331 * Originally Released Under LGPL - original licence link has changed is not relivant.
12334 * <script type="text/javascript">
12338 * @class Roo.data.JsonReader
12339 * @extends Roo.data.DataReader
12340 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12341 * based on mappings in a provided Roo.data.Record constructor.
12343 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12344 * in the reply previously.
12349 var RecordDef = Roo.data.Record.create([
12350 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12351 {name: 'occupation'} // This field will use "occupation" as the mapping.
12353 var myReader = new Roo.data.JsonReader({
12354 totalProperty: "results", // The property which contains the total dataset size (optional)
12355 root: "rows", // The property which contains an Array of row objects
12356 id: "id" // The property within each row object that provides an ID for the record (optional)
12360 * This would consume a JSON file like this:
12362 { 'results': 2, 'rows': [
12363 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12364 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12367 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12368 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12369 * paged from the remote server.
12370 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12371 * @cfg {String} root name of the property which contains the Array of row objects.
12372 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12373 * @cfg {Array} fields Array of field definition objects
12375 * Create a new JsonReader
12376 * @param {Object} meta Metadata configuration options
12377 * @param {Object} recordType Either an Array of field definition objects,
12378 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12380 Roo.data.JsonReader = function(meta, recordType){
12383 // set some defaults:
12384 Roo.applyIf(meta, {
12385 totalProperty: 'total',
12386 successProperty : 'success',
12391 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12393 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12396 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12397 * Used by Store query builder to append _requestMeta to params.
12400 metaFromRemote : false,
12402 * This method is only used by a DataProxy which has retrieved data from a remote server.
12403 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12404 * @return {Object} data A data block which is used by an Roo.data.Store object as
12405 * a cache of Roo.data.Records.
12407 read : function(response){
12408 var json = response.responseText;
12410 var o = /* eval:var:o */ eval("("+json+")");
12412 throw {message: "JsonReader.read: Json object not found"};
12418 this.metaFromRemote = true;
12419 this.meta = o.metaData;
12420 this.recordType = Roo.data.Record.create(o.metaData.fields);
12421 this.onMetaChange(this.meta, this.recordType, o);
12423 return this.readRecords(o);
12426 // private function a store will implement
12427 onMetaChange : function(meta, recordType, o){
12434 simpleAccess: function(obj, subsc) {
12441 getJsonAccessor: function(){
12443 return function(expr) {
12445 return(re.test(expr))
12446 ? new Function("obj", "return obj." + expr)
12451 return Roo.emptyFn;
12456 * Create a data block containing Roo.data.Records from an XML document.
12457 * @param {Object} o An object which contains an Array of row objects in the property specified
12458 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12459 * which contains the total size of the dataset.
12460 * @return {Object} data A data block which is used by an Roo.data.Store object as
12461 * a cache of Roo.data.Records.
12463 readRecords : function(o){
12465 * After any data loads, the raw JSON data is available for further custom processing.
12469 var s = this.meta, Record = this.recordType,
12470 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12472 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12474 if(s.totalProperty) {
12475 this.getTotal = this.getJsonAccessor(s.totalProperty);
12477 if(s.successProperty) {
12478 this.getSuccess = this.getJsonAccessor(s.successProperty);
12480 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12482 var g = this.getJsonAccessor(s.id);
12483 this.getId = function(rec) {
12485 return (r === undefined || r === "") ? null : r;
12488 this.getId = function(){return null;};
12491 for(var jj = 0; jj < fl; jj++){
12493 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12494 this.ef[jj] = this.getJsonAccessor(map);
12498 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12499 if(s.totalProperty){
12500 var vt = parseInt(this.getTotal(o), 10);
12505 if(s.successProperty){
12506 var vs = this.getSuccess(o);
12507 if(vs === false || vs === 'false'){
12512 for(var i = 0; i < c; i++){
12515 var id = this.getId(n);
12516 for(var j = 0; j < fl; j++){
12518 var v = this.ef[j](n);
12520 Roo.log('missing convert for ' + f.name);
12524 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12526 var record = new Record(values, id);
12528 records[i] = record;
12534 totalRecords : totalRecords
12539 * Ext JS Library 1.1.1
12540 * Copyright(c) 2006-2007, Ext JS, LLC.
12542 * Originally Released Under LGPL - original licence link has changed is not relivant.
12545 * <script type="text/javascript">
12549 * @class Roo.data.ArrayReader
12550 * @extends Roo.data.DataReader
12551 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12552 * Each element of that Array represents a row of data fields. The
12553 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12554 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12558 var RecordDef = Roo.data.Record.create([
12559 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12560 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12562 var myReader = new Roo.data.ArrayReader({
12563 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12567 * This would consume an Array like this:
12569 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12571 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12573 * Create a new JsonReader
12574 * @param {Object} meta Metadata configuration options.
12575 * @param {Object} recordType Either an Array of field definition objects
12576 * as specified to {@link Roo.data.Record#create},
12577 * or an {@link Roo.data.Record} object
12578 * created using {@link Roo.data.Record#create}.
12580 Roo.data.ArrayReader = function(meta, recordType){
12581 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12584 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12586 * Create a data block containing Roo.data.Records from an XML document.
12587 * @param {Object} o An Array of row objects which represents the dataset.
12588 * @return {Object} data A data block which is used by an Roo.data.Store object as
12589 * a cache of Roo.data.Records.
12591 readRecords : function(o){
12592 var sid = this.meta ? this.meta.id : null;
12593 var recordType = this.recordType, fields = recordType.prototype.fields;
12596 for(var i = 0; i < root.length; i++){
12599 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12600 for(var j = 0, jlen = fields.length; j < jlen; j++){
12601 var f = fields.items[j];
12602 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12603 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12605 values[f.name] = v;
12607 var record = new recordType(values, id);
12609 records[records.length] = record;
12613 totalRecords : records.length
12622 * @class Roo.bootstrap.ComboBox
12623 * @extends Roo.bootstrap.TriggerField
12624 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12625 * @cfg {Boolean} append (true|false) default false
12626 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12627 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12628 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12629 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12630 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12631 * @cfg {Boolean} animate default true
12632 * @cfg {Boolean} emptyResultText only for touch device
12633 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12634 * @cfg {String} emptyTitle default ''
12636 * Create a new ComboBox.
12637 * @param {Object} config Configuration options
12639 Roo.bootstrap.ComboBox = function(config){
12640 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12644 * Fires when the dropdown list is expanded
12645 * @param {Roo.bootstrap.ComboBox} combo This combo box
12650 * Fires when the dropdown list is collapsed
12651 * @param {Roo.bootstrap.ComboBox} combo This combo box
12655 * @event beforeselect
12656 * Fires before a list item is selected. Return false to cancel the selection.
12657 * @param {Roo.bootstrap.ComboBox} combo This combo box
12658 * @param {Roo.data.Record} record The data record returned from the underlying store
12659 * @param {Number} index The index of the selected item in the dropdown list
12661 'beforeselect' : true,
12664 * Fires when a list item is selected
12665 * @param {Roo.bootstrap.ComboBox} combo This combo box
12666 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12667 * @param {Number} index The index of the selected item in the dropdown list
12671 * @event beforequery
12672 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12673 * The event object passed has these properties:
12674 * @param {Roo.bootstrap.ComboBox} combo This combo box
12675 * @param {String} query The query
12676 * @param {Boolean} forceAll true to force "all" query
12677 * @param {Boolean} cancel true to cancel the query
12678 * @param {Object} e The query event object
12680 'beforequery': true,
12683 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12684 * @param {Roo.bootstrap.ComboBox} combo This combo box
12689 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12690 * @param {Roo.bootstrap.ComboBox} combo This combo box
12691 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12696 * Fires when the remove value from the combobox array
12697 * @param {Roo.bootstrap.ComboBox} combo This combo box
12701 * @event afterremove
12702 * Fires when the remove value from the combobox array
12703 * @param {Roo.bootstrap.ComboBox} combo This combo box
12705 'afterremove' : true,
12707 * @event specialfilter
12708 * Fires when specialfilter
12709 * @param {Roo.bootstrap.ComboBox} combo This combo box
12711 'specialfilter' : true,
12714 * Fires when tick the element
12715 * @param {Roo.bootstrap.ComboBox} combo This combo box
12719 * @event touchviewdisplay
12720 * Fires when touch view require special display (default is using displayField)
12721 * @param {Roo.bootstrap.ComboBox} combo This combo box
12722 * @param {Object} cfg set html .
12724 'touchviewdisplay' : true
12729 this.tickItems = [];
12731 this.selectedIndex = -1;
12732 if(this.mode == 'local'){
12733 if(config.queryDelay === undefined){
12734 this.queryDelay = 10;
12736 if(config.minChars === undefined){
12742 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12745 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12746 * rendering into an Roo.Editor, defaults to false)
12749 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12750 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12753 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12756 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12757 * the dropdown list (defaults to undefined, with no header element)
12761 * @cfg {String/Roo.Template} tpl The template to use to render the output
12765 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12767 listWidth: undefined,
12769 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12770 * mode = 'remote' or 'text' if mode = 'local')
12772 displayField: undefined,
12775 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12776 * mode = 'remote' or 'value' if mode = 'local').
12777 * Note: use of a valueField requires the user make a selection
12778 * in order for a value to be mapped.
12780 valueField: undefined,
12782 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12787 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12788 * field's data value (defaults to the underlying DOM element's name)
12790 hiddenName: undefined,
12792 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12796 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12798 selectedClass: 'active',
12801 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12805 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12806 * anchor positions (defaults to 'tl-bl')
12808 listAlign: 'tl-bl?',
12810 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12814 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12815 * query specified by the allQuery config option (defaults to 'query')
12817 triggerAction: 'query',
12819 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12820 * (defaults to 4, does not apply if editable = false)
12824 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12825 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12829 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12830 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12834 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12835 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12839 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12840 * when editable = true (defaults to false)
12842 selectOnFocus:false,
12844 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12846 queryParam: 'query',
12848 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12849 * when mode = 'remote' (defaults to 'Loading...')
12851 loadingText: 'Loading...',
12853 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12857 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12861 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12862 * traditional select (defaults to true)
12866 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12870 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12874 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12875 * listWidth has a higher value)
12879 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12880 * allow the user to set arbitrary text into the field (defaults to false)
12882 forceSelection:false,
12884 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12885 * if typeAhead = true (defaults to 250)
12887 typeAheadDelay : 250,
12889 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12890 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12892 valueNotFoundText : undefined,
12894 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12896 blockFocus : false,
12899 * @cfg {Boolean} disableClear Disable showing of clear button.
12901 disableClear : false,
12903 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12905 alwaysQuery : false,
12908 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12913 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12915 invalidClass : "has-warning",
12918 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12920 validClass : "has-success",
12923 * @cfg {Boolean} specialFilter (true|false) special filter default false
12925 specialFilter : false,
12928 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12930 mobileTouchView : true,
12933 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12935 useNativeIOS : false,
12937 ios_options : false,
12949 btnPosition : 'right',
12950 triggerList : true,
12951 showToggleBtn : true,
12953 emptyResultText: 'Empty',
12954 triggerText : 'Select',
12957 // element that contains real text value.. (when hidden is used..)
12959 getAutoCreate : function()
12964 * Render classic select for iso
12967 if(Roo.isIOS && this.useNativeIOS){
12968 cfg = this.getAutoCreateNativeIOS();
12976 if(Roo.isTouch && this.mobileTouchView){
12977 cfg = this.getAutoCreateTouchView();
12984 if(!this.tickable){
12985 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12990 * ComboBox with tickable selections
12993 var align = this.labelAlign || this.parentLabelAlign();
12996 cls : 'form-group roo-combobox-tickable' //input-group
12999 var btn_text_select = '';
13000 var btn_text_done = '';
13001 var btn_text_cancel = '';
13003 if (this.btn_text_show) {
13004 btn_text_select = 'Select';
13005 btn_text_done = 'Done';
13006 btn_text_cancel = 'Cancel';
13011 cls : 'tickable-buttons',
13016 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13017 //html : this.triggerText
13018 html: btn_text_select
13024 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13026 html: btn_text_done
13032 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13034 html: btn_text_cancel
13040 buttons.cn.unshift({
13042 cls: 'roo-select2-search-field-input'
13048 Roo.each(buttons.cn, function(c){
13050 c.cls += ' btn-' + _this.size;
13053 if (_this.disabled) {
13064 cls: 'form-hidden-field'
13068 cls: 'roo-select2-choices',
13072 cls: 'roo-select2-search-field',
13083 cls: 'roo-select2-container input-group roo-select2-container-multi',
13088 // cls: 'typeahead typeahead-long dropdown-menu',
13089 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13094 if(this.hasFeedback && !this.allowBlank){
13098 cls: 'glyphicon form-control-feedback'
13101 combobox.cn.push(feedback);
13105 if (align ==='left' && this.fieldLabel.length) {
13107 cfg.cls += ' roo-form-group-label-left';
13112 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13113 tooltip : 'This field is required'
13118 cls : 'control-label',
13119 html : this.fieldLabel
13131 var labelCfg = cfg.cn[1];
13132 var contentCfg = cfg.cn[2];
13135 if(this.indicatorpos == 'right'){
13141 cls : 'control-label',
13145 html : this.fieldLabel
13149 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13150 tooltip : 'This field is required'
13165 labelCfg = cfg.cn[0];
13166 contentCfg = cfg.cn[1];
13170 if(this.labelWidth > 12){
13171 labelCfg.style = "width: " + this.labelWidth + 'px';
13174 if(this.labelWidth < 13 && this.labelmd == 0){
13175 this.labelmd = this.labelWidth;
13178 if(this.labellg > 0){
13179 labelCfg.cls += ' col-lg-' + this.labellg;
13180 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13183 if(this.labelmd > 0){
13184 labelCfg.cls += ' col-md-' + this.labelmd;
13185 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13188 if(this.labelsm > 0){
13189 labelCfg.cls += ' col-sm-' + this.labelsm;
13190 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13193 if(this.labelxs > 0){
13194 labelCfg.cls += ' col-xs-' + this.labelxs;
13195 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13199 } else if ( this.fieldLabel.length) {
13200 // Roo.log(" label");
13204 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13205 tooltip : 'This field is required'
13209 //cls : 'input-group-addon',
13210 html : this.fieldLabel
13215 if(this.indicatorpos == 'right'){
13219 //cls : 'input-group-addon',
13220 html : this.fieldLabel
13224 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13225 tooltip : 'This field is required'
13234 // Roo.log(" no label && no align");
13241 ['xs','sm','md','lg'].map(function(size){
13242 if (settings[size]) {
13243 cfg.cls += ' col-' + size + '-' + settings[size];
13251 _initEventsCalled : false,
13254 initEvents: function()
13256 if (this._initEventsCalled) { // as we call render... prevent looping...
13259 this._initEventsCalled = true;
13262 throw "can not find store for combo";
13265 this.indicator = this.indicatorEl();
13267 this.store = Roo.factory(this.store, Roo.data);
13268 this.store.parent = this;
13270 // if we are building from html. then this element is so complex, that we can not really
13271 // use the rendered HTML.
13272 // so we have to trash and replace the previous code.
13273 if (Roo.XComponent.build_from_html) {
13274 // remove this element....
13275 var e = this.el.dom, k=0;
13276 while (e ) { e = e.previousSibling; ++k;}
13281 this.rendered = false;
13283 this.render(this.parent().getChildContainer(true), k);
13286 if(Roo.isIOS && this.useNativeIOS){
13287 this.initIOSView();
13295 if(Roo.isTouch && this.mobileTouchView){
13296 this.initTouchView();
13301 this.initTickableEvents();
13305 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13307 if(this.hiddenName){
13309 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13311 this.hiddenField.dom.value =
13312 this.hiddenValue !== undefined ? this.hiddenValue :
13313 this.value !== undefined ? this.value : '';
13315 // prevent input submission
13316 this.el.dom.removeAttribute('name');
13317 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13322 // this.el.dom.setAttribute('autocomplete', 'off');
13325 var cls = 'x-combo-list';
13327 //this.list = new Roo.Layer({
13328 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13334 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13335 _this.list.setWidth(lw);
13338 this.list.on('mouseover', this.onViewOver, this);
13339 this.list.on('mousemove', this.onViewMove, this);
13340 this.list.on('scroll', this.onViewScroll, this);
13343 this.list.swallowEvent('mousewheel');
13344 this.assetHeight = 0;
13347 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13348 this.assetHeight += this.header.getHeight();
13351 this.innerList = this.list.createChild({cls:cls+'-inner'});
13352 this.innerList.on('mouseover', this.onViewOver, this);
13353 this.innerList.on('mousemove', this.onViewMove, this);
13354 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13356 if(this.allowBlank && !this.pageSize && !this.disableClear){
13357 this.footer = this.list.createChild({cls:cls+'-ft'});
13358 this.pageTb = new Roo.Toolbar(this.footer);
13362 this.footer = this.list.createChild({cls:cls+'-ft'});
13363 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13364 {pageSize: this.pageSize});
13368 if (this.pageTb && this.allowBlank && !this.disableClear) {
13370 this.pageTb.add(new Roo.Toolbar.Fill(), {
13371 cls: 'x-btn-icon x-btn-clear',
13373 handler: function()
13376 _this.clearValue();
13377 _this.onSelect(false, -1);
13382 this.assetHeight += this.footer.getHeight();
13387 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13390 this.view = new Roo.View(this.list, this.tpl, {
13391 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13393 //this.view.wrapEl.setDisplayed(false);
13394 this.view.on('click', this.onViewClick, this);
13397 this.store.on('beforeload', this.onBeforeLoad, this);
13398 this.store.on('load', this.onLoad, this);
13399 this.store.on('loadexception', this.onLoadException, this);
13401 if(this.resizable){
13402 this.resizer = new Roo.Resizable(this.list, {
13403 pinned:true, handles:'se'
13405 this.resizer.on('resize', function(r, w, h){
13406 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13407 this.listWidth = w;
13408 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13409 this.restrictHeight();
13411 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13414 if(!this.editable){
13415 this.editable = true;
13416 this.setEditable(false);
13421 if (typeof(this.events.add.listeners) != 'undefined') {
13423 this.addicon = this.wrap.createChild(
13424 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13426 this.addicon.on('click', function(e) {
13427 this.fireEvent('add', this);
13430 if (typeof(this.events.edit.listeners) != 'undefined') {
13432 this.editicon = this.wrap.createChild(
13433 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13434 if (this.addicon) {
13435 this.editicon.setStyle('margin-left', '40px');
13437 this.editicon.on('click', function(e) {
13439 // we fire even if inothing is selected..
13440 this.fireEvent('edit', this, this.lastData );
13446 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13447 "up" : function(e){
13448 this.inKeyMode = true;
13452 "down" : function(e){
13453 if(!this.isExpanded()){
13454 this.onTriggerClick();
13456 this.inKeyMode = true;
13461 "enter" : function(e){
13462 // this.onViewClick();
13466 if(this.fireEvent("specialkey", this, e)){
13467 this.onViewClick(false);
13473 "esc" : function(e){
13477 "tab" : function(e){
13480 if(this.fireEvent("specialkey", this, e)){
13481 this.onViewClick(false);
13489 doRelay : function(foo, bar, hname){
13490 if(hname == 'down' || this.scope.isExpanded()){
13491 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13500 this.queryDelay = Math.max(this.queryDelay || 10,
13501 this.mode == 'local' ? 10 : 250);
13504 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13506 if(this.typeAhead){
13507 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13509 if(this.editable !== false){
13510 this.inputEl().on("keyup", this.onKeyUp, this);
13512 if(this.forceSelection){
13513 this.inputEl().on('blur', this.doForce, this);
13517 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13518 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13522 initTickableEvents: function()
13526 if(this.hiddenName){
13528 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13530 this.hiddenField.dom.value =
13531 this.hiddenValue !== undefined ? this.hiddenValue :
13532 this.value !== undefined ? this.value : '';
13534 // prevent input submission
13535 this.el.dom.removeAttribute('name');
13536 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13541 // this.list = this.el.select('ul.dropdown-menu',true).first();
13543 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13544 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13545 if(this.triggerList){
13546 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13549 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13550 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13552 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13553 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13555 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13556 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13558 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13559 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13560 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13563 this.cancelBtn.hide();
13568 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13569 _this.list.setWidth(lw);
13572 this.list.on('mouseover', this.onViewOver, this);
13573 this.list.on('mousemove', this.onViewMove, this);
13575 this.list.on('scroll', this.onViewScroll, this);
13578 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>';
13581 this.view = new Roo.View(this.list, this.tpl, {
13582 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13585 //this.view.wrapEl.setDisplayed(false);
13586 this.view.on('click', this.onViewClick, this);
13590 this.store.on('beforeload', this.onBeforeLoad, this);
13591 this.store.on('load', this.onLoad, this);
13592 this.store.on('loadexception', this.onLoadException, this);
13595 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13596 "up" : function(e){
13597 this.inKeyMode = true;
13601 "down" : function(e){
13602 this.inKeyMode = true;
13606 "enter" : function(e){
13607 if(this.fireEvent("specialkey", this, e)){
13608 this.onViewClick(false);
13614 "esc" : function(e){
13615 this.onTickableFooterButtonClick(e, false, false);
13618 "tab" : function(e){
13619 this.fireEvent("specialkey", this, e);
13621 this.onTickableFooterButtonClick(e, false, false);
13628 doRelay : function(e, fn, key){
13629 if(this.scope.isExpanded()){
13630 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13639 this.queryDelay = Math.max(this.queryDelay || 10,
13640 this.mode == 'local' ? 10 : 250);
13643 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13645 if(this.typeAhead){
13646 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13649 if(this.editable !== false){
13650 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13653 this.indicator = this.indicatorEl();
13655 if(this.indicator){
13656 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13657 this.indicator.hide();
13662 onDestroy : function(){
13664 this.view.setStore(null);
13665 this.view.el.removeAllListeners();
13666 this.view.el.remove();
13667 this.view.purgeListeners();
13670 this.list.dom.innerHTML = '';
13674 this.store.un('beforeload', this.onBeforeLoad, this);
13675 this.store.un('load', this.onLoad, this);
13676 this.store.un('loadexception', this.onLoadException, this);
13678 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13682 fireKey : function(e){
13683 if(e.isNavKeyPress() && !this.list.isVisible()){
13684 this.fireEvent("specialkey", this, e);
13689 onResize: function(w, h){
13690 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13692 // if(typeof w != 'number'){
13693 // // we do not handle it!?!?
13696 // var tw = this.trigger.getWidth();
13697 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13698 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13700 // this.inputEl().setWidth( this.adjustWidth('input', x));
13702 // //this.trigger.setStyle('left', x+'px');
13704 // if(this.list && this.listWidth === undefined){
13705 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13706 // this.list.setWidth(lw);
13707 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13715 * Allow or prevent the user from directly editing the field text. If false is passed,
13716 * the user will only be able to select from the items defined in the dropdown list. This method
13717 * is the runtime equivalent of setting the 'editable' config option at config time.
13718 * @param {Boolean} value True to allow the user to directly edit the field text
13720 setEditable : function(value){
13721 if(value == this.editable){
13724 this.editable = value;
13726 this.inputEl().dom.setAttribute('readOnly', true);
13727 this.inputEl().on('mousedown', this.onTriggerClick, this);
13728 this.inputEl().addClass('x-combo-noedit');
13730 this.inputEl().dom.setAttribute('readOnly', false);
13731 this.inputEl().un('mousedown', this.onTriggerClick, this);
13732 this.inputEl().removeClass('x-combo-noedit');
13738 onBeforeLoad : function(combo,opts){
13739 if(!this.hasFocus){
13743 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13745 this.restrictHeight();
13746 this.selectedIndex = -1;
13750 onLoad : function(){
13752 this.hasQuery = false;
13754 if(!this.hasFocus){
13758 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13759 this.loading.hide();
13762 if(this.store.getCount() > 0){
13765 this.restrictHeight();
13766 if(this.lastQuery == this.allQuery){
13767 if(this.editable && !this.tickable){
13768 this.inputEl().dom.select();
13772 !this.selectByValue(this.value, true) &&
13775 !this.store.lastOptions ||
13776 typeof(this.store.lastOptions.add) == 'undefined' ||
13777 this.store.lastOptions.add != true
13780 this.select(0, true);
13783 if(this.autoFocus){
13786 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13787 this.taTask.delay(this.typeAheadDelay);
13791 this.onEmptyResults();
13797 onLoadException : function()
13799 this.hasQuery = false;
13801 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13802 this.loading.hide();
13805 if(this.tickable && this.editable){
13810 // only causes errors at present
13811 //Roo.log(this.store.reader.jsonData);
13812 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13814 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13820 onTypeAhead : function(){
13821 if(this.store.getCount() > 0){
13822 var r = this.store.getAt(0);
13823 var newValue = r.data[this.displayField];
13824 var len = newValue.length;
13825 var selStart = this.getRawValue().length;
13827 if(selStart != len){
13828 this.setRawValue(newValue);
13829 this.selectText(selStart, newValue.length);
13835 onSelect : function(record, index){
13837 if(this.fireEvent('beforeselect', this, record, index) !== false){
13839 this.setFromData(index > -1 ? record.data : false);
13842 this.fireEvent('select', this, record, index);
13847 * Returns the currently selected field value or empty string if no value is set.
13848 * @return {String} value The selected value
13850 getValue : function()
13852 if(Roo.isIOS && this.useNativeIOS){
13853 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13857 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13860 if(this.valueField){
13861 return typeof this.value != 'undefined' ? this.value : '';
13863 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13867 getRawValue : function()
13869 if(Roo.isIOS && this.useNativeIOS){
13870 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13873 var v = this.inputEl().getValue();
13879 * Clears any text/value currently set in the field
13881 clearValue : function(){
13883 if(this.hiddenField){
13884 this.hiddenField.dom.value = '';
13887 this.setRawValue('');
13888 this.lastSelectionText = '';
13889 this.lastData = false;
13891 var close = this.closeTriggerEl();
13902 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13903 * will be displayed in the field. If the value does not match the data value of an existing item,
13904 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13905 * Otherwise the field will be blank (although the value will still be set).
13906 * @param {String} value The value to match
13908 setValue : function(v)
13910 if(Roo.isIOS && this.useNativeIOS){
13911 this.setIOSValue(v);
13921 if(this.valueField){
13922 var r = this.findRecord(this.valueField, v);
13924 text = r.data[this.displayField];
13925 }else if(this.valueNotFoundText !== undefined){
13926 text = this.valueNotFoundText;
13929 this.lastSelectionText = text;
13930 if(this.hiddenField){
13931 this.hiddenField.dom.value = v;
13933 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13936 var close = this.closeTriggerEl();
13939 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13945 * @property {Object} the last set data for the element
13950 * Sets the value of the field based on a object which is related to the record format for the store.
13951 * @param {Object} value the value to set as. or false on reset?
13953 setFromData : function(o){
13960 var dv = ''; // display value
13961 var vv = ''; // value value..
13963 if (this.displayField) {
13964 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13966 // this is an error condition!!!
13967 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13970 if(this.valueField){
13971 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13974 var close = this.closeTriggerEl();
13977 if(dv.length || vv * 1 > 0){
13979 this.blockFocus=true;
13985 if(this.hiddenField){
13986 this.hiddenField.dom.value = vv;
13988 this.lastSelectionText = dv;
13989 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13993 // no hidden field.. - we store the value in 'value', but still display
13994 // display field!!!!
13995 this.lastSelectionText = dv;
13996 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14003 reset : function(){
14004 // overridden so that last data is reset..
14011 this.setValue(this.originalValue);
14012 //this.clearInvalid();
14013 this.lastData = false;
14015 this.view.clearSelections();
14021 findRecord : function(prop, value){
14023 if(this.store.getCount() > 0){
14024 this.store.each(function(r){
14025 if(r.data[prop] == value){
14035 getName: function()
14037 // returns hidden if it's set..
14038 if (!this.rendered) {return ''};
14039 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14043 onViewMove : function(e, t){
14044 this.inKeyMode = false;
14048 onViewOver : function(e, t){
14049 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14052 var item = this.view.findItemFromChild(t);
14055 var index = this.view.indexOf(item);
14056 this.select(index, false);
14061 onViewClick : function(view, doFocus, el, e)
14063 var index = this.view.getSelectedIndexes()[0];
14065 var r = this.store.getAt(index);
14069 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14076 Roo.each(this.tickItems, function(v,k){
14078 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14080 _this.tickItems.splice(k, 1);
14082 if(typeof(e) == 'undefined' && view == false){
14083 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14095 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14096 this.tickItems.push(r.data);
14099 if(typeof(e) == 'undefined' && view == false){
14100 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14107 this.onSelect(r, index);
14109 if(doFocus !== false && !this.blockFocus){
14110 this.inputEl().focus();
14115 restrictHeight : function(){
14116 //this.innerList.dom.style.height = '';
14117 //var inner = this.innerList.dom;
14118 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14119 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14120 //this.list.beginUpdate();
14121 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14122 this.list.alignTo(this.inputEl(), this.listAlign);
14123 this.list.alignTo(this.inputEl(), this.listAlign);
14124 //this.list.endUpdate();
14128 onEmptyResults : function(){
14130 if(this.tickable && this.editable){
14131 this.hasFocus = false;
14132 this.restrictHeight();
14140 * Returns true if the dropdown list is expanded, else false.
14142 isExpanded : function(){
14143 return this.list.isVisible();
14147 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14148 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14149 * @param {String} value The data value of the item to select
14150 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14151 * selected item if it is not currently in view (defaults to true)
14152 * @return {Boolean} True if the value matched an item in the list, else false
14154 selectByValue : function(v, scrollIntoView){
14155 if(v !== undefined && v !== null){
14156 var r = this.findRecord(this.valueField || this.displayField, v);
14158 this.select(this.store.indexOf(r), scrollIntoView);
14166 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14167 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14168 * @param {Number} index The zero-based index of the list item to select
14169 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14170 * selected item if it is not currently in view (defaults to true)
14172 select : function(index, scrollIntoView){
14173 this.selectedIndex = index;
14174 this.view.select(index);
14175 if(scrollIntoView !== false){
14176 var el = this.view.getNode(index);
14178 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14181 this.list.scrollChildIntoView(el, false);
14187 selectNext : function(){
14188 var ct = this.store.getCount();
14190 if(this.selectedIndex == -1){
14192 }else if(this.selectedIndex < ct-1){
14193 this.select(this.selectedIndex+1);
14199 selectPrev : function(){
14200 var ct = this.store.getCount();
14202 if(this.selectedIndex == -1){
14204 }else if(this.selectedIndex != 0){
14205 this.select(this.selectedIndex-1);
14211 onKeyUp : function(e){
14212 if(this.editable !== false && !e.isSpecialKey()){
14213 this.lastKey = e.getKey();
14214 this.dqTask.delay(this.queryDelay);
14219 validateBlur : function(){
14220 return !this.list || !this.list.isVisible();
14224 initQuery : function(){
14226 var v = this.getRawValue();
14228 if(this.tickable && this.editable){
14229 v = this.tickableInputEl().getValue();
14236 doForce : function(){
14237 if(this.inputEl().dom.value.length > 0){
14238 this.inputEl().dom.value =
14239 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14245 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14246 * query allowing the query action to be canceled if needed.
14247 * @param {String} query The SQL query to execute
14248 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14249 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14250 * saved in the current store (defaults to false)
14252 doQuery : function(q, forceAll){
14254 if(q === undefined || q === null){
14259 forceAll: forceAll,
14263 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14268 forceAll = qe.forceAll;
14269 if(forceAll === true || (q.length >= this.minChars)){
14271 this.hasQuery = true;
14273 if(this.lastQuery != q || this.alwaysQuery){
14274 this.lastQuery = q;
14275 if(this.mode == 'local'){
14276 this.selectedIndex = -1;
14278 this.store.clearFilter();
14281 if(this.specialFilter){
14282 this.fireEvent('specialfilter', this);
14287 this.store.filter(this.displayField, q);
14290 this.store.fireEvent("datachanged", this.store);
14297 this.store.baseParams[this.queryParam] = q;
14299 var options = {params : this.getParams(q)};
14302 options.add = true;
14303 options.params.start = this.page * this.pageSize;
14306 this.store.load(options);
14309 * this code will make the page width larger, at the beginning, the list not align correctly,
14310 * we should expand the list on onLoad
14311 * so command out it
14316 this.selectedIndex = -1;
14321 this.loadNext = false;
14325 getParams : function(q){
14327 //p[this.queryParam] = q;
14331 p.limit = this.pageSize;
14337 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14339 collapse : function(){
14340 if(!this.isExpanded()){
14346 this.hasFocus = false;
14350 this.cancelBtn.hide();
14351 this.trigger.show();
14354 this.tickableInputEl().dom.value = '';
14355 this.tickableInputEl().blur();
14360 Roo.get(document).un('mousedown', this.collapseIf, this);
14361 Roo.get(document).un('mousewheel', this.collapseIf, this);
14362 if (!this.editable) {
14363 Roo.get(document).un('keydown', this.listKeyPress, this);
14365 this.fireEvent('collapse', this);
14371 collapseIf : function(e){
14372 var in_combo = e.within(this.el);
14373 var in_list = e.within(this.list);
14374 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14376 if (in_combo || in_list || is_list) {
14377 //e.stopPropagation();
14382 this.onTickableFooterButtonClick(e, false, false);
14390 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14392 expand : function(){
14394 if(this.isExpanded() || !this.hasFocus){
14398 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14399 this.list.setWidth(lw);
14405 this.restrictHeight();
14409 this.tickItems = Roo.apply([], this.item);
14412 this.cancelBtn.show();
14413 this.trigger.hide();
14416 this.tickableInputEl().focus();
14421 Roo.get(document).on('mousedown', this.collapseIf, this);
14422 Roo.get(document).on('mousewheel', this.collapseIf, this);
14423 if (!this.editable) {
14424 Roo.get(document).on('keydown', this.listKeyPress, this);
14427 this.fireEvent('expand', this);
14431 // Implements the default empty TriggerField.onTriggerClick function
14432 onTriggerClick : function(e)
14434 Roo.log('trigger click');
14436 if(this.disabled || !this.triggerList){
14441 this.loadNext = false;
14443 if(this.isExpanded()){
14445 if (!this.blockFocus) {
14446 this.inputEl().focus();
14450 this.hasFocus = true;
14451 if(this.triggerAction == 'all') {
14452 this.doQuery(this.allQuery, true);
14454 this.doQuery(this.getRawValue());
14456 if (!this.blockFocus) {
14457 this.inputEl().focus();
14462 onTickableTriggerClick : function(e)
14469 this.loadNext = false;
14470 this.hasFocus = true;
14472 if(this.triggerAction == 'all') {
14473 this.doQuery(this.allQuery, true);
14475 this.doQuery(this.getRawValue());
14479 onSearchFieldClick : function(e)
14481 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14482 this.onTickableFooterButtonClick(e, false, false);
14486 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14491 this.loadNext = false;
14492 this.hasFocus = true;
14494 if(this.triggerAction == 'all') {
14495 this.doQuery(this.allQuery, true);
14497 this.doQuery(this.getRawValue());
14501 listKeyPress : function(e)
14503 //Roo.log('listkeypress');
14504 // scroll to first matching element based on key pres..
14505 if (e.isSpecialKey()) {
14508 var k = String.fromCharCode(e.getKey()).toUpperCase();
14511 var csel = this.view.getSelectedNodes();
14512 var cselitem = false;
14514 var ix = this.view.indexOf(csel[0]);
14515 cselitem = this.store.getAt(ix);
14516 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14522 this.store.each(function(v) {
14524 // start at existing selection.
14525 if (cselitem.id == v.id) {
14531 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14532 match = this.store.indexOf(v);
14538 if (match === false) {
14539 return true; // no more action?
14542 this.view.select(match);
14543 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14544 sn.scrollIntoView(sn.dom.parentNode, false);
14547 onViewScroll : function(e, t){
14549 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){
14553 this.hasQuery = true;
14555 this.loading = this.list.select('.loading', true).first();
14557 if(this.loading === null){
14558 this.list.createChild({
14560 cls: 'loading roo-select2-more-results roo-select2-active',
14561 html: 'Loading more results...'
14564 this.loading = this.list.select('.loading', true).first();
14566 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14568 this.loading.hide();
14571 this.loading.show();
14576 this.loadNext = true;
14578 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14583 addItem : function(o)
14585 var dv = ''; // display value
14587 if (this.displayField) {
14588 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14590 // this is an error condition!!!
14591 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14598 var choice = this.choices.createChild({
14600 cls: 'roo-select2-search-choice',
14609 cls: 'roo-select2-search-choice-close fa fa-times',
14614 }, this.searchField);
14616 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14618 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14626 this.inputEl().dom.value = '';
14631 onRemoveItem : function(e, _self, o)
14633 e.preventDefault();
14635 this.lastItem = Roo.apply([], this.item);
14637 var index = this.item.indexOf(o.data) * 1;
14640 Roo.log('not this item?!');
14644 this.item.splice(index, 1);
14649 this.fireEvent('remove', this, e);
14655 syncValue : function()
14657 if(!this.item.length){
14664 Roo.each(this.item, function(i){
14665 if(_this.valueField){
14666 value.push(i[_this.valueField]);
14673 this.value = value.join(',');
14675 if(this.hiddenField){
14676 this.hiddenField.dom.value = this.value;
14679 this.store.fireEvent("datachanged", this.store);
14684 clearItem : function()
14686 if(!this.multiple){
14692 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14700 if(this.tickable && !Roo.isTouch){
14701 this.view.refresh();
14705 inputEl: function ()
14707 if(Roo.isIOS && this.useNativeIOS){
14708 return this.el.select('select.roo-ios-select', true).first();
14711 if(Roo.isTouch && this.mobileTouchView){
14712 return this.el.select('input.form-control',true).first();
14716 return this.searchField;
14719 return this.el.select('input.form-control',true).first();
14722 onTickableFooterButtonClick : function(e, btn, el)
14724 e.preventDefault();
14726 this.lastItem = Roo.apply([], this.item);
14728 if(btn && btn.name == 'cancel'){
14729 this.tickItems = Roo.apply([], this.item);
14738 Roo.each(this.tickItems, function(o){
14746 validate : function()
14748 if(this.getVisibilityEl().hasClass('hidden')){
14752 var v = this.getRawValue();
14755 v = this.getValue();
14758 if(this.disabled || this.allowBlank || v.length){
14763 this.markInvalid();
14767 tickableInputEl : function()
14769 if(!this.tickable || !this.editable){
14770 return this.inputEl();
14773 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14777 getAutoCreateTouchView : function()
14782 cls: 'form-group' //input-group
14788 type : this.inputType,
14789 cls : 'form-control x-combo-noedit',
14790 autocomplete: 'new-password',
14791 placeholder : this.placeholder || '',
14796 input.name = this.name;
14800 input.cls += ' input-' + this.size;
14803 if (this.disabled) {
14804 input.disabled = true;
14815 inputblock.cls += ' input-group';
14817 inputblock.cn.unshift({
14819 cls : 'input-group-addon',
14824 if(this.removable && !this.multiple){
14825 inputblock.cls += ' roo-removable';
14827 inputblock.cn.push({
14830 cls : 'roo-combo-removable-btn close'
14834 if(this.hasFeedback && !this.allowBlank){
14836 inputblock.cls += ' has-feedback';
14838 inputblock.cn.push({
14840 cls: 'glyphicon form-control-feedback'
14847 inputblock.cls += (this.before) ? '' : ' input-group';
14849 inputblock.cn.push({
14851 cls : 'input-group-addon',
14862 cls: 'form-hidden-field'
14876 cls: 'form-hidden-field'
14880 cls: 'roo-select2-choices',
14884 cls: 'roo-select2-search-field',
14897 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14903 if(!this.multiple && this.showToggleBtn){
14910 if (this.caret != false) {
14913 cls: 'fa fa-' + this.caret
14920 cls : 'input-group-addon btn dropdown-toggle',
14925 cls: 'combobox-clear',
14939 combobox.cls += ' roo-select2-container-multi';
14942 var align = this.labelAlign || this.parentLabelAlign();
14944 if (align ==='left' && this.fieldLabel.length) {
14949 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14950 tooltip : 'This field is required'
14954 cls : 'control-label',
14955 html : this.fieldLabel
14966 var labelCfg = cfg.cn[1];
14967 var contentCfg = cfg.cn[2];
14970 if(this.indicatorpos == 'right'){
14975 cls : 'control-label',
14979 html : this.fieldLabel
14983 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14984 tooltip : 'This field is required'
14997 labelCfg = cfg.cn[0];
14998 contentCfg = cfg.cn[1];
15003 if(this.labelWidth > 12){
15004 labelCfg.style = "width: " + this.labelWidth + 'px';
15007 if(this.labelWidth < 13 && this.labelmd == 0){
15008 this.labelmd = this.labelWidth;
15011 if(this.labellg > 0){
15012 labelCfg.cls += ' col-lg-' + this.labellg;
15013 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15016 if(this.labelmd > 0){
15017 labelCfg.cls += ' col-md-' + this.labelmd;
15018 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15021 if(this.labelsm > 0){
15022 labelCfg.cls += ' col-sm-' + this.labelsm;
15023 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15026 if(this.labelxs > 0){
15027 labelCfg.cls += ' col-xs-' + this.labelxs;
15028 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15032 } else if ( this.fieldLabel.length) {
15036 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15037 tooltip : 'This field is required'
15041 cls : 'control-label',
15042 html : this.fieldLabel
15053 if(this.indicatorpos == 'right'){
15057 cls : 'control-label',
15058 html : this.fieldLabel,
15062 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15063 tooltip : 'This field is required'
15080 var settings = this;
15082 ['xs','sm','md','lg'].map(function(size){
15083 if (settings[size]) {
15084 cfg.cls += ' col-' + size + '-' + settings[size];
15091 initTouchView : function()
15093 this.renderTouchView();
15095 this.touchViewEl.on('scroll', function(){
15096 this.el.dom.scrollTop = 0;
15099 this.originalValue = this.getValue();
15101 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15103 this.inputEl().on("click", this.showTouchView, this);
15104 if (this.triggerEl) {
15105 this.triggerEl.on("click", this.showTouchView, this);
15109 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15110 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15112 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15114 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15115 this.store.on('load', this.onTouchViewLoad, this);
15116 this.store.on('loadexception', this.onTouchViewLoadException, this);
15118 if(this.hiddenName){
15120 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15122 this.hiddenField.dom.value =
15123 this.hiddenValue !== undefined ? this.hiddenValue :
15124 this.value !== undefined ? this.value : '';
15126 this.el.dom.removeAttribute('name');
15127 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15131 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15132 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15135 if(this.removable && !this.multiple){
15136 var close = this.closeTriggerEl();
15138 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15139 close.on('click', this.removeBtnClick, this, close);
15143 * fix the bug in Safari iOS8
15145 this.inputEl().on("focus", function(e){
15146 document.activeElement.blur();
15154 renderTouchView : function()
15156 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15157 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15159 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15160 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15162 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15163 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15164 this.touchViewBodyEl.setStyle('overflow', 'auto');
15166 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15167 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15169 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15170 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15174 showTouchView : function()
15180 this.touchViewHeaderEl.hide();
15182 if(this.modalTitle.length){
15183 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15184 this.touchViewHeaderEl.show();
15187 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15188 this.touchViewEl.show();
15190 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15192 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15193 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15195 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15197 if(this.modalTitle.length){
15198 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15201 this.touchViewBodyEl.setHeight(bodyHeight);
15205 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15207 this.touchViewEl.addClass('in');
15210 this.doTouchViewQuery();
15214 hideTouchView : function()
15216 this.touchViewEl.removeClass('in');
15220 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15222 this.touchViewEl.setStyle('display', 'none');
15227 setTouchViewValue : function()
15234 Roo.each(this.tickItems, function(o){
15239 this.hideTouchView();
15242 doTouchViewQuery : function()
15251 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15255 if(!this.alwaysQuery || this.mode == 'local'){
15256 this.onTouchViewLoad();
15263 onTouchViewBeforeLoad : function(combo,opts)
15269 onTouchViewLoad : function()
15271 if(this.store.getCount() < 1){
15272 this.onTouchViewEmptyResults();
15276 this.clearTouchView();
15278 var rawValue = this.getRawValue();
15280 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15282 this.tickItems = [];
15284 this.store.data.each(function(d, rowIndex){
15285 var row = this.touchViewListGroup.createChild(template);
15287 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15288 row.addClass(d.data.cls);
15291 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15294 html : d.data[this.displayField]
15297 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15298 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15301 row.removeClass('selected');
15302 if(!this.multiple && this.valueField &&
15303 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15306 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15307 row.addClass('selected');
15310 if(this.multiple && this.valueField &&
15311 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15315 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15316 this.tickItems.push(d.data);
15319 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15323 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15325 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15327 if(this.modalTitle.length){
15328 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15331 var listHeight = this.touchViewListGroup.getHeight();
15335 if(firstChecked && listHeight > bodyHeight){
15336 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15341 onTouchViewLoadException : function()
15343 this.hideTouchView();
15346 onTouchViewEmptyResults : function()
15348 this.clearTouchView();
15350 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15352 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15356 clearTouchView : function()
15358 this.touchViewListGroup.dom.innerHTML = '';
15361 onTouchViewClick : function(e, el, o)
15363 e.preventDefault();
15366 var rowIndex = o.rowIndex;
15368 var r = this.store.getAt(rowIndex);
15370 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15372 if(!this.multiple){
15373 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15374 c.dom.removeAttribute('checked');
15377 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15379 this.setFromData(r.data);
15381 var close = this.closeTriggerEl();
15387 this.hideTouchView();
15389 this.fireEvent('select', this, r, rowIndex);
15394 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15395 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15396 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15400 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15401 this.addItem(r.data);
15402 this.tickItems.push(r.data);
15406 getAutoCreateNativeIOS : function()
15409 cls: 'form-group' //input-group,
15414 cls : 'roo-ios-select'
15418 combobox.name = this.name;
15421 if (this.disabled) {
15422 combobox.disabled = true;
15425 var settings = this;
15427 ['xs','sm','md','lg'].map(function(size){
15428 if (settings[size]) {
15429 cfg.cls += ' col-' + size + '-' + settings[size];
15439 initIOSView : function()
15441 this.store.on('load', this.onIOSViewLoad, this);
15446 onIOSViewLoad : function()
15448 if(this.store.getCount() < 1){
15452 this.clearIOSView();
15454 if(this.allowBlank) {
15456 var default_text = '-- SELECT --';
15458 if(this.placeholder.length){
15459 default_text = this.placeholder;
15462 if(this.emptyTitle.length){
15463 default_text += ' - ' + this.emptyTitle + ' -';
15466 var opt = this.inputEl().createChild({
15469 html : default_text
15473 o[this.valueField] = 0;
15474 o[this.displayField] = default_text;
15476 this.ios_options.push({
15483 this.store.data.each(function(d, rowIndex){
15487 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15488 html = d.data[this.displayField];
15493 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15494 value = d.data[this.valueField];
15503 if(this.value == d.data[this.valueField]){
15504 option['selected'] = true;
15507 var opt = this.inputEl().createChild(option);
15509 this.ios_options.push({
15516 this.inputEl().on('change', function(){
15517 this.fireEvent('select', this);
15522 clearIOSView: function()
15524 this.inputEl().dom.innerHTML = '';
15526 this.ios_options = [];
15529 setIOSValue: function(v)
15533 if(!this.ios_options){
15537 Roo.each(this.ios_options, function(opts){
15539 opts.el.dom.removeAttribute('selected');
15541 if(opts.data[this.valueField] != v){
15545 opts.el.dom.setAttribute('selected', true);
15551 * @cfg {Boolean} grow
15555 * @cfg {Number} growMin
15559 * @cfg {Number} growMax
15568 Roo.apply(Roo.bootstrap.ComboBox, {
15572 cls: 'modal-header',
15594 cls: 'list-group-item',
15598 cls: 'roo-combobox-list-group-item-value'
15602 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15616 listItemCheckbox : {
15618 cls: 'list-group-item',
15622 cls: 'roo-combobox-list-group-item-value'
15626 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15642 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15647 cls: 'modal-footer',
15655 cls: 'col-xs-6 text-left',
15658 cls: 'btn btn-danger roo-touch-view-cancel',
15664 cls: 'col-xs-6 text-right',
15667 cls: 'btn btn-success roo-touch-view-ok',
15678 Roo.apply(Roo.bootstrap.ComboBox, {
15680 touchViewTemplate : {
15682 cls: 'modal fade roo-combobox-touch-view',
15686 cls: 'modal-dialog',
15687 style : 'position:fixed', // we have to fix position....
15691 cls: 'modal-content',
15693 Roo.bootstrap.ComboBox.header,
15694 Roo.bootstrap.ComboBox.body,
15695 Roo.bootstrap.ComboBox.footer
15704 * Ext JS Library 1.1.1
15705 * Copyright(c) 2006-2007, Ext JS, LLC.
15707 * Originally Released Under LGPL - original licence link has changed is not relivant.
15710 * <script type="text/javascript">
15715 * @extends Roo.util.Observable
15716 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15717 * This class also supports single and multi selection modes. <br>
15718 * Create a data model bound view:
15720 var store = new Roo.data.Store(...);
15722 var view = new Roo.View({
15724 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15726 singleSelect: true,
15727 selectedClass: "ydataview-selected",
15731 // listen for node click?
15732 view.on("click", function(vw, index, node, e){
15733 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15737 dataModel.load("foobar.xml");
15739 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15741 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15742 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15744 * Note: old style constructor is still suported (container, template, config)
15747 * Create a new View
15748 * @param {Object} config The config object
15751 Roo.View = function(config, depreciated_tpl, depreciated_config){
15753 this.parent = false;
15755 if (typeof(depreciated_tpl) == 'undefined') {
15756 // new way.. - universal constructor.
15757 Roo.apply(this, config);
15758 this.el = Roo.get(this.el);
15761 this.el = Roo.get(config);
15762 this.tpl = depreciated_tpl;
15763 Roo.apply(this, depreciated_config);
15765 this.wrapEl = this.el.wrap().wrap();
15766 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15769 if(typeof(this.tpl) == "string"){
15770 this.tpl = new Roo.Template(this.tpl);
15772 // support xtype ctors..
15773 this.tpl = new Roo.factory(this.tpl, Roo);
15777 this.tpl.compile();
15782 * @event beforeclick
15783 * Fires before a click is processed. Returns false to cancel the default action.
15784 * @param {Roo.View} this
15785 * @param {Number} index The index of the target node
15786 * @param {HTMLElement} node The target node
15787 * @param {Roo.EventObject} e The raw event object
15789 "beforeclick" : true,
15792 * Fires when a template node is clicked.
15793 * @param {Roo.View} this
15794 * @param {Number} index The index of the target node
15795 * @param {HTMLElement} node The target node
15796 * @param {Roo.EventObject} e The raw event object
15801 * Fires when a template node is double clicked.
15802 * @param {Roo.View} this
15803 * @param {Number} index The index of the target node
15804 * @param {HTMLElement} node The target node
15805 * @param {Roo.EventObject} e The raw event object
15809 * @event contextmenu
15810 * Fires when a template node is right clicked.
15811 * @param {Roo.View} this
15812 * @param {Number} index The index of the target node
15813 * @param {HTMLElement} node The target node
15814 * @param {Roo.EventObject} e The raw event object
15816 "contextmenu" : true,
15818 * @event selectionchange
15819 * Fires when the selected nodes change.
15820 * @param {Roo.View} this
15821 * @param {Array} selections Array of the selected nodes
15823 "selectionchange" : true,
15826 * @event beforeselect
15827 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15828 * @param {Roo.View} this
15829 * @param {HTMLElement} node The node to be selected
15830 * @param {Array} selections Array of currently selected nodes
15832 "beforeselect" : true,
15834 * @event preparedata
15835 * Fires on every row to render, to allow you to change the data.
15836 * @param {Roo.View} this
15837 * @param {Object} data to be rendered (change this)
15839 "preparedata" : true
15847 "click": this.onClick,
15848 "dblclick": this.onDblClick,
15849 "contextmenu": this.onContextMenu,
15853 this.selections = [];
15855 this.cmp = new Roo.CompositeElementLite([]);
15857 this.store = Roo.factory(this.store, Roo.data);
15858 this.setStore(this.store, true);
15861 if ( this.footer && this.footer.xtype) {
15863 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15865 this.footer.dataSource = this.store;
15866 this.footer.container = fctr;
15867 this.footer = Roo.factory(this.footer, Roo);
15868 fctr.insertFirst(this.el);
15870 // this is a bit insane - as the paging toolbar seems to detach the el..
15871 // dom.parentNode.parentNode.parentNode
15872 // they get detached?
15876 Roo.View.superclass.constructor.call(this);
15881 Roo.extend(Roo.View, Roo.util.Observable, {
15884 * @cfg {Roo.data.Store} store Data store to load data from.
15889 * @cfg {String|Roo.Element} el The container element.
15894 * @cfg {String|Roo.Template} tpl The template used by this View
15898 * @cfg {String} dataName the named area of the template to use as the data area
15899 * Works with domtemplates roo-name="name"
15903 * @cfg {String} selectedClass The css class to add to selected nodes
15905 selectedClass : "x-view-selected",
15907 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15912 * @cfg {String} text to display on mask (default Loading)
15916 * @cfg {Boolean} multiSelect Allow multiple selection
15918 multiSelect : false,
15920 * @cfg {Boolean} singleSelect Allow single selection
15922 singleSelect: false,
15925 * @cfg {Boolean} toggleSelect - selecting
15927 toggleSelect : false,
15930 * @cfg {Boolean} tickable - selecting
15935 * Returns the element this view is bound to.
15936 * @return {Roo.Element}
15938 getEl : function(){
15939 return this.wrapEl;
15945 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15947 refresh : function(){
15948 //Roo.log('refresh');
15951 // if we are using something like 'domtemplate', then
15952 // the what gets used is:
15953 // t.applySubtemplate(NAME, data, wrapping data..)
15954 // the outer template then get' applied with
15955 // the store 'extra data'
15956 // and the body get's added to the
15957 // roo-name="data" node?
15958 // <span class='roo-tpl-{name}'></span> ?????
15962 this.clearSelections();
15963 this.el.update("");
15965 var records = this.store.getRange();
15966 if(records.length < 1) {
15968 // is this valid?? = should it render a template??
15970 this.el.update(this.emptyText);
15974 if (this.dataName) {
15975 this.el.update(t.apply(this.store.meta)); //????
15976 el = this.el.child('.roo-tpl-' + this.dataName);
15979 for(var i = 0, len = records.length; i < len; i++){
15980 var data = this.prepareData(records[i].data, i, records[i]);
15981 this.fireEvent("preparedata", this, data, i, records[i]);
15983 var d = Roo.apply({}, data);
15986 Roo.apply(d, {'roo-id' : Roo.id()});
15990 Roo.each(this.parent.item, function(item){
15991 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15994 Roo.apply(d, {'roo-data-checked' : 'checked'});
15998 html[html.length] = Roo.util.Format.trim(
16000 t.applySubtemplate(this.dataName, d, this.store.meta) :
16007 el.update(html.join(""));
16008 this.nodes = el.dom.childNodes;
16009 this.updateIndexes(0);
16014 * Function to override to reformat the data that is sent to
16015 * the template for each node.
16016 * DEPRICATED - use the preparedata event handler.
16017 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16018 * a JSON object for an UpdateManager bound view).
16020 prepareData : function(data, index, record)
16022 this.fireEvent("preparedata", this, data, index, record);
16026 onUpdate : function(ds, record){
16027 // Roo.log('on update');
16028 this.clearSelections();
16029 var index = this.store.indexOf(record);
16030 var n = this.nodes[index];
16031 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16032 n.parentNode.removeChild(n);
16033 this.updateIndexes(index, index);
16039 onAdd : function(ds, records, index)
16041 //Roo.log(['on Add', ds, records, index] );
16042 this.clearSelections();
16043 if(this.nodes.length == 0){
16047 var n = this.nodes[index];
16048 for(var i = 0, len = records.length; i < len; i++){
16049 var d = this.prepareData(records[i].data, i, records[i]);
16051 this.tpl.insertBefore(n, d);
16054 this.tpl.append(this.el, d);
16057 this.updateIndexes(index);
16060 onRemove : function(ds, record, index){
16061 // Roo.log('onRemove');
16062 this.clearSelections();
16063 var el = this.dataName ?
16064 this.el.child('.roo-tpl-' + this.dataName) :
16067 el.dom.removeChild(this.nodes[index]);
16068 this.updateIndexes(index);
16072 * Refresh an individual node.
16073 * @param {Number} index
16075 refreshNode : function(index){
16076 this.onUpdate(this.store, this.store.getAt(index));
16079 updateIndexes : function(startIndex, endIndex){
16080 var ns = this.nodes;
16081 startIndex = startIndex || 0;
16082 endIndex = endIndex || ns.length - 1;
16083 for(var i = startIndex; i <= endIndex; i++){
16084 ns[i].nodeIndex = i;
16089 * Changes the data store this view uses and refresh the view.
16090 * @param {Store} store
16092 setStore : function(store, initial){
16093 if(!initial && this.store){
16094 this.store.un("datachanged", this.refresh);
16095 this.store.un("add", this.onAdd);
16096 this.store.un("remove", this.onRemove);
16097 this.store.un("update", this.onUpdate);
16098 this.store.un("clear", this.refresh);
16099 this.store.un("beforeload", this.onBeforeLoad);
16100 this.store.un("load", this.onLoad);
16101 this.store.un("loadexception", this.onLoad);
16105 store.on("datachanged", this.refresh, this);
16106 store.on("add", this.onAdd, this);
16107 store.on("remove", this.onRemove, this);
16108 store.on("update", this.onUpdate, this);
16109 store.on("clear", this.refresh, this);
16110 store.on("beforeload", this.onBeforeLoad, this);
16111 store.on("load", this.onLoad, this);
16112 store.on("loadexception", this.onLoad, this);
16120 * onbeforeLoad - masks the loading area.
16123 onBeforeLoad : function(store,opts)
16125 //Roo.log('onBeforeLoad');
16127 this.el.update("");
16129 this.el.mask(this.mask ? this.mask : "Loading" );
16131 onLoad : function ()
16138 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16139 * @param {HTMLElement} node
16140 * @return {HTMLElement} The template node
16142 findItemFromChild : function(node){
16143 var el = this.dataName ?
16144 this.el.child('.roo-tpl-' + this.dataName,true) :
16147 if(!node || node.parentNode == el){
16150 var p = node.parentNode;
16151 while(p && p != el){
16152 if(p.parentNode == el){
16161 onClick : function(e){
16162 var item = this.findItemFromChild(e.getTarget());
16164 var index = this.indexOf(item);
16165 if(this.onItemClick(item, index, e) !== false){
16166 this.fireEvent("click", this, index, item, e);
16169 this.clearSelections();
16174 onContextMenu : function(e){
16175 var item = this.findItemFromChild(e.getTarget());
16177 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16182 onDblClick : function(e){
16183 var item = this.findItemFromChild(e.getTarget());
16185 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16189 onItemClick : function(item, index, e)
16191 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16194 if (this.toggleSelect) {
16195 var m = this.isSelected(item) ? 'unselect' : 'select';
16198 _t[m](item, true, false);
16201 if(this.multiSelect || this.singleSelect){
16202 if(this.multiSelect && e.shiftKey && this.lastSelection){
16203 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16205 this.select(item, this.multiSelect && e.ctrlKey);
16206 this.lastSelection = item;
16209 if(!this.tickable){
16210 e.preventDefault();
16218 * Get the number of selected nodes.
16221 getSelectionCount : function(){
16222 return this.selections.length;
16226 * Get the currently selected nodes.
16227 * @return {Array} An array of HTMLElements
16229 getSelectedNodes : function(){
16230 return this.selections;
16234 * Get the indexes of the selected nodes.
16237 getSelectedIndexes : function(){
16238 var indexes = [], s = this.selections;
16239 for(var i = 0, len = s.length; i < len; i++){
16240 indexes.push(s[i].nodeIndex);
16246 * Clear all selections
16247 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16249 clearSelections : function(suppressEvent){
16250 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16251 this.cmp.elements = this.selections;
16252 this.cmp.removeClass(this.selectedClass);
16253 this.selections = [];
16254 if(!suppressEvent){
16255 this.fireEvent("selectionchange", this, this.selections);
16261 * Returns true if the passed node is selected
16262 * @param {HTMLElement/Number} node The node or node index
16263 * @return {Boolean}
16265 isSelected : function(node){
16266 var s = this.selections;
16270 node = this.getNode(node);
16271 return s.indexOf(node) !== -1;
16276 * @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
16277 * @param {Boolean} keepExisting (optional) true to keep existing selections
16278 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16280 select : function(nodeInfo, keepExisting, suppressEvent){
16281 if(nodeInfo instanceof Array){
16283 this.clearSelections(true);
16285 for(var i = 0, len = nodeInfo.length; i < len; i++){
16286 this.select(nodeInfo[i], true, true);
16290 var node = this.getNode(nodeInfo);
16291 if(!node || this.isSelected(node)){
16292 return; // already selected.
16295 this.clearSelections(true);
16298 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16299 Roo.fly(node).addClass(this.selectedClass);
16300 this.selections.push(node);
16301 if(!suppressEvent){
16302 this.fireEvent("selectionchange", this, this.selections);
16310 * @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
16311 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16312 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16314 unselect : function(nodeInfo, keepExisting, suppressEvent)
16316 if(nodeInfo instanceof Array){
16317 Roo.each(this.selections, function(s) {
16318 this.unselect(s, nodeInfo);
16322 var node = this.getNode(nodeInfo);
16323 if(!node || !this.isSelected(node)){
16324 //Roo.log("not selected");
16325 return; // not selected.
16329 Roo.each(this.selections, function(s) {
16331 Roo.fly(node).removeClass(this.selectedClass);
16338 this.selections= ns;
16339 this.fireEvent("selectionchange", this, this.selections);
16343 * Gets a template node.
16344 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16345 * @return {HTMLElement} The node or null if it wasn't found
16347 getNode : function(nodeInfo){
16348 if(typeof nodeInfo == "string"){
16349 return document.getElementById(nodeInfo);
16350 }else if(typeof nodeInfo == "number"){
16351 return this.nodes[nodeInfo];
16357 * Gets a range template nodes.
16358 * @param {Number} startIndex
16359 * @param {Number} endIndex
16360 * @return {Array} An array of nodes
16362 getNodes : function(start, end){
16363 var ns = this.nodes;
16364 start = start || 0;
16365 end = typeof end == "undefined" ? ns.length - 1 : end;
16368 for(var i = start; i <= end; i++){
16372 for(var i = start; i >= end; i--){
16380 * Finds the index of the passed node
16381 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16382 * @return {Number} The index of the node or -1
16384 indexOf : function(node){
16385 node = this.getNode(node);
16386 if(typeof node.nodeIndex == "number"){
16387 return node.nodeIndex;
16389 var ns = this.nodes;
16390 for(var i = 0, len = ns.length; i < len; i++){
16401 * based on jquery fullcalendar
16405 Roo.bootstrap = Roo.bootstrap || {};
16407 * @class Roo.bootstrap.Calendar
16408 * @extends Roo.bootstrap.Component
16409 * Bootstrap Calendar class
16410 * @cfg {Boolean} loadMask (true|false) default false
16411 * @cfg {Object} header generate the user specific header of the calendar, default false
16414 * Create a new Container
16415 * @param {Object} config The config object
16420 Roo.bootstrap.Calendar = function(config){
16421 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16425 * Fires when a date is selected
16426 * @param {DatePicker} this
16427 * @param {Date} date The selected date
16431 * @event monthchange
16432 * Fires when the displayed month changes
16433 * @param {DatePicker} this
16434 * @param {Date} date The selected month
16436 'monthchange': true,
16438 * @event evententer
16439 * Fires when mouse over an event
16440 * @param {Calendar} this
16441 * @param {event} Event
16443 'evententer': true,
16445 * @event eventleave
16446 * Fires when the mouse leaves an
16447 * @param {Calendar} this
16450 'eventleave': true,
16452 * @event eventclick
16453 * Fires when the mouse click an
16454 * @param {Calendar} this
16463 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16466 * @cfg {Number} startDay
16467 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16475 getAutoCreate : function(){
16478 var fc_button = function(name, corner, style, content ) {
16479 return Roo.apply({},{
16481 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16483 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16486 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16497 style : 'width:100%',
16504 cls : 'fc-header-left',
16506 fc_button('prev', 'left', 'arrow', '‹' ),
16507 fc_button('next', 'right', 'arrow', '›' ),
16508 { tag: 'span', cls: 'fc-header-space' },
16509 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16517 cls : 'fc-header-center',
16521 cls: 'fc-header-title',
16524 html : 'month / year'
16532 cls : 'fc-header-right',
16534 /* fc_button('month', 'left', '', 'month' ),
16535 fc_button('week', '', '', 'week' ),
16536 fc_button('day', 'right', '', 'day' )
16548 header = this.header;
16551 var cal_heads = function() {
16553 // fixme - handle this.
16555 for (var i =0; i < Date.dayNames.length; i++) {
16556 var d = Date.dayNames[i];
16559 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16560 html : d.substring(0,3)
16564 ret[0].cls += ' fc-first';
16565 ret[6].cls += ' fc-last';
16568 var cal_cell = function(n) {
16571 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16576 cls: 'fc-day-number',
16580 cls: 'fc-day-content',
16584 style: 'position: relative;' // height: 17px;
16596 var cal_rows = function() {
16599 for (var r = 0; r < 6; r++) {
16606 for (var i =0; i < Date.dayNames.length; i++) {
16607 var d = Date.dayNames[i];
16608 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16611 row.cn[0].cls+=' fc-first';
16612 row.cn[0].cn[0].style = 'min-height:90px';
16613 row.cn[6].cls+=' fc-last';
16617 ret[0].cls += ' fc-first';
16618 ret[4].cls += ' fc-prev-last';
16619 ret[5].cls += ' fc-last';
16626 cls: 'fc-border-separate',
16627 style : 'width:100%',
16635 cls : 'fc-first fc-last',
16653 cls : 'fc-content',
16654 style : "position: relative;",
16657 cls : 'fc-view fc-view-month fc-grid',
16658 style : 'position: relative',
16659 unselectable : 'on',
16662 cls : 'fc-event-container',
16663 style : 'position:absolute;z-index:8;top:0;left:0;'
16681 initEvents : function()
16684 throw "can not find store for calendar";
16690 style: "text-align:center",
16694 style: "background-color:white;width:50%;margin:250 auto",
16698 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16709 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16711 var size = this.el.select('.fc-content', true).first().getSize();
16712 this.maskEl.setSize(size.width, size.height);
16713 this.maskEl.enableDisplayMode("block");
16714 if(!this.loadMask){
16715 this.maskEl.hide();
16718 this.store = Roo.factory(this.store, Roo.data);
16719 this.store.on('load', this.onLoad, this);
16720 this.store.on('beforeload', this.onBeforeLoad, this);
16724 this.cells = this.el.select('.fc-day',true);
16725 //Roo.log(this.cells);
16726 this.textNodes = this.el.query('.fc-day-number');
16727 this.cells.addClassOnOver('fc-state-hover');
16729 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16730 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16731 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16732 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16734 this.on('monthchange', this.onMonthChange, this);
16736 this.update(new Date().clearTime());
16739 resize : function() {
16740 var sz = this.el.getSize();
16742 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16743 this.el.select('.fc-day-content div',true).setHeight(34);
16748 showPrevMonth : function(e){
16749 this.update(this.activeDate.add("mo", -1));
16751 showToday : function(e){
16752 this.update(new Date().clearTime());
16755 showNextMonth : function(e){
16756 this.update(this.activeDate.add("mo", 1));
16760 showPrevYear : function(){
16761 this.update(this.activeDate.add("y", -1));
16765 showNextYear : function(){
16766 this.update(this.activeDate.add("y", 1));
16771 update : function(date)
16773 var vd = this.activeDate;
16774 this.activeDate = date;
16775 // if(vd && this.el){
16776 // var t = date.getTime();
16777 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16778 // Roo.log('using add remove');
16780 // this.fireEvent('monthchange', this, date);
16782 // this.cells.removeClass("fc-state-highlight");
16783 // this.cells.each(function(c){
16784 // if(c.dateValue == t){
16785 // c.addClass("fc-state-highlight");
16786 // setTimeout(function(){
16787 // try{c.dom.firstChild.focus();}catch(e){}
16797 var days = date.getDaysInMonth();
16799 var firstOfMonth = date.getFirstDateOfMonth();
16800 var startingPos = firstOfMonth.getDay()-this.startDay;
16802 if(startingPos < this.startDay){
16806 var pm = date.add(Date.MONTH, -1);
16807 var prevStart = pm.getDaysInMonth()-startingPos;
16809 this.cells = this.el.select('.fc-day',true);
16810 this.textNodes = this.el.query('.fc-day-number');
16811 this.cells.addClassOnOver('fc-state-hover');
16813 var cells = this.cells.elements;
16814 var textEls = this.textNodes;
16816 Roo.each(cells, function(cell){
16817 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16820 days += startingPos;
16822 // convert everything to numbers so it's fast
16823 var day = 86400000;
16824 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16827 //Roo.log(prevStart);
16829 var today = new Date().clearTime().getTime();
16830 var sel = date.clearTime().getTime();
16831 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16832 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16833 var ddMatch = this.disabledDatesRE;
16834 var ddText = this.disabledDatesText;
16835 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16836 var ddaysText = this.disabledDaysText;
16837 var format = this.format;
16839 var setCellClass = function(cal, cell){
16843 //Roo.log('set Cell Class');
16845 var t = d.getTime();
16849 cell.dateValue = t;
16851 cell.className += " fc-today";
16852 cell.className += " fc-state-highlight";
16853 cell.title = cal.todayText;
16856 // disable highlight in other month..
16857 //cell.className += " fc-state-highlight";
16862 cell.className = " fc-state-disabled";
16863 cell.title = cal.minText;
16867 cell.className = " fc-state-disabled";
16868 cell.title = cal.maxText;
16872 if(ddays.indexOf(d.getDay()) != -1){
16873 cell.title = ddaysText;
16874 cell.className = " fc-state-disabled";
16877 if(ddMatch && format){
16878 var fvalue = d.dateFormat(format);
16879 if(ddMatch.test(fvalue)){
16880 cell.title = ddText.replace("%0", fvalue);
16881 cell.className = " fc-state-disabled";
16885 if (!cell.initialClassName) {
16886 cell.initialClassName = cell.dom.className;
16889 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16894 for(; i < startingPos; i++) {
16895 textEls[i].innerHTML = (++prevStart);
16896 d.setDate(d.getDate()+1);
16898 cells[i].className = "fc-past fc-other-month";
16899 setCellClass(this, cells[i]);
16904 for(; i < days; i++){
16905 intDay = i - startingPos + 1;
16906 textEls[i].innerHTML = (intDay);
16907 d.setDate(d.getDate()+1);
16909 cells[i].className = ''; // "x-date-active";
16910 setCellClass(this, cells[i]);
16914 for(; i < 42; i++) {
16915 textEls[i].innerHTML = (++extraDays);
16916 d.setDate(d.getDate()+1);
16918 cells[i].className = "fc-future fc-other-month";
16919 setCellClass(this, cells[i]);
16922 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16924 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16926 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16927 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16929 if(totalRows != 6){
16930 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16931 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16934 this.fireEvent('monthchange', this, date);
16938 if(!this.internalRender){
16939 var main = this.el.dom.firstChild;
16940 var w = main.offsetWidth;
16941 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16942 Roo.fly(main).setWidth(w);
16943 this.internalRender = true;
16944 // opera does not respect the auto grow header center column
16945 // then, after it gets a width opera refuses to recalculate
16946 // without a second pass
16947 if(Roo.isOpera && !this.secondPass){
16948 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16949 this.secondPass = true;
16950 this.update.defer(10, this, [date]);
16957 findCell : function(dt) {
16958 dt = dt.clearTime().getTime();
16960 this.cells.each(function(c){
16961 //Roo.log("check " +c.dateValue + '?=' + dt);
16962 if(c.dateValue == dt){
16972 findCells : function(ev) {
16973 var s = ev.start.clone().clearTime().getTime();
16975 var e= ev.end.clone().clearTime().getTime();
16978 this.cells.each(function(c){
16979 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16981 if(c.dateValue > e){
16984 if(c.dateValue < s){
16993 // findBestRow: function(cells)
16997 // for (var i =0 ; i < cells.length;i++) {
16998 // ret = Math.max(cells[i].rows || 0,ret);
17005 addItem : function(ev)
17007 // look for vertical location slot in
17008 var cells = this.findCells(ev);
17010 // ev.row = this.findBestRow(cells);
17012 // work out the location.
17016 for(var i =0; i < cells.length; i++) {
17018 cells[i].row = cells[0].row;
17021 cells[i].row = cells[i].row + 1;
17031 if (crow.start.getY() == cells[i].getY()) {
17033 crow.end = cells[i];
17050 cells[0].events.push(ev);
17052 this.calevents.push(ev);
17055 clearEvents: function() {
17057 if(!this.calevents){
17061 Roo.each(this.cells.elements, function(c){
17067 Roo.each(this.calevents, function(e) {
17068 Roo.each(e.els, function(el) {
17069 el.un('mouseenter' ,this.onEventEnter, this);
17070 el.un('mouseleave' ,this.onEventLeave, this);
17075 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17081 renderEvents: function()
17085 this.cells.each(function(c) {
17094 if(c.row != c.events.length){
17095 r = 4 - (4 - (c.row - c.events.length));
17098 c.events = ev.slice(0, r);
17099 c.more = ev.slice(r);
17101 if(c.more.length && c.more.length == 1){
17102 c.events.push(c.more.pop());
17105 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17109 this.cells.each(function(c) {
17111 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17114 for (var e = 0; e < c.events.length; e++){
17115 var ev = c.events[e];
17116 var rows = ev.rows;
17118 for(var i = 0; i < rows.length; i++) {
17120 // how many rows should it span..
17123 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17124 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17126 unselectable : "on",
17129 cls: 'fc-event-inner',
17133 // cls: 'fc-event-time',
17134 // html : cells.length > 1 ? '' : ev.time
17138 cls: 'fc-event-title',
17139 html : String.format('{0}', ev.title)
17146 cls: 'ui-resizable-handle ui-resizable-e',
17147 html : '  '
17154 cfg.cls += ' fc-event-start';
17156 if ((i+1) == rows.length) {
17157 cfg.cls += ' fc-event-end';
17160 var ctr = _this.el.select('.fc-event-container',true).first();
17161 var cg = ctr.createChild(cfg);
17163 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17164 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17166 var r = (c.more.length) ? 1 : 0;
17167 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17168 cg.setWidth(ebox.right - sbox.x -2);
17170 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17171 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17172 cg.on('click', _this.onEventClick, _this, ev);
17183 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17184 style : 'position: absolute',
17185 unselectable : "on",
17188 cls: 'fc-event-inner',
17192 cls: 'fc-event-title',
17200 cls: 'ui-resizable-handle ui-resizable-e',
17201 html : '  '
17207 var ctr = _this.el.select('.fc-event-container',true).first();
17208 var cg = ctr.createChild(cfg);
17210 var sbox = c.select('.fc-day-content',true).first().getBox();
17211 var ebox = c.select('.fc-day-content',true).first().getBox();
17213 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17214 cg.setWidth(ebox.right - sbox.x -2);
17216 cg.on('click', _this.onMoreEventClick, _this, c.more);
17226 onEventEnter: function (e, el,event,d) {
17227 this.fireEvent('evententer', this, el, event);
17230 onEventLeave: function (e, el,event,d) {
17231 this.fireEvent('eventleave', this, el, event);
17234 onEventClick: function (e, el,event,d) {
17235 this.fireEvent('eventclick', this, el, event);
17238 onMonthChange: function () {
17242 onMoreEventClick: function(e, el, more)
17246 this.calpopover.placement = 'right';
17247 this.calpopover.setTitle('More');
17249 this.calpopover.setContent('');
17251 var ctr = this.calpopover.el.select('.popover-content', true).first();
17253 Roo.each(more, function(m){
17255 cls : 'fc-event-hori fc-event-draggable',
17258 var cg = ctr.createChild(cfg);
17260 cg.on('click', _this.onEventClick, _this, m);
17263 this.calpopover.show(el);
17268 onLoad: function ()
17270 this.calevents = [];
17273 if(this.store.getCount() > 0){
17274 this.store.data.each(function(d){
17277 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17278 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17279 time : d.data.start_time,
17280 title : d.data.title,
17281 description : d.data.description,
17282 venue : d.data.venue
17287 this.renderEvents();
17289 if(this.calevents.length && this.loadMask){
17290 this.maskEl.hide();
17294 onBeforeLoad: function()
17296 this.clearEvents();
17298 this.maskEl.show();
17312 * @class Roo.bootstrap.Popover
17313 * @extends Roo.bootstrap.Component
17314 * Bootstrap Popover class
17315 * @cfg {String} html contents of the popover (or false to use children..)
17316 * @cfg {String} title of popover (or false to hide)
17317 * @cfg {String} placement how it is placed
17318 * @cfg {String} trigger click || hover (or false to trigger manually)
17319 * @cfg {String} over what (parent or false to trigger manually.)
17320 * @cfg {Number} delay - delay before showing
17323 * Create a new Popover
17324 * @param {Object} config The config object
17327 Roo.bootstrap.Popover = function(config){
17328 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17334 * After the popover show
17336 * @param {Roo.bootstrap.Popover} this
17341 * After the popover hide
17343 * @param {Roo.bootstrap.Popover} this
17349 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17351 title: 'Fill in a title',
17354 placement : 'right',
17355 trigger : 'hover', // hover
17361 can_build_overlaid : false,
17363 getChildContainer : function()
17365 return this.el.select('.popover-content',true).first();
17368 getAutoCreate : function(){
17371 cls : 'popover roo-dynamic',
17372 style: 'display:block',
17378 cls : 'popover-inner',
17382 cls: 'popover-title',
17386 cls : 'popover-content',
17397 setTitle: function(str)
17400 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17402 setContent: function(str)
17405 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17407 // as it get's added to the bottom of the page.
17408 onRender : function(ct, position)
17410 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17412 var cfg = Roo.apply({}, this.getAutoCreate());
17416 cfg.cls += ' ' + this.cls;
17419 cfg.style = this.style;
17421 //Roo.log("adding to ");
17422 this.el = Roo.get(document.body).createChild(cfg, position);
17423 // Roo.log(this.el);
17428 initEvents : function()
17430 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17431 this.el.enableDisplayMode('block');
17433 if (this.over === false) {
17436 if (this.triggers === false) {
17439 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17440 var triggers = this.trigger ? this.trigger.split(' ') : [];
17441 Roo.each(triggers, function(trigger) {
17443 if (trigger == 'click') {
17444 on_el.on('click', this.toggle, this);
17445 } else if (trigger != 'manual') {
17446 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17447 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17449 on_el.on(eventIn ,this.enter, this);
17450 on_el.on(eventOut, this.leave, this);
17461 toggle : function () {
17462 this.hoverState == 'in' ? this.leave() : this.enter();
17465 enter : function () {
17467 clearTimeout(this.timeout);
17469 this.hoverState = 'in';
17471 if (!this.delay || !this.delay.show) {
17476 this.timeout = setTimeout(function () {
17477 if (_t.hoverState == 'in') {
17480 }, this.delay.show)
17483 leave : function() {
17484 clearTimeout(this.timeout);
17486 this.hoverState = 'out';
17488 if (!this.delay || !this.delay.hide) {
17493 this.timeout = setTimeout(function () {
17494 if (_t.hoverState == 'out') {
17497 }, this.delay.hide)
17500 show : function (on_el)
17503 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17507 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17508 if (this.html !== false) {
17509 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17511 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17512 if (!this.title.length) {
17513 this.el.select('.popover-title',true).hide();
17516 var placement = typeof this.placement == 'function' ?
17517 this.placement.call(this, this.el, on_el) :
17520 var autoToken = /\s?auto?\s?/i;
17521 var autoPlace = autoToken.test(placement);
17523 placement = placement.replace(autoToken, '') || 'top';
17527 //this.el.setXY([0,0]);
17529 this.el.dom.style.display='block';
17530 this.el.addClass(placement);
17532 //this.el.appendTo(on_el);
17534 var p = this.getPosition();
17535 var box = this.el.getBox();
17540 var align = Roo.bootstrap.Popover.alignment[placement];
17543 this.el.alignTo(on_el, align[0],align[1]);
17544 //var arrow = this.el.select('.arrow',true).first();
17545 //arrow.set(align[2],
17547 this.el.addClass('in');
17550 if (this.el.hasClass('fade')) {
17554 this.hoverState = 'in';
17556 this.fireEvent('show', this);
17561 this.el.setXY([0,0]);
17562 this.el.removeClass('in');
17564 this.hoverState = null;
17566 this.fireEvent('hide', this);
17571 Roo.bootstrap.Popover.alignment = {
17572 'left' : ['r-l', [-10,0], 'right'],
17573 'right' : ['l-r', [10,0], 'left'],
17574 'bottom' : ['t-b', [0,10], 'top'],
17575 'top' : [ 'b-t', [0,-10], 'bottom']
17586 * @class Roo.bootstrap.Progress
17587 * @extends Roo.bootstrap.Component
17588 * Bootstrap Progress class
17589 * @cfg {Boolean} striped striped of the progress bar
17590 * @cfg {Boolean} active animated of the progress bar
17594 * Create a new Progress
17595 * @param {Object} config The config object
17598 Roo.bootstrap.Progress = function(config){
17599 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17602 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17607 getAutoCreate : function(){
17615 cfg.cls += ' progress-striped';
17619 cfg.cls += ' active';
17638 * @class Roo.bootstrap.ProgressBar
17639 * @extends Roo.bootstrap.Component
17640 * Bootstrap ProgressBar class
17641 * @cfg {Number} aria_valuenow aria-value now
17642 * @cfg {Number} aria_valuemin aria-value min
17643 * @cfg {Number} aria_valuemax aria-value max
17644 * @cfg {String} label label for the progress bar
17645 * @cfg {String} panel (success | info | warning | danger )
17646 * @cfg {String} role role of the progress bar
17647 * @cfg {String} sr_only text
17651 * Create a new ProgressBar
17652 * @param {Object} config The config object
17655 Roo.bootstrap.ProgressBar = function(config){
17656 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17659 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17663 aria_valuemax : 100,
17669 getAutoCreate : function()
17674 cls: 'progress-bar',
17675 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17687 cfg.role = this.role;
17690 if(this.aria_valuenow){
17691 cfg['aria-valuenow'] = this.aria_valuenow;
17694 if(this.aria_valuemin){
17695 cfg['aria-valuemin'] = this.aria_valuemin;
17698 if(this.aria_valuemax){
17699 cfg['aria-valuemax'] = this.aria_valuemax;
17702 if(this.label && !this.sr_only){
17703 cfg.html = this.label;
17707 cfg.cls += ' progress-bar-' + this.panel;
17713 update : function(aria_valuenow)
17715 this.aria_valuenow = aria_valuenow;
17717 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17732 * @class Roo.bootstrap.TabGroup
17733 * @extends Roo.bootstrap.Column
17734 * Bootstrap Column class
17735 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17736 * @cfg {Boolean} carousel true to make the group behave like a carousel
17737 * @cfg {Boolean} bullets show bullets for the panels
17738 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17739 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17740 * @cfg {Boolean} showarrow (true|false) show arrow default true
17743 * Create a new TabGroup
17744 * @param {Object} config The config object
17747 Roo.bootstrap.TabGroup = function(config){
17748 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17750 this.navId = Roo.id();
17753 Roo.bootstrap.TabGroup.register(this);
17757 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17760 transition : false,
17765 slideOnTouch : false,
17768 getAutoCreate : function()
17770 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17772 cfg.cls += ' tab-content';
17774 if (this.carousel) {
17775 cfg.cls += ' carousel slide';
17778 cls : 'carousel-inner',
17782 if(this.bullets && !Roo.isTouch){
17785 cls : 'carousel-bullets',
17789 if(this.bullets_cls){
17790 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17797 cfg.cn[0].cn.push(bullets);
17800 if(this.showarrow){
17801 cfg.cn[0].cn.push({
17803 class : 'carousel-arrow',
17807 class : 'carousel-prev',
17811 class : 'fa fa-chevron-left'
17817 class : 'carousel-next',
17821 class : 'fa fa-chevron-right'
17834 initEvents: function()
17836 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17837 // this.el.on("touchstart", this.onTouchStart, this);
17840 if(this.autoslide){
17843 this.slideFn = window.setInterval(function() {
17844 _this.showPanelNext();
17848 if(this.showarrow){
17849 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17850 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17856 // onTouchStart : function(e, el, o)
17858 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17862 // this.showPanelNext();
17866 getChildContainer : function()
17868 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17872 * register a Navigation item
17873 * @param {Roo.bootstrap.NavItem} the navitem to add
17875 register : function(item)
17877 this.tabs.push( item);
17878 item.navId = this.navId; // not really needed..
17883 getActivePanel : function()
17886 Roo.each(this.tabs, function(t) {
17896 getPanelByName : function(n)
17899 Roo.each(this.tabs, function(t) {
17900 if (t.tabId == n) {
17908 indexOfPanel : function(p)
17911 Roo.each(this.tabs, function(t,i) {
17912 if (t.tabId == p.tabId) {
17921 * show a specific panel
17922 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17923 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17925 showPanel : function (pan)
17927 if(this.transition || typeof(pan) == 'undefined'){
17928 Roo.log("waiting for the transitionend");
17932 if (typeof(pan) == 'number') {
17933 pan = this.tabs[pan];
17936 if (typeof(pan) == 'string') {
17937 pan = this.getPanelByName(pan);
17940 var cur = this.getActivePanel();
17943 Roo.log('pan or acitve pan is undefined');
17947 if (pan.tabId == this.getActivePanel().tabId) {
17951 if (false === cur.fireEvent('beforedeactivate')) {
17955 if(this.bullets > 0 && !Roo.isTouch){
17956 this.setActiveBullet(this.indexOfPanel(pan));
17959 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17961 this.transition = true;
17962 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17963 var lr = dir == 'next' ? 'left' : 'right';
17964 pan.el.addClass(dir); // or prev
17965 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17966 cur.el.addClass(lr); // or right
17967 pan.el.addClass(lr);
17970 cur.el.on('transitionend', function() {
17971 Roo.log("trans end?");
17973 pan.el.removeClass([lr,dir]);
17974 pan.setActive(true);
17976 cur.el.removeClass([lr]);
17977 cur.setActive(false);
17979 _this.transition = false;
17981 }, this, { single: true } );
17986 cur.setActive(false);
17987 pan.setActive(true);
17992 showPanelNext : function()
17994 var i = this.indexOfPanel(this.getActivePanel());
17996 if (i >= this.tabs.length - 1 && !this.autoslide) {
18000 if (i >= this.tabs.length - 1 && this.autoslide) {
18004 this.showPanel(this.tabs[i+1]);
18007 showPanelPrev : function()
18009 var i = this.indexOfPanel(this.getActivePanel());
18011 if (i < 1 && !this.autoslide) {
18015 if (i < 1 && this.autoslide) {
18016 i = this.tabs.length;
18019 this.showPanel(this.tabs[i-1]);
18023 addBullet: function()
18025 if(!this.bullets || Roo.isTouch){
18028 var ctr = this.el.select('.carousel-bullets',true).first();
18029 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18030 var bullet = ctr.createChild({
18031 cls : 'bullet bullet-' + i
18032 },ctr.dom.lastChild);
18037 bullet.on('click', (function(e, el, o, ii, t){
18039 e.preventDefault();
18041 this.showPanel(ii);
18043 if(this.autoslide && this.slideFn){
18044 clearInterval(this.slideFn);
18045 this.slideFn = window.setInterval(function() {
18046 _this.showPanelNext();
18050 }).createDelegate(this, [i, bullet], true));
18055 setActiveBullet : function(i)
18061 Roo.each(this.el.select('.bullet', true).elements, function(el){
18062 el.removeClass('selected');
18065 var bullet = this.el.select('.bullet-' + i, true).first();
18071 bullet.addClass('selected');
18082 Roo.apply(Roo.bootstrap.TabGroup, {
18086 * register a Navigation Group
18087 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18089 register : function(navgrp)
18091 this.groups[navgrp.navId] = navgrp;
18095 * fetch a Navigation Group based on the navigation ID
18096 * if one does not exist , it will get created.
18097 * @param {string} the navgroup to add
18098 * @returns {Roo.bootstrap.NavGroup} the navgroup
18100 get: function(navId) {
18101 if (typeof(this.groups[navId]) == 'undefined') {
18102 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18104 return this.groups[navId] ;
18119 * @class Roo.bootstrap.TabPanel
18120 * @extends Roo.bootstrap.Component
18121 * Bootstrap TabPanel class
18122 * @cfg {Boolean} active panel active
18123 * @cfg {String} html panel content
18124 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18125 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18126 * @cfg {String} href click to link..
18130 * Create a new TabPanel
18131 * @param {Object} config The config object
18134 Roo.bootstrap.TabPanel = function(config){
18135 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18139 * Fires when the active status changes
18140 * @param {Roo.bootstrap.TabPanel} this
18141 * @param {Boolean} state the new state
18146 * @event beforedeactivate
18147 * Fires before a tab is de-activated - can be used to do validation on a form.
18148 * @param {Roo.bootstrap.TabPanel} this
18149 * @return {Boolean} false if there is an error
18152 'beforedeactivate': true
18155 this.tabId = this.tabId || Roo.id();
18159 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18167 getAutoCreate : function(){
18170 // item is needed for carousel - not sure if it has any effect otherwise
18171 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18172 html: this.html || ''
18176 cfg.cls += ' active';
18180 cfg.tabId = this.tabId;
18187 initEvents: function()
18189 var p = this.parent();
18191 this.navId = this.navId || p.navId;
18193 if (typeof(this.navId) != 'undefined') {
18194 // not really needed.. but just in case.. parent should be a NavGroup.
18195 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18199 var i = tg.tabs.length - 1;
18201 if(this.active && tg.bullets > 0 && i < tg.bullets){
18202 tg.setActiveBullet(i);
18206 this.el.on('click', this.onClick, this);
18209 this.el.on("touchstart", this.onTouchStart, this);
18210 this.el.on("touchmove", this.onTouchMove, this);
18211 this.el.on("touchend", this.onTouchEnd, this);
18216 onRender : function(ct, position)
18218 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18221 setActive : function(state)
18223 Roo.log("panel - set active " + this.tabId + "=" + state);
18225 this.active = state;
18227 this.el.removeClass('active');
18229 } else if (!this.el.hasClass('active')) {
18230 this.el.addClass('active');
18233 this.fireEvent('changed', this, state);
18236 onClick : function(e)
18238 e.preventDefault();
18240 if(!this.href.length){
18244 window.location.href = this.href;
18253 onTouchStart : function(e)
18255 this.swiping = false;
18257 this.startX = e.browserEvent.touches[0].clientX;
18258 this.startY = e.browserEvent.touches[0].clientY;
18261 onTouchMove : function(e)
18263 this.swiping = true;
18265 this.endX = e.browserEvent.touches[0].clientX;
18266 this.endY = e.browserEvent.touches[0].clientY;
18269 onTouchEnd : function(e)
18276 var tabGroup = this.parent();
18278 if(this.endX > this.startX){ // swiping right
18279 tabGroup.showPanelPrev();
18283 if(this.startX > this.endX){ // swiping left
18284 tabGroup.showPanelNext();
18303 * @class Roo.bootstrap.DateField
18304 * @extends Roo.bootstrap.Input
18305 * Bootstrap DateField class
18306 * @cfg {Number} weekStart default 0
18307 * @cfg {String} viewMode default empty, (months|years)
18308 * @cfg {String} minViewMode default empty, (months|years)
18309 * @cfg {Number} startDate default -Infinity
18310 * @cfg {Number} endDate default Infinity
18311 * @cfg {Boolean} todayHighlight default false
18312 * @cfg {Boolean} todayBtn default false
18313 * @cfg {Boolean} calendarWeeks default false
18314 * @cfg {Object} daysOfWeekDisabled default empty
18315 * @cfg {Boolean} singleMode default false (true | false)
18317 * @cfg {Boolean} keyboardNavigation default true
18318 * @cfg {String} language default en
18321 * Create a new DateField
18322 * @param {Object} config The config object
18325 Roo.bootstrap.DateField = function(config){
18326 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18330 * Fires when this field show.
18331 * @param {Roo.bootstrap.DateField} this
18332 * @param {Mixed} date The date value
18337 * Fires when this field hide.
18338 * @param {Roo.bootstrap.DateField} this
18339 * @param {Mixed} date The date value
18344 * Fires when select a date.
18345 * @param {Roo.bootstrap.DateField} this
18346 * @param {Mixed} date The date value
18350 * @event beforeselect
18351 * Fires when before select a date.
18352 * @param {Roo.bootstrap.DateField} this
18353 * @param {Mixed} date The date value
18355 beforeselect : true
18359 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18362 * @cfg {String} format
18363 * The default date format string which can be overriden for localization support. The format must be
18364 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18368 * @cfg {String} altFormats
18369 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18370 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18372 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18380 todayHighlight : false,
18386 keyboardNavigation: true,
18388 calendarWeeks: false,
18390 startDate: -Infinity,
18394 daysOfWeekDisabled: [],
18398 singleMode : false,
18400 UTCDate: function()
18402 return new Date(Date.UTC.apply(Date, arguments));
18405 UTCToday: function()
18407 var today = new Date();
18408 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18411 getDate: function() {
18412 var d = this.getUTCDate();
18413 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18416 getUTCDate: function() {
18420 setDate: function(d) {
18421 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18424 setUTCDate: function(d) {
18426 this.setValue(this.formatDate(this.date));
18429 onRender: function(ct, position)
18432 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18434 this.language = this.language || 'en';
18435 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18436 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18438 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18439 this.format = this.format || 'm/d/y';
18440 this.isInline = false;
18441 this.isInput = true;
18442 this.component = this.el.select('.add-on', true).first() || false;
18443 this.component = (this.component && this.component.length === 0) ? false : this.component;
18444 this.hasInput = this.component && this.inputEl().length;
18446 if (typeof(this.minViewMode === 'string')) {
18447 switch (this.minViewMode) {
18449 this.minViewMode = 1;
18452 this.minViewMode = 2;
18455 this.minViewMode = 0;
18460 if (typeof(this.viewMode === 'string')) {
18461 switch (this.viewMode) {
18474 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18476 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18478 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18480 this.picker().on('mousedown', this.onMousedown, this);
18481 this.picker().on('click', this.onClick, this);
18483 this.picker().addClass('datepicker-dropdown');
18485 this.startViewMode = this.viewMode;
18487 if(this.singleMode){
18488 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18489 v.setVisibilityMode(Roo.Element.DISPLAY);
18493 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18494 v.setStyle('width', '189px');
18498 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18499 if(!this.calendarWeeks){
18504 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18505 v.attr('colspan', function(i, val){
18506 return parseInt(val) + 1;
18511 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18513 this.setStartDate(this.startDate);
18514 this.setEndDate(this.endDate);
18516 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18523 if(this.isInline) {
18528 picker : function()
18530 return this.pickerEl;
18531 // return this.el.select('.datepicker', true).first();
18534 fillDow: function()
18536 var dowCnt = this.weekStart;
18545 if(this.calendarWeeks){
18553 while (dowCnt < this.weekStart + 7) {
18557 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18561 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18564 fillMonths: function()
18567 var months = this.picker().select('>.datepicker-months td', true).first();
18569 months.dom.innerHTML = '';
18575 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18578 months.createChild(month);
18585 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;
18587 if (this.date < this.startDate) {
18588 this.viewDate = new Date(this.startDate);
18589 } else if (this.date > this.endDate) {
18590 this.viewDate = new Date(this.endDate);
18592 this.viewDate = new Date(this.date);
18600 var d = new Date(this.viewDate),
18601 year = d.getUTCFullYear(),
18602 month = d.getUTCMonth(),
18603 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18604 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18605 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18606 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18607 currentDate = this.date && this.date.valueOf(),
18608 today = this.UTCToday();
18610 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18612 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18614 // this.picker.select('>tfoot th.today').
18615 // .text(dates[this.language].today)
18616 // .toggle(this.todayBtn !== false);
18618 this.updateNavArrows();
18621 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18623 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18625 prevMonth.setUTCDate(day);
18627 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18629 var nextMonth = new Date(prevMonth);
18631 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18633 nextMonth = nextMonth.valueOf();
18635 var fillMonths = false;
18637 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18639 while(prevMonth.valueOf() <= nextMonth) {
18642 if (prevMonth.getUTCDay() === this.weekStart) {
18644 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18652 if(this.calendarWeeks){
18653 // ISO 8601: First week contains first thursday.
18654 // ISO also states week starts on Monday, but we can be more abstract here.
18656 // Start of current week: based on weekstart/current date
18657 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18658 // Thursday of this week
18659 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18660 // First Thursday of year, year from thursday
18661 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18662 // Calendar week: ms between thursdays, div ms per day, div 7 days
18663 calWeek = (th - yth) / 864e5 / 7 + 1;
18665 fillMonths.cn.push({
18673 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18675 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18678 if (this.todayHighlight &&
18679 prevMonth.getUTCFullYear() == today.getFullYear() &&
18680 prevMonth.getUTCMonth() == today.getMonth() &&
18681 prevMonth.getUTCDate() == today.getDate()) {
18682 clsName += ' today';
18685 if (currentDate && prevMonth.valueOf() === currentDate) {
18686 clsName += ' active';
18689 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18690 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18691 clsName += ' disabled';
18694 fillMonths.cn.push({
18696 cls: 'day ' + clsName,
18697 html: prevMonth.getDate()
18700 prevMonth.setDate(prevMonth.getDate()+1);
18703 var currentYear = this.date && this.date.getUTCFullYear();
18704 var currentMonth = this.date && this.date.getUTCMonth();
18706 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18708 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18709 v.removeClass('active');
18711 if(currentYear === year && k === currentMonth){
18712 v.addClass('active');
18715 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18716 v.addClass('disabled');
18722 year = parseInt(year/10, 10) * 10;
18724 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18726 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18729 for (var i = -1; i < 11; i++) {
18730 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18732 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18740 showMode: function(dir)
18743 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18746 Roo.each(this.picker().select('>div',true).elements, function(v){
18747 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18750 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18755 if(this.isInline) {
18759 this.picker().removeClass(['bottom', 'top']);
18761 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18763 * place to the top of element!
18767 this.picker().addClass('top');
18768 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18773 this.picker().addClass('bottom');
18775 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18778 parseDate : function(value)
18780 if(!value || value instanceof Date){
18783 var v = Date.parseDate(value, this.format);
18784 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18785 v = Date.parseDate(value, 'Y-m-d');
18787 if(!v && this.altFormats){
18788 if(!this.altFormatsArray){
18789 this.altFormatsArray = this.altFormats.split("|");
18791 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18792 v = Date.parseDate(value, this.altFormatsArray[i]);
18798 formatDate : function(date, fmt)
18800 return (!date || !(date instanceof Date)) ?
18801 date : date.dateFormat(fmt || this.format);
18804 onFocus : function()
18806 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18810 onBlur : function()
18812 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18814 var d = this.inputEl().getValue();
18823 this.picker().show();
18827 this.fireEvent('show', this, this.date);
18832 if(this.isInline) {
18835 this.picker().hide();
18836 this.viewMode = this.startViewMode;
18839 this.fireEvent('hide', this, this.date);
18843 onMousedown: function(e)
18845 e.stopPropagation();
18846 e.preventDefault();
18851 Roo.bootstrap.DateField.superclass.keyup.call(this);
18855 setValue: function(v)
18857 if(this.fireEvent('beforeselect', this, v) !== false){
18858 var d = new Date(this.parseDate(v) ).clearTime();
18860 if(isNaN(d.getTime())){
18861 this.date = this.viewDate = '';
18862 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18866 v = this.formatDate(d);
18868 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18870 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18874 this.fireEvent('select', this, this.date);
18878 getValue: function()
18880 return this.formatDate(this.date);
18883 fireKey: function(e)
18885 if (!this.picker().isVisible()){
18886 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18892 var dateChanged = false,
18894 newDate, newViewDate;
18899 e.preventDefault();
18903 if (!this.keyboardNavigation) {
18906 dir = e.keyCode == 37 ? -1 : 1;
18909 newDate = this.moveYear(this.date, dir);
18910 newViewDate = this.moveYear(this.viewDate, dir);
18911 } else if (e.shiftKey){
18912 newDate = this.moveMonth(this.date, dir);
18913 newViewDate = this.moveMonth(this.viewDate, dir);
18915 newDate = new Date(this.date);
18916 newDate.setUTCDate(this.date.getUTCDate() + dir);
18917 newViewDate = new Date(this.viewDate);
18918 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18920 if (this.dateWithinRange(newDate)){
18921 this.date = newDate;
18922 this.viewDate = newViewDate;
18923 this.setValue(this.formatDate(this.date));
18925 e.preventDefault();
18926 dateChanged = true;
18931 if (!this.keyboardNavigation) {
18934 dir = e.keyCode == 38 ? -1 : 1;
18936 newDate = this.moveYear(this.date, dir);
18937 newViewDate = this.moveYear(this.viewDate, dir);
18938 } else if (e.shiftKey){
18939 newDate = this.moveMonth(this.date, dir);
18940 newViewDate = this.moveMonth(this.viewDate, dir);
18942 newDate = new Date(this.date);
18943 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18944 newViewDate = new Date(this.viewDate);
18945 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18947 if (this.dateWithinRange(newDate)){
18948 this.date = newDate;
18949 this.viewDate = newViewDate;
18950 this.setValue(this.formatDate(this.date));
18952 e.preventDefault();
18953 dateChanged = true;
18957 this.setValue(this.formatDate(this.date));
18959 e.preventDefault();
18962 this.setValue(this.formatDate(this.date));
18976 onClick: function(e)
18978 e.stopPropagation();
18979 e.preventDefault();
18981 var target = e.getTarget();
18983 if(target.nodeName.toLowerCase() === 'i'){
18984 target = Roo.get(target).dom.parentNode;
18987 var nodeName = target.nodeName;
18988 var className = target.className;
18989 var html = target.innerHTML;
18990 //Roo.log(nodeName);
18992 switch(nodeName.toLowerCase()) {
18994 switch(className) {
19000 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19001 switch(this.viewMode){
19003 this.viewDate = this.moveMonth(this.viewDate, dir);
19007 this.viewDate = this.moveYear(this.viewDate, dir);
19013 var date = new Date();
19014 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19016 this.setValue(this.formatDate(this.date));
19023 if (className.indexOf('disabled') < 0) {
19024 this.viewDate.setUTCDate(1);
19025 if (className.indexOf('month') > -1) {
19026 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19028 var year = parseInt(html, 10) || 0;
19029 this.viewDate.setUTCFullYear(year);
19033 if(this.singleMode){
19034 this.setValue(this.formatDate(this.viewDate));
19045 //Roo.log(className);
19046 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19047 var day = parseInt(html, 10) || 1;
19048 var year = this.viewDate.getUTCFullYear(),
19049 month = this.viewDate.getUTCMonth();
19051 if (className.indexOf('old') > -1) {
19058 } else if (className.indexOf('new') > -1) {
19066 //Roo.log([year,month,day]);
19067 this.date = this.UTCDate(year, month, day,0,0,0,0);
19068 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19070 //Roo.log(this.formatDate(this.date));
19071 this.setValue(this.formatDate(this.date));
19078 setStartDate: function(startDate)
19080 this.startDate = startDate || -Infinity;
19081 if (this.startDate !== -Infinity) {
19082 this.startDate = this.parseDate(this.startDate);
19085 this.updateNavArrows();
19088 setEndDate: function(endDate)
19090 this.endDate = endDate || Infinity;
19091 if (this.endDate !== Infinity) {
19092 this.endDate = this.parseDate(this.endDate);
19095 this.updateNavArrows();
19098 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19100 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19101 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19102 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19104 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19105 return parseInt(d, 10);
19108 this.updateNavArrows();
19111 updateNavArrows: function()
19113 if(this.singleMode){
19117 var d = new Date(this.viewDate),
19118 year = d.getUTCFullYear(),
19119 month = d.getUTCMonth();
19121 Roo.each(this.picker().select('.prev', true).elements, function(v){
19123 switch (this.viewMode) {
19126 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19132 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19139 Roo.each(this.picker().select('.next', true).elements, function(v){
19141 switch (this.viewMode) {
19144 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19150 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19158 moveMonth: function(date, dir)
19163 var new_date = new Date(date.valueOf()),
19164 day = new_date.getUTCDate(),
19165 month = new_date.getUTCMonth(),
19166 mag = Math.abs(dir),
19168 dir = dir > 0 ? 1 : -1;
19171 // If going back one month, make sure month is not current month
19172 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19174 return new_date.getUTCMonth() == month;
19176 // If going forward one month, make sure month is as expected
19177 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19179 return new_date.getUTCMonth() != new_month;
19181 new_month = month + dir;
19182 new_date.setUTCMonth(new_month);
19183 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19184 if (new_month < 0 || new_month > 11) {
19185 new_month = (new_month + 12) % 12;
19188 // For magnitudes >1, move one month at a time...
19189 for (var i=0; i<mag; i++) {
19190 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19191 new_date = this.moveMonth(new_date, dir);
19193 // ...then reset the day, keeping it in the new month
19194 new_month = new_date.getUTCMonth();
19195 new_date.setUTCDate(day);
19197 return new_month != new_date.getUTCMonth();
19200 // Common date-resetting loop -- if date is beyond end of month, make it
19203 new_date.setUTCDate(--day);
19204 new_date.setUTCMonth(new_month);
19209 moveYear: function(date, dir)
19211 return this.moveMonth(date, dir*12);
19214 dateWithinRange: function(date)
19216 return date >= this.startDate && date <= this.endDate;
19222 this.picker().remove();
19225 validateValue : function(value)
19227 if(this.getVisibilityEl().hasClass('hidden')){
19231 if(value.length < 1) {
19232 if(this.allowBlank){
19238 if(value.length < this.minLength){
19241 if(value.length > this.maxLength){
19245 var vt = Roo.form.VTypes;
19246 if(!vt[this.vtype](value, this)){
19250 if(typeof this.validator == "function"){
19251 var msg = this.validator(value);
19257 if(this.regex && !this.regex.test(value)){
19261 if(typeof(this.parseDate(value)) == 'undefined'){
19265 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19269 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19279 this.date = this.viewDate = '';
19281 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19284 setVisible : function(visible)
19290 this.getEl().removeClass('hidden');
19296 this.getEl().addClass('hidden');
19301 Roo.apply(Roo.bootstrap.DateField, {
19312 html: '<i class="fa fa-arrow-left"/>'
19322 html: '<i class="fa fa-arrow-right"/>'
19364 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19365 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19366 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19367 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19368 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19381 navFnc: 'FullYear',
19386 navFnc: 'FullYear',
19391 Roo.apply(Roo.bootstrap.DateField, {
19395 cls: 'datepicker dropdown-menu roo-dynamic',
19399 cls: 'datepicker-days',
19403 cls: 'table-condensed',
19405 Roo.bootstrap.DateField.head,
19409 Roo.bootstrap.DateField.footer
19416 cls: 'datepicker-months',
19420 cls: 'table-condensed',
19422 Roo.bootstrap.DateField.head,
19423 Roo.bootstrap.DateField.content,
19424 Roo.bootstrap.DateField.footer
19431 cls: 'datepicker-years',
19435 cls: 'table-condensed',
19437 Roo.bootstrap.DateField.head,
19438 Roo.bootstrap.DateField.content,
19439 Roo.bootstrap.DateField.footer
19458 * @class Roo.bootstrap.TimeField
19459 * @extends Roo.bootstrap.Input
19460 * Bootstrap DateField class
19464 * Create a new TimeField
19465 * @param {Object} config The config object
19468 Roo.bootstrap.TimeField = function(config){
19469 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19473 * Fires when this field show.
19474 * @param {Roo.bootstrap.DateField} thisthis
19475 * @param {Mixed} date The date value
19480 * Fires when this field hide.
19481 * @param {Roo.bootstrap.DateField} this
19482 * @param {Mixed} date The date value
19487 * Fires when select a date.
19488 * @param {Roo.bootstrap.DateField} this
19489 * @param {Mixed} date The date value
19495 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19498 * @cfg {String} format
19499 * The default time format string which can be overriden for localization support. The format must be
19500 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19504 onRender: function(ct, position)
19507 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19509 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19511 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19513 this.pop = this.picker().select('>.datepicker-time',true).first();
19514 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19516 this.picker().on('mousedown', this.onMousedown, this);
19517 this.picker().on('click', this.onClick, this);
19519 this.picker().addClass('datepicker-dropdown');
19524 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19525 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19526 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19527 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19528 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19529 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19533 fireKey: function(e){
19534 if (!this.picker().isVisible()){
19535 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19541 e.preventDefault();
19549 this.onTogglePeriod();
19552 this.onIncrementMinutes();
19555 this.onDecrementMinutes();
19564 onClick: function(e) {
19565 e.stopPropagation();
19566 e.preventDefault();
19569 picker : function()
19571 return this.el.select('.datepicker', true).first();
19574 fillTime: function()
19576 var time = this.pop.select('tbody', true).first();
19578 time.dom.innerHTML = '';
19593 cls: 'hours-up glyphicon glyphicon-chevron-up'
19613 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19634 cls: 'timepicker-hour',
19649 cls: 'timepicker-minute',
19664 cls: 'btn btn-primary period',
19686 cls: 'hours-down glyphicon glyphicon-chevron-down'
19706 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19724 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19731 var hours = this.time.getHours();
19732 var minutes = this.time.getMinutes();
19745 hours = hours - 12;
19749 hours = '0' + hours;
19753 minutes = '0' + minutes;
19756 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19757 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19758 this.pop.select('button', true).first().dom.innerHTML = period;
19764 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19766 var cls = ['bottom'];
19768 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19775 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19780 this.picker().addClass(cls.join('-'));
19784 Roo.each(cls, function(c){
19786 _this.picker().setTop(_this.inputEl().getHeight());
19790 _this.picker().setTop(0 - _this.picker().getHeight());
19795 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19799 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19806 onFocus : function()
19808 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19812 onBlur : function()
19814 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19820 this.picker().show();
19825 this.fireEvent('show', this, this.date);
19830 this.picker().hide();
19833 this.fireEvent('hide', this, this.date);
19836 setTime : function()
19839 this.setValue(this.time.format(this.format));
19841 this.fireEvent('select', this, this.date);
19846 onMousedown: function(e){
19847 e.stopPropagation();
19848 e.preventDefault();
19851 onIncrementHours: function()
19853 Roo.log('onIncrementHours');
19854 this.time = this.time.add(Date.HOUR, 1);
19859 onDecrementHours: function()
19861 Roo.log('onDecrementHours');
19862 this.time = this.time.add(Date.HOUR, -1);
19866 onIncrementMinutes: function()
19868 Roo.log('onIncrementMinutes');
19869 this.time = this.time.add(Date.MINUTE, 1);
19873 onDecrementMinutes: function()
19875 Roo.log('onDecrementMinutes');
19876 this.time = this.time.add(Date.MINUTE, -1);
19880 onTogglePeriod: function()
19882 Roo.log('onTogglePeriod');
19883 this.time = this.time.add(Date.HOUR, 12);
19890 Roo.apply(Roo.bootstrap.TimeField, {
19920 cls: 'btn btn-info ok',
19932 Roo.apply(Roo.bootstrap.TimeField, {
19936 cls: 'datepicker dropdown-menu',
19940 cls: 'datepicker-time',
19944 cls: 'table-condensed',
19946 Roo.bootstrap.TimeField.content,
19947 Roo.bootstrap.TimeField.footer
19966 * @class Roo.bootstrap.MonthField
19967 * @extends Roo.bootstrap.Input
19968 * Bootstrap MonthField class
19970 * @cfg {String} language default en
19973 * Create a new MonthField
19974 * @param {Object} config The config object
19977 Roo.bootstrap.MonthField = function(config){
19978 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19983 * Fires when this field show.
19984 * @param {Roo.bootstrap.MonthField} this
19985 * @param {Mixed} date The date value
19990 * Fires when this field hide.
19991 * @param {Roo.bootstrap.MonthField} this
19992 * @param {Mixed} date The date value
19997 * Fires when select a date.
19998 * @param {Roo.bootstrap.MonthField} this
19999 * @param {String} oldvalue The old value
20000 * @param {String} newvalue The new value
20006 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20008 onRender: function(ct, position)
20011 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20013 this.language = this.language || 'en';
20014 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20015 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20017 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20018 this.isInline = false;
20019 this.isInput = true;
20020 this.component = this.el.select('.add-on', true).first() || false;
20021 this.component = (this.component && this.component.length === 0) ? false : this.component;
20022 this.hasInput = this.component && this.inputEL().length;
20024 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20026 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20028 this.picker().on('mousedown', this.onMousedown, this);
20029 this.picker().on('click', this.onClick, this);
20031 this.picker().addClass('datepicker-dropdown');
20033 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20034 v.setStyle('width', '189px');
20041 if(this.isInline) {
20047 setValue: function(v, suppressEvent)
20049 var o = this.getValue();
20051 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20055 if(suppressEvent !== true){
20056 this.fireEvent('select', this, o, v);
20061 getValue: function()
20066 onClick: function(e)
20068 e.stopPropagation();
20069 e.preventDefault();
20071 var target = e.getTarget();
20073 if(target.nodeName.toLowerCase() === 'i'){
20074 target = Roo.get(target).dom.parentNode;
20077 var nodeName = target.nodeName;
20078 var className = target.className;
20079 var html = target.innerHTML;
20081 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20085 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20087 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20093 picker : function()
20095 return this.pickerEl;
20098 fillMonths: function()
20101 var months = this.picker().select('>.datepicker-months td', true).first();
20103 months.dom.innerHTML = '';
20109 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20112 months.createChild(month);
20121 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20122 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20125 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20126 e.removeClass('active');
20128 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20129 e.addClass('active');
20136 if(this.isInline) {
20140 this.picker().removeClass(['bottom', 'top']);
20142 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20144 * place to the top of element!
20148 this.picker().addClass('top');
20149 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20154 this.picker().addClass('bottom');
20156 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20159 onFocus : function()
20161 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20165 onBlur : function()
20167 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20169 var d = this.inputEl().getValue();
20178 this.picker().show();
20179 this.picker().select('>.datepicker-months', true).first().show();
20183 this.fireEvent('show', this, this.date);
20188 if(this.isInline) {
20191 this.picker().hide();
20192 this.fireEvent('hide', this, this.date);
20196 onMousedown: function(e)
20198 e.stopPropagation();
20199 e.preventDefault();
20204 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20208 fireKey: function(e)
20210 if (!this.picker().isVisible()){
20211 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20222 e.preventDefault();
20226 dir = e.keyCode == 37 ? -1 : 1;
20228 this.vIndex = this.vIndex + dir;
20230 if(this.vIndex < 0){
20234 if(this.vIndex > 11){
20238 if(isNaN(this.vIndex)){
20242 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20248 dir = e.keyCode == 38 ? -1 : 1;
20250 this.vIndex = this.vIndex + dir * 4;
20252 if(this.vIndex < 0){
20256 if(this.vIndex > 11){
20260 if(isNaN(this.vIndex)){
20264 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20269 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20270 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20274 e.preventDefault();
20277 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20278 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20294 this.picker().remove();
20299 Roo.apply(Roo.bootstrap.MonthField, {
20318 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20319 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20324 Roo.apply(Roo.bootstrap.MonthField, {
20328 cls: 'datepicker dropdown-menu roo-dynamic',
20332 cls: 'datepicker-months',
20336 cls: 'table-condensed',
20338 Roo.bootstrap.DateField.content
20358 * @class Roo.bootstrap.CheckBox
20359 * @extends Roo.bootstrap.Input
20360 * Bootstrap CheckBox class
20362 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20363 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20364 * @cfg {String} boxLabel The text that appears beside the checkbox
20365 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20366 * @cfg {Boolean} checked initnal the element
20367 * @cfg {Boolean} inline inline the element (default false)
20368 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20369 * @cfg {String} tooltip label tooltip
20372 * Create a new CheckBox
20373 * @param {Object} config The config object
20376 Roo.bootstrap.CheckBox = function(config){
20377 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20382 * Fires when the element is checked or unchecked.
20383 * @param {Roo.bootstrap.CheckBox} this This input
20384 * @param {Boolean} checked The new checked value
20389 * Fires when the element is click.
20390 * @param {Roo.bootstrap.CheckBox} this This input
20397 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20399 inputType: 'checkbox',
20408 getAutoCreate : function()
20410 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20416 cfg.cls = 'form-group ' + this.inputType; //input-group
20419 cfg.cls += ' ' + this.inputType + '-inline';
20425 type : this.inputType,
20426 value : this.inputValue,
20427 cls : 'roo-' + this.inputType, //'form-box',
20428 placeholder : this.placeholder || ''
20432 if(this.inputType != 'radio'){
20436 cls : 'roo-hidden-value',
20437 value : this.checked ? this.inputValue : this.valueOff
20442 if (this.weight) { // Validity check?
20443 cfg.cls += " " + this.inputType + "-" + this.weight;
20446 if (this.disabled) {
20447 input.disabled=true;
20451 input.checked = this.checked;
20456 input.name = this.name;
20458 if(this.inputType != 'radio'){
20459 hidden.name = this.name;
20460 input.name = '_hidden_' + this.name;
20465 input.cls += ' input-' + this.size;
20470 ['xs','sm','md','lg'].map(function(size){
20471 if (settings[size]) {
20472 cfg.cls += ' col-' + size + '-' + settings[size];
20476 var inputblock = input;
20478 if (this.before || this.after) {
20481 cls : 'input-group',
20486 inputblock.cn.push({
20488 cls : 'input-group-addon',
20493 inputblock.cn.push(input);
20495 if(this.inputType != 'radio'){
20496 inputblock.cn.push(hidden);
20500 inputblock.cn.push({
20502 cls : 'input-group-addon',
20509 if (align ==='left' && this.fieldLabel.length) {
20510 // Roo.log("left and has label");
20515 cls : 'control-label',
20516 html : this.fieldLabel
20526 if(this.labelWidth > 12){
20527 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20530 if(this.labelWidth < 13 && this.labelmd == 0){
20531 this.labelmd = this.labelWidth;
20534 if(this.labellg > 0){
20535 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20536 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20539 if(this.labelmd > 0){
20540 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20541 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20544 if(this.labelsm > 0){
20545 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20546 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20549 if(this.labelxs > 0){
20550 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20551 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20554 } else if ( this.fieldLabel.length) {
20555 // Roo.log(" label");
20559 tag: this.boxLabel ? 'span' : 'label',
20561 cls: 'control-label box-input-label',
20562 //cls : 'input-group-addon',
20563 html : this.fieldLabel
20572 // Roo.log(" no label && no align");
20573 cfg.cn = [ inputblock ] ;
20579 var boxLabelCfg = {
20581 //'for': id, // box label is handled by onclick - so no for...
20583 html: this.boxLabel
20587 boxLabelCfg.tooltip = this.tooltip;
20590 cfg.cn.push(boxLabelCfg);
20593 if(this.inputType != 'radio'){
20594 cfg.cn.push(hidden);
20602 * return the real input element.
20604 inputEl: function ()
20606 return this.el.select('input.roo-' + this.inputType,true).first();
20608 hiddenEl: function ()
20610 return this.el.select('input.roo-hidden-value',true).first();
20613 labelEl: function()
20615 return this.el.select('label.control-label',true).first();
20617 /* depricated... */
20621 return this.labelEl();
20624 boxLabelEl: function()
20626 return this.el.select('label.box-label',true).first();
20629 initEvents : function()
20631 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20633 this.inputEl().on('click', this.onClick, this);
20635 if (this.boxLabel) {
20636 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20639 this.startValue = this.getValue();
20642 Roo.bootstrap.CheckBox.register(this);
20646 onClick : function(e)
20648 if(this.fireEvent('click', this, e) !== false){
20649 this.setChecked(!this.checked);
20654 setChecked : function(state,suppressEvent)
20656 this.startValue = this.getValue();
20658 if(this.inputType == 'radio'){
20660 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20661 e.dom.checked = false;
20664 this.inputEl().dom.checked = true;
20666 this.inputEl().dom.value = this.inputValue;
20668 if(suppressEvent !== true){
20669 this.fireEvent('check', this, true);
20677 this.checked = state;
20679 this.inputEl().dom.checked = state;
20682 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20684 if(suppressEvent !== true){
20685 this.fireEvent('check', this, state);
20691 getValue : function()
20693 if(this.inputType == 'radio'){
20694 return this.getGroupValue();
20697 return this.hiddenEl().dom.value;
20701 getGroupValue : function()
20703 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20707 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20710 setValue : function(v,suppressEvent)
20712 if(this.inputType == 'radio'){
20713 this.setGroupValue(v, suppressEvent);
20717 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20722 setGroupValue : function(v, suppressEvent)
20724 this.startValue = this.getValue();
20726 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20727 e.dom.checked = false;
20729 if(e.dom.value == v){
20730 e.dom.checked = true;
20734 if(suppressEvent !== true){
20735 this.fireEvent('check', this, true);
20743 validate : function()
20745 if(this.getVisibilityEl().hasClass('hidden')){
20751 (this.inputType == 'radio' && this.validateRadio()) ||
20752 (this.inputType == 'checkbox' && this.validateCheckbox())
20758 this.markInvalid();
20762 validateRadio : function()
20764 if(this.getVisibilityEl().hasClass('hidden')){
20768 if(this.allowBlank){
20774 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20775 if(!e.dom.checked){
20787 validateCheckbox : function()
20790 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20791 //return (this.getValue() == this.inputValue) ? true : false;
20794 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20802 for(var i in group){
20803 if(group[i].el.isVisible(true)){
20811 for(var i in group){
20816 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20823 * Mark this field as valid
20825 markValid : function()
20829 this.fireEvent('valid', this);
20831 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20834 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20841 if(this.inputType == 'radio'){
20842 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20843 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20844 e.findParent('.form-group', false, true).addClass(_this.validClass);
20851 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20852 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20856 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20862 for(var i in group){
20863 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20864 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20869 * Mark this field as invalid
20870 * @param {String} msg The validation message
20872 markInvalid : function(msg)
20874 if(this.allowBlank){
20880 this.fireEvent('invalid', this, msg);
20882 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20885 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20889 label.markInvalid();
20892 if(this.inputType == 'radio'){
20893 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20894 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20895 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20902 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20903 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20907 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20913 for(var i in group){
20914 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20915 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20920 clearInvalid : function()
20922 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20924 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20926 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20928 if (label && label.iconEl) {
20929 label.iconEl.removeClass(label.validClass);
20930 label.iconEl.removeClass(label.invalidClass);
20934 disable : function()
20936 if(this.inputType != 'radio'){
20937 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20944 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20945 _this.getActionEl().addClass(this.disabledClass);
20946 e.dom.disabled = true;
20950 this.disabled = true;
20951 this.fireEvent("disable", this);
20955 enable : function()
20957 if(this.inputType != 'radio'){
20958 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20965 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20966 _this.getActionEl().removeClass(this.disabledClass);
20967 e.dom.disabled = false;
20971 this.disabled = false;
20972 this.fireEvent("enable", this);
20976 setBoxLabel : function(v)
20981 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20987 Roo.apply(Roo.bootstrap.CheckBox, {
20992 * register a CheckBox Group
20993 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20995 register : function(checkbox)
20997 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20998 this.groups[checkbox.groupId] = {};
21001 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21005 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21009 * fetch a CheckBox Group based on the group ID
21010 * @param {string} the group ID
21011 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21013 get: function(groupId) {
21014 if (typeof(this.groups[groupId]) == 'undefined') {
21018 return this.groups[groupId] ;
21031 * @class Roo.bootstrap.Radio
21032 * @extends Roo.bootstrap.Component
21033 * Bootstrap Radio class
21034 * @cfg {String} boxLabel - the label associated
21035 * @cfg {String} value - the value of radio
21038 * Create a new Radio
21039 * @param {Object} config The config object
21041 Roo.bootstrap.Radio = function(config){
21042 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21046 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21052 getAutoCreate : function()
21056 cls : 'form-group radio',
21061 html : this.boxLabel
21069 initEvents : function()
21071 this.parent().register(this);
21073 this.el.on('click', this.onClick, this);
21077 onClick : function(e)
21079 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21080 this.setChecked(true);
21084 setChecked : function(state, suppressEvent)
21086 this.parent().setValue(this.value, suppressEvent);
21090 setBoxLabel : function(v)
21095 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21110 * @class Roo.bootstrap.SecurePass
21111 * @extends Roo.bootstrap.Input
21112 * Bootstrap SecurePass class
21116 * Create a new SecurePass
21117 * @param {Object} config The config object
21120 Roo.bootstrap.SecurePass = function (config) {
21121 // these go here, so the translation tool can replace them..
21123 PwdEmpty: "Please type a password, and then retype it to confirm.",
21124 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21125 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21126 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21127 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21128 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21129 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21130 TooWeak: "Your password is Too Weak."
21132 this.meterLabel = "Password strength:";
21133 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21134 this.meterClass = [
21135 "roo-password-meter-tooweak",
21136 "roo-password-meter-weak",
21137 "roo-password-meter-medium",
21138 "roo-password-meter-strong",
21139 "roo-password-meter-grey"
21144 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21147 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21149 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21151 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21152 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21153 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21154 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21155 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21156 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21157 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21167 * @cfg {String/Object} Label for the strength meter (defaults to
21168 * 'Password strength:')
21173 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21174 * ['Weak', 'Medium', 'Strong'])
21177 pwdStrengths: false,
21190 initEvents: function ()
21192 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21194 if (this.el.is('input[type=password]') && Roo.isSafari) {
21195 this.el.on('keydown', this.SafariOnKeyDown, this);
21198 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21201 onRender: function (ct, position)
21203 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21204 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21205 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21207 this.trigger.createChild({
21212 cls: 'roo-password-meter-grey col-xs-12',
21215 //width: this.meterWidth + 'px'
21219 cls: 'roo-password-meter-text'
21225 if (this.hideTrigger) {
21226 this.trigger.setDisplayed(false);
21228 this.setSize(this.width || '', this.height || '');
21231 onDestroy: function ()
21233 if (this.trigger) {
21234 this.trigger.removeAllListeners();
21235 this.trigger.remove();
21238 this.wrap.remove();
21240 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21243 checkStrength: function ()
21245 var pwd = this.inputEl().getValue();
21246 if (pwd == this._lastPwd) {
21251 if (this.ClientSideStrongPassword(pwd)) {
21253 } else if (this.ClientSideMediumPassword(pwd)) {
21255 } else if (this.ClientSideWeakPassword(pwd)) {
21261 Roo.log('strength1: ' + strength);
21263 //var pm = this.trigger.child('div/div/div').dom;
21264 var pm = this.trigger.child('div/div');
21265 pm.removeClass(this.meterClass);
21266 pm.addClass(this.meterClass[strength]);
21269 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21271 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21273 this._lastPwd = pwd;
21277 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21279 this._lastPwd = '';
21281 var pm = this.trigger.child('div/div');
21282 pm.removeClass(this.meterClass);
21283 pm.addClass('roo-password-meter-grey');
21286 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21289 this.inputEl().dom.type='password';
21292 validateValue: function (value)
21295 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21298 if (value.length == 0) {
21299 if (this.allowBlank) {
21300 this.clearInvalid();
21304 this.markInvalid(this.errors.PwdEmpty);
21305 this.errorMsg = this.errors.PwdEmpty;
21313 if ('[\x21-\x7e]*'.match(value)) {
21314 this.markInvalid(this.errors.PwdBadChar);
21315 this.errorMsg = this.errors.PwdBadChar;
21318 if (value.length < 6) {
21319 this.markInvalid(this.errors.PwdShort);
21320 this.errorMsg = this.errors.PwdShort;
21323 if (value.length > 16) {
21324 this.markInvalid(this.errors.PwdLong);
21325 this.errorMsg = this.errors.PwdLong;
21329 if (this.ClientSideStrongPassword(value)) {
21331 } else if (this.ClientSideMediumPassword(value)) {
21333 } else if (this.ClientSideWeakPassword(value)) {
21340 if (strength < 2) {
21341 //this.markInvalid(this.errors.TooWeak);
21342 this.errorMsg = this.errors.TooWeak;
21347 console.log('strength2: ' + strength);
21349 //var pm = this.trigger.child('div/div/div').dom;
21351 var pm = this.trigger.child('div/div');
21352 pm.removeClass(this.meterClass);
21353 pm.addClass(this.meterClass[strength]);
21355 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21357 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21359 this.errorMsg = '';
21363 CharacterSetChecks: function (type)
21366 this.fResult = false;
21369 isctype: function (character, type)
21372 case this.kCapitalLetter:
21373 if (character >= 'A' && character <= 'Z') {
21378 case this.kSmallLetter:
21379 if (character >= 'a' && character <= 'z') {
21385 if (character >= '0' && character <= '9') {
21390 case this.kPunctuation:
21391 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21402 IsLongEnough: function (pwd, size)
21404 return !(pwd == null || isNaN(size) || pwd.length < size);
21407 SpansEnoughCharacterSets: function (word, nb)
21409 if (!this.IsLongEnough(word, nb))
21414 var characterSetChecks = new Array(
21415 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21416 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21419 for (var index = 0; index < word.length; ++index) {
21420 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21421 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21422 characterSetChecks[nCharSet].fResult = true;
21429 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21430 if (characterSetChecks[nCharSet].fResult) {
21435 if (nCharSets < nb) {
21441 ClientSideStrongPassword: function (pwd)
21443 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21446 ClientSideMediumPassword: function (pwd)
21448 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21451 ClientSideWeakPassword: function (pwd)
21453 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21456 })//<script type="text/javascript">
21459 * Based Ext JS Library 1.1.1
21460 * Copyright(c) 2006-2007, Ext JS, LLC.
21466 * @class Roo.HtmlEditorCore
21467 * @extends Roo.Component
21468 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21470 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21473 Roo.HtmlEditorCore = function(config){
21476 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21481 * @event initialize
21482 * Fires when the editor is fully initialized (including the iframe)
21483 * @param {Roo.HtmlEditorCore} this
21488 * Fires when the editor is first receives the focus. Any insertion must wait
21489 * until after this event.
21490 * @param {Roo.HtmlEditorCore} this
21494 * @event beforesync
21495 * Fires before the textarea is updated with content from the editor iframe. Return false
21496 * to cancel the sync.
21497 * @param {Roo.HtmlEditorCore} this
21498 * @param {String} html
21502 * @event beforepush
21503 * Fires before the iframe editor is updated with content from the textarea. Return false
21504 * to cancel the push.
21505 * @param {Roo.HtmlEditorCore} this
21506 * @param {String} html
21511 * Fires when the textarea is updated with content from the editor iframe.
21512 * @param {Roo.HtmlEditorCore} this
21513 * @param {String} html
21518 * Fires when the iframe editor is updated with content from the textarea.
21519 * @param {Roo.HtmlEditorCore} this
21520 * @param {String} html
21525 * @event editorevent
21526 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21527 * @param {Roo.HtmlEditorCore} this
21533 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21535 // defaults : white / black...
21536 this.applyBlacklists();
21543 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21547 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21553 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21558 * @cfg {Number} height (in pixels)
21562 * @cfg {Number} width (in pixels)
21567 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21570 stylesheets: false,
21575 // private properties
21576 validationEvent : false,
21578 initialized : false,
21580 sourceEditMode : false,
21581 onFocus : Roo.emptyFn,
21583 hideMode:'offsets',
21587 // blacklist + whitelisted elements..
21594 * Protected method that will not generally be called directly. It
21595 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21596 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21598 getDocMarkup : function(){
21602 // inherit styels from page...??
21603 if (this.stylesheets === false) {
21605 Roo.get(document.head).select('style').each(function(node) {
21606 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21609 Roo.get(document.head).select('link').each(function(node) {
21610 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21613 } else if (!this.stylesheets.length) {
21615 st = '<style type="text/css">' +
21616 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21619 st = '<style type="text/css">' +
21624 st += '<style type="text/css">' +
21625 'IMG { cursor: pointer } ' +
21628 var cls = 'roo-htmleditor-body';
21630 if(this.bodyCls.length){
21631 cls += ' ' + this.bodyCls;
21634 return '<html><head>' + st +
21635 //<style type="text/css">' +
21636 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21638 ' </head><body class="' + cls + '"></body></html>';
21642 onRender : function(ct, position)
21645 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21646 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21649 this.el.dom.style.border = '0 none';
21650 this.el.dom.setAttribute('tabIndex', -1);
21651 this.el.addClass('x-hidden hide');
21655 if(Roo.isIE){ // fix IE 1px bogus margin
21656 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21660 this.frameId = Roo.id();
21664 var iframe = this.owner.wrap.createChild({
21666 cls: 'form-control', // bootstrap..
21668 name: this.frameId,
21669 frameBorder : 'no',
21670 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21675 this.iframe = iframe.dom;
21677 this.assignDocWin();
21679 this.doc.designMode = 'on';
21682 this.doc.write(this.getDocMarkup());
21686 var task = { // must defer to wait for browser to be ready
21688 //console.log("run task?" + this.doc.readyState);
21689 this.assignDocWin();
21690 if(this.doc.body || this.doc.readyState == 'complete'){
21692 this.doc.designMode="on";
21696 Roo.TaskMgr.stop(task);
21697 this.initEditor.defer(10, this);
21704 Roo.TaskMgr.start(task);
21709 onResize : function(w, h)
21711 Roo.log('resize: ' +w + ',' + h );
21712 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21716 if(typeof w == 'number'){
21718 this.iframe.style.width = w + 'px';
21720 if(typeof h == 'number'){
21722 this.iframe.style.height = h + 'px';
21724 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21731 * Toggles the editor between standard and source edit mode.
21732 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21734 toggleSourceEdit : function(sourceEditMode){
21736 this.sourceEditMode = sourceEditMode === true;
21738 if(this.sourceEditMode){
21740 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21743 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21744 //this.iframe.className = '';
21747 //this.setSize(this.owner.wrap.getSize());
21748 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21755 * Protected method that will not generally be called directly. If you need/want
21756 * custom HTML cleanup, this is the method you should override.
21757 * @param {String} html The HTML to be cleaned
21758 * return {String} The cleaned HTML
21760 cleanHtml : function(html){
21761 html = String(html);
21762 if(html.length > 5){
21763 if(Roo.isSafari){ // strip safari nonsense
21764 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21767 if(html == ' '){
21774 * HTML Editor -> Textarea
21775 * Protected method that will not generally be called directly. Syncs the contents
21776 * of the editor iframe with the textarea.
21778 syncValue : function(){
21779 if(this.initialized){
21780 var bd = (this.doc.body || this.doc.documentElement);
21781 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21782 var html = bd.innerHTML;
21784 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21785 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21787 html = '<div style="'+m[0]+'">' + html + '</div>';
21790 html = this.cleanHtml(html);
21791 // fix up the special chars.. normaly like back quotes in word...
21792 // however we do not want to do this with chinese..
21793 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21794 var cc = b.charCodeAt();
21796 (cc >= 0x4E00 && cc < 0xA000 ) ||
21797 (cc >= 0x3400 && cc < 0x4E00 ) ||
21798 (cc >= 0xf900 && cc < 0xfb00 )
21804 if(this.owner.fireEvent('beforesync', this, html) !== false){
21805 this.el.dom.value = html;
21806 this.owner.fireEvent('sync', this, html);
21812 * Protected method that will not generally be called directly. Pushes the value of the textarea
21813 * into the iframe editor.
21815 pushValue : function(){
21816 if(this.initialized){
21817 var v = this.el.dom.value.trim();
21819 // if(v.length < 1){
21823 if(this.owner.fireEvent('beforepush', this, v) !== false){
21824 var d = (this.doc.body || this.doc.documentElement);
21826 this.cleanUpPaste();
21827 this.el.dom.value = d.innerHTML;
21828 this.owner.fireEvent('push', this, v);
21834 deferFocus : function(){
21835 this.focus.defer(10, this);
21839 focus : function(){
21840 if(this.win && !this.sourceEditMode){
21847 assignDocWin: function()
21849 var iframe = this.iframe;
21852 this.doc = iframe.contentWindow.document;
21853 this.win = iframe.contentWindow;
21855 // if (!Roo.get(this.frameId)) {
21858 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21859 // this.win = Roo.get(this.frameId).dom.contentWindow;
21861 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21865 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21866 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21871 initEditor : function(){
21872 //console.log("INIT EDITOR");
21873 this.assignDocWin();
21877 this.doc.designMode="on";
21879 this.doc.write(this.getDocMarkup());
21882 var dbody = (this.doc.body || this.doc.documentElement);
21883 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21884 // this copies styles from the containing element into thsi one..
21885 // not sure why we need all of this..
21886 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21888 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21889 //ss['background-attachment'] = 'fixed'; // w3c
21890 dbody.bgProperties = 'fixed'; // ie
21891 //Roo.DomHelper.applyStyles(dbody, ss);
21892 Roo.EventManager.on(this.doc, {
21893 //'mousedown': this.onEditorEvent,
21894 'mouseup': this.onEditorEvent,
21895 'dblclick': this.onEditorEvent,
21896 'click': this.onEditorEvent,
21897 'keyup': this.onEditorEvent,
21902 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21904 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21905 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21907 this.initialized = true;
21909 this.owner.fireEvent('initialize', this);
21914 onDestroy : function(){
21920 //for (var i =0; i < this.toolbars.length;i++) {
21921 // // fixme - ask toolbars for heights?
21922 // this.toolbars[i].onDestroy();
21925 //this.wrap.dom.innerHTML = '';
21926 //this.wrap.remove();
21931 onFirstFocus : function(){
21933 this.assignDocWin();
21936 this.activated = true;
21939 if(Roo.isGecko){ // prevent silly gecko errors
21941 var s = this.win.getSelection();
21942 if(!s.focusNode || s.focusNode.nodeType != 3){
21943 var r = s.getRangeAt(0);
21944 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21949 this.execCmd('useCSS', true);
21950 this.execCmd('styleWithCSS', false);
21953 this.owner.fireEvent('activate', this);
21957 adjustFont: function(btn){
21958 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21959 //if(Roo.isSafari){ // safari
21962 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21963 if(Roo.isSafari){ // safari
21964 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21965 v = (v < 10) ? 10 : v;
21966 v = (v > 48) ? 48 : v;
21967 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21972 v = Math.max(1, v+adjust);
21974 this.execCmd('FontSize', v );
21977 onEditorEvent : function(e)
21979 this.owner.fireEvent('editorevent', this, e);
21980 // this.updateToolbar();
21981 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21984 insertTag : function(tg)
21986 // could be a bit smarter... -> wrap the current selected tRoo..
21987 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21989 range = this.createRange(this.getSelection());
21990 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21991 wrappingNode.appendChild(range.extractContents());
21992 range.insertNode(wrappingNode);
21999 this.execCmd("formatblock", tg);
22003 insertText : function(txt)
22007 var range = this.createRange();
22008 range.deleteContents();
22009 //alert(Sender.getAttribute('label'));
22011 range.insertNode(this.doc.createTextNode(txt));
22017 * Executes a Midas editor command on the editor document and performs necessary focus and
22018 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22019 * @param {String} cmd The Midas command
22020 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22022 relayCmd : function(cmd, value){
22024 this.execCmd(cmd, value);
22025 this.owner.fireEvent('editorevent', this);
22026 //this.updateToolbar();
22027 this.owner.deferFocus();
22031 * Executes a Midas editor command directly on the editor document.
22032 * For visual commands, you should use {@link #relayCmd} instead.
22033 * <b>This should only be called after the editor is initialized.</b>
22034 * @param {String} cmd The Midas command
22035 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22037 execCmd : function(cmd, value){
22038 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22045 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22047 * @param {String} text | dom node..
22049 insertAtCursor : function(text)
22052 if(!this.activated){
22058 var r = this.doc.selection.createRange();
22069 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22073 // from jquery ui (MIT licenced)
22075 var win = this.win;
22077 if (win.getSelection && win.getSelection().getRangeAt) {
22078 range = win.getSelection().getRangeAt(0);
22079 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22080 range.insertNode(node);
22081 } else if (win.document.selection && win.document.selection.createRange) {
22082 // no firefox support
22083 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22084 win.document.selection.createRange().pasteHTML(txt);
22086 // no firefox support
22087 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22088 this.execCmd('InsertHTML', txt);
22097 mozKeyPress : function(e){
22099 var c = e.getCharCode(), cmd;
22102 c = String.fromCharCode(c).toLowerCase();
22116 this.cleanUpPaste.defer(100, this);
22124 e.preventDefault();
22132 fixKeys : function(){ // load time branching for fastest keydown performance
22134 return function(e){
22135 var k = e.getKey(), r;
22138 r = this.doc.selection.createRange();
22141 r.pasteHTML('    ');
22148 r = this.doc.selection.createRange();
22150 var target = r.parentElement();
22151 if(!target || target.tagName.toLowerCase() != 'li'){
22153 r.pasteHTML('<br />');
22159 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22160 this.cleanUpPaste.defer(100, this);
22166 }else if(Roo.isOpera){
22167 return function(e){
22168 var k = e.getKey();
22172 this.execCmd('InsertHTML','    ');
22175 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22176 this.cleanUpPaste.defer(100, this);
22181 }else if(Roo.isSafari){
22182 return function(e){
22183 var k = e.getKey();
22187 this.execCmd('InsertText','\t');
22191 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22192 this.cleanUpPaste.defer(100, this);
22200 getAllAncestors: function()
22202 var p = this.getSelectedNode();
22205 a.push(p); // push blank onto stack..
22206 p = this.getParentElement();
22210 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22214 a.push(this.doc.body);
22218 lastSelNode : false,
22221 getSelection : function()
22223 this.assignDocWin();
22224 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22227 getSelectedNode: function()
22229 // this may only work on Gecko!!!
22231 // should we cache this!!!!
22236 var range = this.createRange(this.getSelection()).cloneRange();
22239 var parent = range.parentElement();
22241 var testRange = range.duplicate();
22242 testRange.moveToElementText(parent);
22243 if (testRange.inRange(range)) {
22246 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22249 parent = parent.parentElement;
22254 // is ancestor a text element.
22255 var ac = range.commonAncestorContainer;
22256 if (ac.nodeType == 3) {
22257 ac = ac.parentNode;
22260 var ar = ac.childNodes;
22263 var other_nodes = [];
22264 var has_other_nodes = false;
22265 for (var i=0;i<ar.length;i++) {
22266 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22269 // fullly contained node.
22271 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22276 // probably selected..
22277 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22278 other_nodes.push(ar[i]);
22282 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22287 has_other_nodes = true;
22289 if (!nodes.length && other_nodes.length) {
22290 nodes= other_nodes;
22292 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22298 createRange: function(sel)
22300 // this has strange effects when using with
22301 // top toolbar - not sure if it's a great idea.
22302 //this.editor.contentWindow.focus();
22303 if (typeof sel != "undefined") {
22305 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22307 return this.doc.createRange();
22310 return this.doc.createRange();
22313 getParentElement: function()
22316 this.assignDocWin();
22317 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22319 var range = this.createRange(sel);
22322 var p = range.commonAncestorContainer;
22323 while (p.nodeType == 3) { // text node
22334 * Range intersection.. the hard stuff...
22338 * [ -- selected range --- ]
22342 * if end is before start or hits it. fail.
22343 * if start is after end or hits it fail.
22345 * if either hits (but other is outside. - then it's not
22351 // @see http://www.thismuchiknow.co.uk/?p=64.
22352 rangeIntersectsNode : function(range, node)
22354 var nodeRange = node.ownerDocument.createRange();
22356 nodeRange.selectNode(node);
22358 nodeRange.selectNodeContents(node);
22361 var rangeStartRange = range.cloneRange();
22362 rangeStartRange.collapse(true);
22364 var rangeEndRange = range.cloneRange();
22365 rangeEndRange.collapse(false);
22367 var nodeStartRange = nodeRange.cloneRange();
22368 nodeStartRange.collapse(true);
22370 var nodeEndRange = nodeRange.cloneRange();
22371 nodeEndRange.collapse(false);
22373 return rangeStartRange.compareBoundaryPoints(
22374 Range.START_TO_START, nodeEndRange) == -1 &&
22375 rangeEndRange.compareBoundaryPoints(
22376 Range.START_TO_START, nodeStartRange) == 1;
22380 rangeCompareNode : function(range, node)
22382 var nodeRange = node.ownerDocument.createRange();
22384 nodeRange.selectNode(node);
22386 nodeRange.selectNodeContents(node);
22390 range.collapse(true);
22392 nodeRange.collapse(true);
22394 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22395 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22397 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22399 var nodeIsBefore = ss == 1;
22400 var nodeIsAfter = ee == -1;
22402 if (nodeIsBefore && nodeIsAfter) {
22405 if (!nodeIsBefore && nodeIsAfter) {
22406 return 1; //right trailed.
22409 if (nodeIsBefore && !nodeIsAfter) {
22410 return 2; // left trailed.
22416 // private? - in a new class?
22417 cleanUpPaste : function()
22419 // cleans up the whole document..
22420 Roo.log('cleanuppaste');
22422 this.cleanUpChildren(this.doc.body);
22423 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22424 if (clean != this.doc.body.innerHTML) {
22425 this.doc.body.innerHTML = clean;
22430 cleanWordChars : function(input) {// change the chars to hex code
22431 var he = Roo.HtmlEditorCore;
22433 var output = input;
22434 Roo.each(he.swapCodes, function(sw) {
22435 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22437 output = output.replace(swapper, sw[1]);
22444 cleanUpChildren : function (n)
22446 if (!n.childNodes.length) {
22449 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22450 this.cleanUpChild(n.childNodes[i]);
22457 cleanUpChild : function (node)
22460 //console.log(node);
22461 if (node.nodeName == "#text") {
22462 // clean up silly Windows -- stuff?
22465 if (node.nodeName == "#comment") {
22466 node.parentNode.removeChild(node);
22467 // clean up silly Windows -- stuff?
22470 var lcname = node.tagName.toLowerCase();
22471 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22472 // whitelist of tags..
22474 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22476 node.parentNode.removeChild(node);
22481 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22483 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22484 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22486 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22487 // remove_keep_children = true;
22490 if (remove_keep_children) {
22491 this.cleanUpChildren(node);
22492 // inserts everything just before this node...
22493 while (node.childNodes.length) {
22494 var cn = node.childNodes[0];
22495 node.removeChild(cn);
22496 node.parentNode.insertBefore(cn, node);
22498 node.parentNode.removeChild(node);
22502 if (!node.attributes || !node.attributes.length) {
22503 this.cleanUpChildren(node);
22507 function cleanAttr(n,v)
22510 if (v.match(/^\./) || v.match(/^\//)) {
22513 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22516 if (v.match(/^#/)) {
22519 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22520 node.removeAttribute(n);
22524 var cwhite = this.cwhite;
22525 var cblack = this.cblack;
22527 function cleanStyle(n,v)
22529 if (v.match(/expression/)) { //XSS?? should we even bother..
22530 node.removeAttribute(n);
22534 var parts = v.split(/;/);
22537 Roo.each(parts, function(p) {
22538 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22542 var l = p.split(':').shift().replace(/\s+/g,'');
22543 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22545 if ( cwhite.length && cblack.indexOf(l) > -1) {
22546 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22547 //node.removeAttribute(n);
22551 // only allow 'c whitelisted system attributes'
22552 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22553 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22554 //node.removeAttribute(n);
22564 if (clean.length) {
22565 node.setAttribute(n, clean.join(';'));
22567 node.removeAttribute(n);
22573 for (var i = node.attributes.length-1; i > -1 ; i--) {
22574 var a = node.attributes[i];
22577 if (a.name.toLowerCase().substr(0,2)=='on') {
22578 node.removeAttribute(a.name);
22581 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22582 node.removeAttribute(a.name);
22585 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22586 cleanAttr(a.name,a.value); // fixme..
22589 if (a.name == 'style') {
22590 cleanStyle(a.name,a.value);
22593 /// clean up MS crap..
22594 // tecnically this should be a list of valid class'es..
22597 if (a.name == 'class') {
22598 if (a.value.match(/^Mso/)) {
22599 node.className = '';
22602 if (a.value.match(/^body$/)) {
22603 node.className = '';
22614 this.cleanUpChildren(node);
22620 * Clean up MS wordisms...
22622 cleanWord : function(node)
22627 this.cleanWord(this.doc.body);
22630 if (node.nodeName == "#text") {
22631 // clean up silly Windows -- stuff?
22634 if (node.nodeName == "#comment") {
22635 node.parentNode.removeChild(node);
22636 // clean up silly Windows -- stuff?
22640 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22641 node.parentNode.removeChild(node);
22645 // remove - but keep children..
22646 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22647 while (node.childNodes.length) {
22648 var cn = node.childNodes[0];
22649 node.removeChild(cn);
22650 node.parentNode.insertBefore(cn, node);
22652 node.parentNode.removeChild(node);
22653 this.iterateChildren(node, this.cleanWord);
22657 if (node.className.length) {
22659 var cn = node.className.split(/\W+/);
22661 Roo.each(cn, function(cls) {
22662 if (cls.match(/Mso[a-zA-Z]+/)) {
22667 node.className = cna.length ? cna.join(' ') : '';
22669 node.removeAttribute("class");
22673 if (node.hasAttribute("lang")) {
22674 node.removeAttribute("lang");
22677 if (node.hasAttribute("style")) {
22679 var styles = node.getAttribute("style").split(";");
22681 Roo.each(styles, function(s) {
22682 if (!s.match(/:/)) {
22685 var kv = s.split(":");
22686 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22689 // what ever is left... we allow.
22692 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22693 if (!nstyle.length) {
22694 node.removeAttribute('style');
22697 this.iterateChildren(node, this.cleanWord);
22703 * iterateChildren of a Node, calling fn each time, using this as the scole..
22704 * @param {DomNode} node node to iterate children of.
22705 * @param {Function} fn method of this class to call on each item.
22707 iterateChildren : function(node, fn)
22709 if (!node.childNodes.length) {
22712 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22713 fn.call(this, node.childNodes[i])
22719 * cleanTableWidths.
22721 * Quite often pasting from word etc.. results in tables with column and widths.
22722 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22725 cleanTableWidths : function(node)
22730 this.cleanTableWidths(this.doc.body);
22735 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22738 Roo.log(node.tagName);
22739 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22740 this.iterateChildren(node, this.cleanTableWidths);
22743 if (node.hasAttribute('width')) {
22744 node.removeAttribute('width');
22748 if (node.hasAttribute("style")) {
22751 var styles = node.getAttribute("style").split(";");
22753 Roo.each(styles, function(s) {
22754 if (!s.match(/:/)) {
22757 var kv = s.split(":");
22758 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22761 // what ever is left... we allow.
22764 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22765 if (!nstyle.length) {
22766 node.removeAttribute('style');
22770 this.iterateChildren(node, this.cleanTableWidths);
22778 domToHTML : function(currentElement, depth, nopadtext) {
22780 depth = depth || 0;
22781 nopadtext = nopadtext || false;
22783 if (!currentElement) {
22784 return this.domToHTML(this.doc.body);
22787 //Roo.log(currentElement);
22789 var allText = false;
22790 var nodeName = currentElement.nodeName;
22791 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22793 if (nodeName == '#text') {
22795 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22800 if (nodeName != 'BODY') {
22803 // Prints the node tagName, such as <A>, <IMG>, etc
22806 for(i = 0; i < currentElement.attributes.length;i++) {
22808 var aname = currentElement.attributes.item(i).name;
22809 if (!currentElement.attributes.item(i).value.length) {
22812 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22815 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22824 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22827 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22832 // Traverse the tree
22834 var currentElementChild = currentElement.childNodes.item(i);
22835 var allText = true;
22836 var innerHTML = '';
22838 while (currentElementChild) {
22839 // Formatting code (indent the tree so it looks nice on the screen)
22840 var nopad = nopadtext;
22841 if (lastnode == 'SPAN') {
22845 if (currentElementChild.nodeName == '#text') {
22846 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22847 toadd = nopadtext ? toadd : toadd.trim();
22848 if (!nopad && toadd.length > 80) {
22849 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22851 innerHTML += toadd;
22854 currentElementChild = currentElement.childNodes.item(i);
22860 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22862 // Recursively traverse the tree structure of the child node
22863 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22864 lastnode = currentElementChild.nodeName;
22866 currentElementChild=currentElement.childNodes.item(i);
22872 // The remaining code is mostly for formatting the tree
22873 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22878 ret+= "</"+tagName+">";
22884 applyBlacklists : function()
22886 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22887 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22891 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22892 if (b.indexOf(tag) > -1) {
22895 this.white.push(tag);
22899 Roo.each(w, function(tag) {
22900 if (b.indexOf(tag) > -1) {
22903 if (this.white.indexOf(tag) > -1) {
22906 this.white.push(tag);
22911 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22912 if (w.indexOf(tag) > -1) {
22915 this.black.push(tag);
22919 Roo.each(b, function(tag) {
22920 if (w.indexOf(tag) > -1) {
22923 if (this.black.indexOf(tag) > -1) {
22926 this.black.push(tag);
22931 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22932 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22936 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22937 if (b.indexOf(tag) > -1) {
22940 this.cwhite.push(tag);
22944 Roo.each(w, function(tag) {
22945 if (b.indexOf(tag) > -1) {
22948 if (this.cwhite.indexOf(tag) > -1) {
22951 this.cwhite.push(tag);
22956 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22957 if (w.indexOf(tag) > -1) {
22960 this.cblack.push(tag);
22964 Roo.each(b, function(tag) {
22965 if (w.indexOf(tag) > -1) {
22968 if (this.cblack.indexOf(tag) > -1) {
22971 this.cblack.push(tag);
22976 setStylesheets : function(stylesheets)
22978 if(typeof(stylesheets) == 'string'){
22979 Roo.get(this.iframe.contentDocument.head).createChild({
22981 rel : 'stylesheet',
22990 Roo.each(stylesheets, function(s) {
22995 Roo.get(_this.iframe.contentDocument.head).createChild({
22997 rel : 'stylesheet',
23006 removeStylesheets : function()
23010 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23015 setStyle : function(style)
23017 Roo.get(this.iframe.contentDocument.head).createChild({
23026 // hide stuff that is not compatible
23040 * @event specialkey
23044 * @cfg {String} fieldClass @hide
23047 * @cfg {String} focusClass @hide
23050 * @cfg {String} autoCreate @hide
23053 * @cfg {String} inputType @hide
23056 * @cfg {String} invalidClass @hide
23059 * @cfg {String} invalidText @hide
23062 * @cfg {String} msgFx @hide
23065 * @cfg {String} validateOnBlur @hide
23069 Roo.HtmlEditorCore.white = [
23070 'area', 'br', 'img', 'input', 'hr', 'wbr',
23072 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23073 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23074 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23075 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23076 'table', 'ul', 'xmp',
23078 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23081 'dir', 'menu', 'ol', 'ul', 'dl',
23087 Roo.HtmlEditorCore.black = [
23088 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23090 'base', 'basefont', 'bgsound', 'blink', 'body',
23091 'frame', 'frameset', 'head', 'html', 'ilayer',
23092 'iframe', 'layer', 'link', 'meta', 'object',
23093 'script', 'style' ,'title', 'xml' // clean later..
23095 Roo.HtmlEditorCore.clean = [
23096 'script', 'style', 'title', 'xml'
23098 Roo.HtmlEditorCore.remove = [
23103 Roo.HtmlEditorCore.ablack = [
23107 Roo.HtmlEditorCore.aclean = [
23108 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23112 Roo.HtmlEditorCore.pwhite= [
23113 'http', 'https', 'mailto'
23116 // white listed style attributes.
23117 Roo.HtmlEditorCore.cwhite= [
23118 // 'text-align', /// default is to allow most things..
23124 // black listed style attributes.
23125 Roo.HtmlEditorCore.cblack= [
23126 // 'font-size' -- this can be set by the project
23130 Roo.HtmlEditorCore.swapCodes =[
23149 * @class Roo.bootstrap.HtmlEditor
23150 * @extends Roo.bootstrap.TextArea
23151 * Bootstrap HtmlEditor class
23154 * Create a new HtmlEditor
23155 * @param {Object} config The config object
23158 Roo.bootstrap.HtmlEditor = function(config){
23159 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23160 if (!this.toolbars) {
23161 this.toolbars = [];
23164 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23167 * @event initialize
23168 * Fires when the editor is fully initialized (including the iframe)
23169 * @param {HtmlEditor} this
23174 * Fires when the editor is first receives the focus. Any insertion must wait
23175 * until after this event.
23176 * @param {HtmlEditor} this
23180 * @event beforesync
23181 * Fires before the textarea is updated with content from the editor iframe. Return false
23182 * to cancel the sync.
23183 * @param {HtmlEditor} this
23184 * @param {String} html
23188 * @event beforepush
23189 * Fires before the iframe editor is updated with content from the textarea. Return false
23190 * to cancel the push.
23191 * @param {HtmlEditor} this
23192 * @param {String} html
23197 * Fires when the textarea is updated with content from the editor iframe.
23198 * @param {HtmlEditor} this
23199 * @param {String} html
23204 * Fires when the iframe editor is updated with content from the textarea.
23205 * @param {HtmlEditor} this
23206 * @param {String} html
23210 * @event editmodechange
23211 * Fires when the editor switches edit modes
23212 * @param {HtmlEditor} this
23213 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23215 editmodechange: true,
23217 * @event editorevent
23218 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23219 * @param {HtmlEditor} this
23223 * @event firstfocus
23224 * Fires when on first focus - needed by toolbars..
23225 * @param {HtmlEditor} this
23230 * Auto save the htmlEditor value as a file into Events
23231 * @param {HtmlEditor} this
23235 * @event savedpreview
23236 * preview the saved version of htmlEditor
23237 * @param {HtmlEditor} this
23244 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23248 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23253 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23258 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23263 * @cfg {Number} height (in pixels)
23267 * @cfg {Number} width (in pixels)
23272 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23275 stylesheets: false,
23280 // private properties
23281 validationEvent : false,
23283 initialized : false,
23286 onFocus : Roo.emptyFn,
23288 hideMode:'offsets',
23290 tbContainer : false,
23294 toolbarContainer :function() {
23295 return this.wrap.select('.x-html-editor-tb',true).first();
23299 * Protected method that will not generally be called directly. It
23300 * is called when the editor creates its toolbar. Override this method if you need to
23301 * add custom toolbar buttons.
23302 * @param {HtmlEditor} editor
23304 createToolbar : function(){
23305 Roo.log('renewing');
23306 Roo.log("create toolbars");
23308 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23309 this.toolbars[0].render(this.toolbarContainer());
23313 // if (!editor.toolbars || !editor.toolbars.length) {
23314 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23317 // for (var i =0 ; i < editor.toolbars.length;i++) {
23318 // editor.toolbars[i] = Roo.factory(
23319 // typeof(editor.toolbars[i]) == 'string' ?
23320 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23321 // Roo.bootstrap.HtmlEditor);
23322 // editor.toolbars[i].init(editor);
23328 onRender : function(ct, position)
23330 // Roo.log("Call onRender: " + this.xtype);
23332 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23334 this.wrap = this.inputEl().wrap({
23335 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23338 this.editorcore.onRender(ct, position);
23340 if (this.resizable) {
23341 this.resizeEl = new Roo.Resizable(this.wrap, {
23345 minHeight : this.height,
23346 height: this.height,
23347 handles : this.resizable,
23350 resize : function(r, w, h) {
23351 _t.onResize(w,h); // -something
23357 this.createToolbar(this);
23360 if(!this.width && this.resizable){
23361 this.setSize(this.wrap.getSize());
23363 if (this.resizeEl) {
23364 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23365 // should trigger onReize..
23371 onResize : function(w, h)
23373 Roo.log('resize: ' +w + ',' + h );
23374 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23378 if(this.inputEl() ){
23379 if(typeof w == 'number'){
23380 var aw = w - this.wrap.getFrameWidth('lr');
23381 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23384 if(typeof h == 'number'){
23385 var tbh = -11; // fixme it needs to tool bar size!
23386 for (var i =0; i < this.toolbars.length;i++) {
23387 // fixme - ask toolbars for heights?
23388 tbh += this.toolbars[i].el.getHeight();
23389 //if (this.toolbars[i].footer) {
23390 // tbh += this.toolbars[i].footer.el.getHeight();
23398 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23399 ah -= 5; // knock a few pixes off for look..
23400 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23404 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23405 this.editorcore.onResize(ew,eh);
23410 * Toggles the editor between standard and source edit mode.
23411 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23413 toggleSourceEdit : function(sourceEditMode)
23415 this.editorcore.toggleSourceEdit(sourceEditMode);
23417 if(this.editorcore.sourceEditMode){
23418 Roo.log('editor - showing textarea');
23421 // Roo.log(this.syncValue());
23423 this.inputEl().removeClass(['hide', 'x-hidden']);
23424 this.inputEl().dom.removeAttribute('tabIndex');
23425 this.inputEl().focus();
23427 Roo.log('editor - hiding textarea');
23429 // Roo.log(this.pushValue());
23432 this.inputEl().addClass(['hide', 'x-hidden']);
23433 this.inputEl().dom.setAttribute('tabIndex', -1);
23434 //this.deferFocus();
23437 if(this.resizable){
23438 this.setSize(this.wrap.getSize());
23441 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23444 // private (for BoxComponent)
23445 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23447 // private (for BoxComponent)
23448 getResizeEl : function(){
23452 // private (for BoxComponent)
23453 getPositionEl : function(){
23458 initEvents : function(){
23459 this.originalValue = this.getValue();
23463 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23466 // markInvalid : Roo.emptyFn,
23468 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23471 // clearInvalid : Roo.emptyFn,
23473 setValue : function(v){
23474 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23475 this.editorcore.pushValue();
23480 deferFocus : function(){
23481 this.focus.defer(10, this);
23485 focus : function(){
23486 this.editorcore.focus();
23492 onDestroy : function(){
23498 for (var i =0; i < this.toolbars.length;i++) {
23499 // fixme - ask toolbars for heights?
23500 this.toolbars[i].onDestroy();
23503 this.wrap.dom.innerHTML = '';
23504 this.wrap.remove();
23509 onFirstFocus : function(){
23510 //Roo.log("onFirstFocus");
23511 this.editorcore.onFirstFocus();
23512 for (var i =0; i < this.toolbars.length;i++) {
23513 this.toolbars[i].onFirstFocus();
23519 syncValue : function()
23521 this.editorcore.syncValue();
23524 pushValue : function()
23526 this.editorcore.pushValue();
23530 // hide stuff that is not compatible
23544 * @event specialkey
23548 * @cfg {String} fieldClass @hide
23551 * @cfg {String} focusClass @hide
23554 * @cfg {String} autoCreate @hide
23557 * @cfg {String} inputType @hide
23560 * @cfg {String} invalidClass @hide
23563 * @cfg {String} invalidText @hide
23566 * @cfg {String} msgFx @hide
23569 * @cfg {String} validateOnBlur @hide
23578 Roo.namespace('Roo.bootstrap.htmleditor');
23580 * @class Roo.bootstrap.HtmlEditorToolbar1
23585 new Roo.bootstrap.HtmlEditor({
23588 new Roo.bootstrap.HtmlEditorToolbar1({
23589 disable : { fonts: 1 , format: 1, ..., ... , ...],
23595 * @cfg {Object} disable List of elements to disable..
23596 * @cfg {Array} btns List of additional buttons.
23600 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23603 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23606 Roo.apply(this, config);
23608 // default disabled, based on 'good practice'..
23609 this.disable = this.disable || {};
23610 Roo.applyIf(this.disable, {
23613 specialElements : true
23615 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23617 this.editor = config.editor;
23618 this.editorcore = config.editor.editorcore;
23620 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23622 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23623 // dont call parent... till later.
23625 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23630 editorcore : false,
23635 "h1","h2","h3","h4","h5","h6",
23637 "abbr", "acronym", "address", "cite", "samp", "var",
23641 onRender : function(ct, position)
23643 // Roo.log("Call onRender: " + this.xtype);
23645 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23647 this.el.dom.style.marginBottom = '0';
23649 var editorcore = this.editorcore;
23650 var editor= this.editor;
23653 var btn = function(id,cmd , toggle, handler, html){
23655 var event = toggle ? 'toggle' : 'click';
23660 xns: Roo.bootstrap,
23663 enableToggle:toggle !== false,
23665 pressed : toggle ? false : null,
23668 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23669 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23675 // var cb_box = function...
23680 xns: Roo.bootstrap,
23681 glyphicon : 'font',
23685 xns: Roo.bootstrap,
23689 Roo.each(this.formats, function(f) {
23690 style.menu.items.push({
23692 xns: Roo.bootstrap,
23693 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23698 editorcore.insertTag(this.tagname);
23705 children.push(style);
23707 btn('bold',false,true);
23708 btn('italic',false,true);
23709 btn('align-left', 'justifyleft',true);
23710 btn('align-center', 'justifycenter',true);
23711 btn('align-right' , 'justifyright',true);
23712 btn('link', false, false, function(btn) {
23713 //Roo.log("create link?");
23714 var url = prompt(this.createLinkText, this.defaultLinkValue);
23715 if(url && url != 'http:/'+'/'){
23716 this.editorcore.relayCmd('createlink', url);
23719 btn('list','insertunorderedlist',true);
23720 btn('pencil', false,true, function(btn){
23722 this.toggleSourceEdit(btn.pressed);
23725 if (this.editor.btns.length > 0) {
23726 for (var i = 0; i<this.editor.btns.length; i++) {
23727 children.push(this.editor.btns[i]);
23735 xns: Roo.bootstrap,
23740 xns: Roo.bootstrap,
23745 cog.menu.items.push({
23747 xns: Roo.bootstrap,
23748 html : Clean styles,
23753 editorcore.insertTag(this.tagname);
23762 this.xtype = 'NavSimplebar';
23764 for(var i=0;i< children.length;i++) {
23766 this.buttons.add(this.addxtypeChild(children[i]));
23770 editor.on('editorevent', this.updateToolbar, this);
23772 onBtnClick : function(id)
23774 this.editorcore.relayCmd(id);
23775 this.editorcore.focus();
23779 * Protected method that will not generally be called directly. It triggers
23780 * a toolbar update by reading the markup state of the current selection in the editor.
23782 updateToolbar: function(){
23784 if(!this.editorcore.activated){
23785 this.editor.onFirstFocus(); // is this neeed?
23789 var btns = this.buttons;
23790 var doc = this.editorcore.doc;
23791 btns.get('bold').setActive(doc.queryCommandState('bold'));
23792 btns.get('italic').setActive(doc.queryCommandState('italic'));
23793 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23795 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23796 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23797 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23799 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23800 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23803 var ans = this.editorcore.getAllAncestors();
23804 if (this.formatCombo) {
23807 var store = this.formatCombo.store;
23808 this.formatCombo.setValue("");
23809 for (var i =0; i < ans.length;i++) {
23810 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23812 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23820 // hides menus... - so this cant be on a menu...
23821 Roo.bootstrap.MenuMgr.hideAll();
23823 Roo.bootstrap.MenuMgr.hideAll();
23824 //this.editorsyncValue();
23826 onFirstFocus: function() {
23827 this.buttons.each(function(item){
23831 toggleSourceEdit : function(sourceEditMode){
23834 if(sourceEditMode){
23835 Roo.log("disabling buttons");
23836 this.buttons.each( function(item){
23837 if(item.cmd != 'pencil'){
23843 Roo.log("enabling buttons");
23844 if(this.editorcore.initialized){
23845 this.buttons.each( function(item){
23851 Roo.log("calling toggole on editor");
23852 // tell the editor that it's been pressed..
23853 this.editor.toggleSourceEdit(sourceEditMode);
23863 * @class Roo.bootstrap.Table.AbstractSelectionModel
23864 * @extends Roo.util.Observable
23865 * Abstract base class for grid SelectionModels. It provides the interface that should be
23866 * implemented by descendant classes. This class should not be directly instantiated.
23869 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23870 this.locked = false;
23871 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23875 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23876 /** @ignore Called by the grid automatically. Do not call directly. */
23877 init : function(grid){
23883 * Locks the selections.
23886 this.locked = true;
23890 * Unlocks the selections.
23892 unlock : function(){
23893 this.locked = false;
23897 * Returns true if the selections are locked.
23898 * @return {Boolean}
23900 isLocked : function(){
23901 return this.locked;
23905 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23906 * @class Roo.bootstrap.Table.RowSelectionModel
23907 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23908 * It supports multiple selections and keyboard selection/navigation.
23910 * @param {Object} config
23913 Roo.bootstrap.Table.RowSelectionModel = function(config){
23914 Roo.apply(this, config);
23915 this.selections = new Roo.util.MixedCollection(false, function(o){
23920 this.lastActive = false;
23924 * @event selectionchange
23925 * Fires when the selection changes
23926 * @param {SelectionModel} this
23928 "selectionchange" : true,
23930 * @event afterselectionchange
23931 * Fires after the selection changes (eg. by key press or clicking)
23932 * @param {SelectionModel} this
23934 "afterselectionchange" : true,
23936 * @event beforerowselect
23937 * Fires when a row is selected being selected, return false to cancel.
23938 * @param {SelectionModel} this
23939 * @param {Number} rowIndex The selected index
23940 * @param {Boolean} keepExisting False if other selections will be cleared
23942 "beforerowselect" : true,
23945 * Fires when a row is selected.
23946 * @param {SelectionModel} this
23947 * @param {Number} rowIndex The selected index
23948 * @param {Roo.data.Record} r The record
23950 "rowselect" : true,
23952 * @event rowdeselect
23953 * Fires when a row is deselected.
23954 * @param {SelectionModel} this
23955 * @param {Number} rowIndex The selected index
23957 "rowdeselect" : true
23959 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23960 this.locked = false;
23963 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23965 * @cfg {Boolean} singleSelect
23966 * True to allow selection of only one row at a time (defaults to false)
23968 singleSelect : false,
23971 initEvents : function()
23974 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23975 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23976 //}else{ // allow click to work like normal
23977 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23979 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23980 this.grid.on("rowclick", this.handleMouseDown, this);
23982 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23983 "up" : function(e){
23985 this.selectPrevious(e.shiftKey);
23986 }else if(this.last !== false && this.lastActive !== false){
23987 var last = this.last;
23988 this.selectRange(this.last, this.lastActive-1);
23989 this.grid.getView().focusRow(this.lastActive);
23990 if(last !== false){
23994 this.selectFirstRow();
23996 this.fireEvent("afterselectionchange", this);
23998 "down" : function(e){
24000 this.selectNext(e.shiftKey);
24001 }else if(this.last !== false && this.lastActive !== false){
24002 var last = this.last;
24003 this.selectRange(this.last, this.lastActive+1);
24004 this.grid.getView().focusRow(this.lastActive);
24005 if(last !== false){
24009 this.selectFirstRow();
24011 this.fireEvent("afterselectionchange", this);
24015 this.grid.store.on('load', function(){
24016 this.selections.clear();
24019 var view = this.grid.view;
24020 view.on("refresh", this.onRefresh, this);
24021 view.on("rowupdated", this.onRowUpdated, this);
24022 view.on("rowremoved", this.onRemove, this);
24027 onRefresh : function()
24029 var ds = this.grid.store, i, v = this.grid.view;
24030 var s = this.selections;
24031 s.each(function(r){
24032 if((i = ds.indexOfId(r.id)) != -1){
24041 onRemove : function(v, index, r){
24042 this.selections.remove(r);
24046 onRowUpdated : function(v, index, r){
24047 if(this.isSelected(r)){
24048 v.onRowSelect(index);
24054 * @param {Array} records The records to select
24055 * @param {Boolean} keepExisting (optional) True to keep existing selections
24057 selectRecords : function(records, keepExisting)
24060 this.clearSelections();
24062 var ds = this.grid.store;
24063 for(var i = 0, len = records.length; i < len; i++){
24064 this.selectRow(ds.indexOf(records[i]), true);
24069 * Gets the number of selected rows.
24072 getCount : function(){
24073 return this.selections.length;
24077 * Selects the first row in the grid.
24079 selectFirstRow : function(){
24084 * Select the last row.
24085 * @param {Boolean} keepExisting (optional) True to keep existing selections
24087 selectLastRow : function(keepExisting){
24088 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24089 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24093 * Selects the row immediately following the last selected row.
24094 * @param {Boolean} keepExisting (optional) True to keep existing selections
24096 selectNext : function(keepExisting)
24098 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24099 this.selectRow(this.last+1, keepExisting);
24100 this.grid.getView().focusRow(this.last);
24105 * Selects the row that precedes the last selected row.
24106 * @param {Boolean} keepExisting (optional) True to keep existing selections
24108 selectPrevious : function(keepExisting){
24110 this.selectRow(this.last-1, keepExisting);
24111 this.grid.getView().focusRow(this.last);
24116 * Returns the selected records
24117 * @return {Array} Array of selected records
24119 getSelections : function(){
24120 return [].concat(this.selections.items);
24124 * Returns the first selected record.
24127 getSelected : function(){
24128 return this.selections.itemAt(0);
24133 * Clears all selections.
24135 clearSelections : function(fast)
24141 var ds = this.grid.store;
24142 var s = this.selections;
24143 s.each(function(r){
24144 this.deselectRow(ds.indexOfId(r.id));
24148 this.selections.clear();
24155 * Selects all rows.
24157 selectAll : function(){
24161 this.selections.clear();
24162 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24163 this.selectRow(i, true);
24168 * Returns True if there is a selection.
24169 * @return {Boolean}
24171 hasSelection : function(){
24172 return this.selections.length > 0;
24176 * Returns True if the specified row is selected.
24177 * @param {Number/Record} record The record or index of the record to check
24178 * @return {Boolean}
24180 isSelected : function(index){
24181 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24182 return (r && this.selections.key(r.id) ? true : false);
24186 * Returns True if the specified record id is selected.
24187 * @param {String} id The id of record to check
24188 * @return {Boolean}
24190 isIdSelected : function(id){
24191 return (this.selections.key(id) ? true : false);
24196 handleMouseDBClick : function(e, t){
24200 handleMouseDown : function(e, t)
24202 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24203 if(this.isLocked() || rowIndex < 0 ){
24206 if(e.shiftKey && this.last !== false){
24207 var last = this.last;
24208 this.selectRange(last, rowIndex, e.ctrlKey);
24209 this.last = last; // reset the last
24213 var isSelected = this.isSelected(rowIndex);
24214 //Roo.log("select row:" + rowIndex);
24216 this.deselectRow(rowIndex);
24218 this.selectRow(rowIndex, true);
24222 if(e.button !== 0 && isSelected){
24223 alert('rowIndex 2: ' + rowIndex);
24224 view.focusRow(rowIndex);
24225 }else if(e.ctrlKey && isSelected){
24226 this.deselectRow(rowIndex);
24227 }else if(!isSelected){
24228 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24229 view.focusRow(rowIndex);
24233 this.fireEvent("afterselectionchange", this);
24236 handleDragableRowClick : function(grid, rowIndex, e)
24238 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24239 this.selectRow(rowIndex, false);
24240 grid.view.focusRow(rowIndex);
24241 this.fireEvent("afterselectionchange", this);
24246 * Selects multiple rows.
24247 * @param {Array} rows Array of the indexes of the row to select
24248 * @param {Boolean} keepExisting (optional) True to keep existing selections
24250 selectRows : function(rows, keepExisting){
24252 this.clearSelections();
24254 for(var i = 0, len = rows.length; i < len; i++){
24255 this.selectRow(rows[i], true);
24260 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24261 * @param {Number} startRow The index of the first row in the range
24262 * @param {Number} endRow The index of the last row in the range
24263 * @param {Boolean} keepExisting (optional) True to retain existing selections
24265 selectRange : function(startRow, endRow, keepExisting){
24270 this.clearSelections();
24272 if(startRow <= endRow){
24273 for(var i = startRow; i <= endRow; i++){
24274 this.selectRow(i, true);
24277 for(var i = startRow; i >= endRow; i--){
24278 this.selectRow(i, true);
24284 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24285 * @param {Number} startRow The index of the first row in the range
24286 * @param {Number} endRow The index of the last row in the range
24288 deselectRange : function(startRow, endRow, preventViewNotify){
24292 for(var i = startRow; i <= endRow; i++){
24293 this.deselectRow(i, preventViewNotify);
24299 * @param {Number} row The index of the row to select
24300 * @param {Boolean} keepExisting (optional) True to keep existing selections
24302 selectRow : function(index, keepExisting, preventViewNotify)
24304 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24307 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24308 if(!keepExisting || this.singleSelect){
24309 this.clearSelections();
24312 var r = this.grid.store.getAt(index);
24313 //console.log('selectRow - record id :' + r.id);
24315 this.selections.add(r);
24316 this.last = this.lastActive = index;
24317 if(!preventViewNotify){
24318 var proxy = new Roo.Element(
24319 this.grid.getRowDom(index)
24321 proxy.addClass('bg-info info');
24323 this.fireEvent("rowselect", this, index, r);
24324 this.fireEvent("selectionchange", this);
24330 * @param {Number} row The index of the row to deselect
24332 deselectRow : function(index, preventViewNotify)
24337 if(this.last == index){
24340 if(this.lastActive == index){
24341 this.lastActive = false;
24344 var r = this.grid.store.getAt(index);
24349 this.selections.remove(r);
24350 //.console.log('deselectRow - record id :' + r.id);
24351 if(!preventViewNotify){
24353 var proxy = new Roo.Element(
24354 this.grid.getRowDom(index)
24356 proxy.removeClass('bg-info info');
24358 this.fireEvent("rowdeselect", this, index);
24359 this.fireEvent("selectionchange", this);
24363 restoreLast : function(){
24365 this.last = this._last;
24370 acceptsNav : function(row, col, cm){
24371 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24375 onEditorKey : function(field, e){
24376 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24381 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24383 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24385 }else if(k == e.ENTER && !e.ctrlKey){
24389 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24391 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24393 }else if(k == e.ESC){
24397 g.startEditing(newCell[0], newCell[1]);
24403 * Ext JS Library 1.1.1
24404 * Copyright(c) 2006-2007, Ext JS, LLC.
24406 * Originally Released Under LGPL - original licence link has changed is not relivant.
24409 * <script type="text/javascript">
24413 * @class Roo.bootstrap.PagingToolbar
24414 * @extends Roo.bootstrap.NavSimplebar
24415 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24417 * Create a new PagingToolbar
24418 * @param {Object} config The config object
24419 * @param {Roo.data.Store} store
24421 Roo.bootstrap.PagingToolbar = function(config)
24423 // old args format still supported... - xtype is prefered..
24424 // created from xtype...
24426 this.ds = config.dataSource;
24428 if (config.store && !this.ds) {
24429 this.store= Roo.factory(config.store, Roo.data);
24430 this.ds = this.store;
24431 this.ds.xmodule = this.xmodule || false;
24434 this.toolbarItems = [];
24435 if (config.items) {
24436 this.toolbarItems = config.items;
24439 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24444 this.bind(this.ds);
24447 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24451 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24453 * @cfg {Roo.data.Store} dataSource
24454 * The underlying data store providing the paged data
24457 * @cfg {String/HTMLElement/Element} container
24458 * container The id or element that will contain the toolbar
24461 * @cfg {Boolean} displayInfo
24462 * True to display the displayMsg (defaults to false)
24465 * @cfg {Number} pageSize
24466 * The number of records to display per page (defaults to 20)
24470 * @cfg {String} displayMsg
24471 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24473 displayMsg : 'Displaying {0} - {1} of {2}',
24475 * @cfg {String} emptyMsg
24476 * The message to display when no records are found (defaults to "No data to display")
24478 emptyMsg : 'No data to display',
24480 * Customizable piece of the default paging text (defaults to "Page")
24483 beforePageText : "Page",
24485 * Customizable piece of the default paging text (defaults to "of %0")
24488 afterPageText : "of {0}",
24490 * Customizable piece of the default paging text (defaults to "First Page")
24493 firstText : "First Page",
24495 * Customizable piece of the default paging text (defaults to "Previous Page")
24498 prevText : "Previous Page",
24500 * Customizable piece of the default paging text (defaults to "Next Page")
24503 nextText : "Next Page",
24505 * Customizable piece of the default paging text (defaults to "Last Page")
24508 lastText : "Last Page",
24510 * Customizable piece of the default paging text (defaults to "Refresh")
24513 refreshText : "Refresh",
24517 onRender : function(ct, position)
24519 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24520 this.navgroup.parentId = this.id;
24521 this.navgroup.onRender(this.el, null);
24522 // add the buttons to the navgroup
24524 if(this.displayInfo){
24525 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24526 this.displayEl = this.el.select('.x-paging-info', true).first();
24527 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24528 // this.displayEl = navel.el.select('span',true).first();
24534 Roo.each(_this.buttons, function(e){ // this might need to use render????
24535 Roo.factory(e).render(_this.el);
24539 Roo.each(_this.toolbarItems, function(e) {
24540 _this.navgroup.addItem(e);
24544 this.first = this.navgroup.addItem({
24545 tooltip: this.firstText,
24547 icon : 'fa fa-backward',
24549 preventDefault: true,
24550 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24553 this.prev = this.navgroup.addItem({
24554 tooltip: this.prevText,
24556 icon : 'fa fa-step-backward',
24558 preventDefault: true,
24559 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24561 //this.addSeparator();
24564 var field = this.navgroup.addItem( {
24566 cls : 'x-paging-position',
24568 html : this.beforePageText +
24569 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24570 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24573 this.field = field.el.select('input', true).first();
24574 this.field.on("keydown", this.onPagingKeydown, this);
24575 this.field.on("focus", function(){this.dom.select();});
24578 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24579 //this.field.setHeight(18);
24580 //this.addSeparator();
24581 this.next = this.navgroup.addItem({
24582 tooltip: this.nextText,
24584 html : ' <i class="fa fa-step-forward">',
24586 preventDefault: true,
24587 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24589 this.last = this.navgroup.addItem({
24590 tooltip: this.lastText,
24591 icon : 'fa fa-forward',
24594 preventDefault: true,
24595 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24597 //this.addSeparator();
24598 this.loading = this.navgroup.addItem({
24599 tooltip: this.refreshText,
24600 icon: 'fa fa-refresh',
24601 preventDefault: true,
24602 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24608 updateInfo : function(){
24609 if(this.displayEl){
24610 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24611 var msg = count == 0 ?
24615 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24617 this.displayEl.update(msg);
24622 onLoad : function(ds, r, o)
24624 this.cursor = o.params.start ? o.params.start : 0;
24626 var d = this.getPageData(),
24631 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24632 this.field.dom.value = ap;
24633 this.first.setDisabled(ap == 1);
24634 this.prev.setDisabled(ap == 1);
24635 this.next.setDisabled(ap == ps);
24636 this.last.setDisabled(ap == ps);
24637 this.loading.enable();
24642 getPageData : function(){
24643 var total = this.ds.getTotalCount();
24646 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24647 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24652 onLoadError : function(){
24653 this.loading.enable();
24657 onPagingKeydown : function(e){
24658 var k = e.getKey();
24659 var d = this.getPageData();
24661 var v = this.field.dom.value, pageNum;
24662 if(!v || isNaN(pageNum = parseInt(v, 10))){
24663 this.field.dom.value = d.activePage;
24666 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24667 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24670 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))
24672 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24673 this.field.dom.value = pageNum;
24674 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24677 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24679 var v = this.field.dom.value, pageNum;
24680 var increment = (e.shiftKey) ? 10 : 1;
24681 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24684 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24685 this.field.dom.value = d.activePage;
24688 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24690 this.field.dom.value = parseInt(v, 10) + increment;
24691 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24692 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24699 beforeLoad : function(){
24701 this.loading.disable();
24706 onClick : function(which){
24715 ds.load({params:{start: 0, limit: this.pageSize}});
24718 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24721 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24724 var total = ds.getTotalCount();
24725 var extra = total % this.pageSize;
24726 var lastStart = extra ? (total - extra) : total-this.pageSize;
24727 ds.load({params:{start: lastStart, limit: this.pageSize}});
24730 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24736 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24737 * @param {Roo.data.Store} store The data store to unbind
24739 unbind : function(ds){
24740 ds.un("beforeload", this.beforeLoad, this);
24741 ds.un("load", this.onLoad, this);
24742 ds.un("loadexception", this.onLoadError, this);
24743 ds.un("remove", this.updateInfo, this);
24744 ds.un("add", this.updateInfo, this);
24745 this.ds = undefined;
24749 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24750 * @param {Roo.data.Store} store The data store to bind
24752 bind : function(ds){
24753 ds.on("beforeload", this.beforeLoad, this);
24754 ds.on("load", this.onLoad, this);
24755 ds.on("loadexception", this.onLoadError, this);
24756 ds.on("remove", this.updateInfo, this);
24757 ds.on("add", this.updateInfo, this);
24768 * @class Roo.bootstrap.MessageBar
24769 * @extends Roo.bootstrap.Component
24770 * Bootstrap MessageBar class
24771 * @cfg {String} html contents of the MessageBar
24772 * @cfg {String} weight (info | success | warning | danger) default info
24773 * @cfg {String} beforeClass insert the bar before the given class
24774 * @cfg {Boolean} closable (true | false) default false
24775 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24778 * Create a new Element
24779 * @param {Object} config The config object
24782 Roo.bootstrap.MessageBar = function(config){
24783 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24786 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24792 beforeClass: 'bootstrap-sticky-wrap',
24794 getAutoCreate : function(){
24798 cls: 'alert alert-dismissable alert-' + this.weight,
24803 html: this.html || ''
24809 cfg.cls += ' alert-messages-fixed';
24823 onRender : function(ct, position)
24825 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24828 var cfg = Roo.apply({}, this.getAutoCreate());
24832 cfg.cls += ' ' + this.cls;
24835 cfg.style = this.style;
24837 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24839 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24842 this.el.select('>button.close').on('click', this.hide, this);
24848 if (!this.rendered) {
24854 this.fireEvent('show', this);
24860 if (!this.rendered) {
24866 this.fireEvent('hide', this);
24869 update : function()
24871 // var e = this.el.dom.firstChild;
24873 // if(this.closable){
24874 // e = e.nextSibling;
24877 // e.data = this.html || '';
24879 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24895 * @class Roo.bootstrap.Graph
24896 * @extends Roo.bootstrap.Component
24897 * Bootstrap Graph class
24901 @cfg {String} graphtype bar | vbar | pie
24902 @cfg {number} g_x coodinator | centre x (pie)
24903 @cfg {number} g_y coodinator | centre y (pie)
24904 @cfg {number} g_r radius (pie)
24905 @cfg {number} g_height height of the chart (respected by all elements in the set)
24906 @cfg {number} g_width width of the chart (respected by all elements in the set)
24907 @cfg {Object} title The title of the chart
24910 -opts (object) options for the chart
24912 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24913 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24915 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.
24916 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24918 o stretch (boolean)
24920 -opts (object) options for the pie
24923 o startAngle (number)
24924 o endAngle (number)
24928 * Create a new Input
24929 * @param {Object} config The config object
24932 Roo.bootstrap.Graph = function(config){
24933 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24939 * The img click event for the img.
24940 * @param {Roo.EventObject} e
24946 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24957 //g_colors: this.colors,
24964 getAutoCreate : function(){
24975 onRender : function(ct,position){
24978 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24980 if (typeof(Raphael) == 'undefined') {
24981 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24985 this.raphael = Raphael(this.el.dom);
24987 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24988 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24989 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24990 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24992 r.text(160, 10, "Single Series Chart").attr(txtattr);
24993 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24994 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24995 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24997 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24998 r.barchart(330, 10, 300, 220, data1);
24999 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25000 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25003 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25004 // r.barchart(30, 30, 560, 250, xdata, {
25005 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25006 // axis : "0 0 1 1",
25007 // axisxlabels : xdata
25008 // //yvalues : cols,
25011 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25013 // this.load(null,xdata,{
25014 // axis : "0 0 1 1",
25015 // axisxlabels : xdata
25020 load : function(graphtype,xdata,opts)
25022 this.raphael.clear();
25024 graphtype = this.graphtype;
25029 var r = this.raphael,
25030 fin = function () {
25031 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25033 fout = function () {
25034 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25036 pfin = function() {
25037 this.sector.stop();
25038 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25041 this.label[0].stop();
25042 this.label[0].attr({ r: 7.5 });
25043 this.label[1].attr({ "font-weight": 800 });
25046 pfout = function() {
25047 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25050 this.label[0].animate({ r: 5 }, 500, "bounce");
25051 this.label[1].attr({ "font-weight": 400 });
25057 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25060 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25063 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25064 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25066 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25073 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25078 setTitle: function(o)
25083 initEvents: function() {
25086 this.el.on('click', this.onClick, this);
25090 onClick : function(e)
25092 Roo.log('img onclick');
25093 this.fireEvent('click', this, e);
25105 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25108 * @class Roo.bootstrap.dash.NumberBox
25109 * @extends Roo.bootstrap.Component
25110 * Bootstrap NumberBox class
25111 * @cfg {String} headline Box headline
25112 * @cfg {String} content Box content
25113 * @cfg {String} icon Box icon
25114 * @cfg {String} footer Footer text
25115 * @cfg {String} fhref Footer href
25118 * Create a new NumberBox
25119 * @param {Object} config The config object
25123 Roo.bootstrap.dash.NumberBox = function(config){
25124 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25128 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25137 getAutoCreate : function(){
25141 cls : 'small-box ',
25149 cls : 'roo-headline',
25150 html : this.headline
25154 cls : 'roo-content',
25155 html : this.content
25169 cls : 'ion ' + this.icon
25178 cls : 'small-box-footer',
25179 href : this.fhref || '#',
25183 cfg.cn.push(footer);
25190 onRender : function(ct,position){
25191 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25198 setHeadline: function (value)
25200 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25203 setFooter: function (value, href)
25205 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25208 this.el.select('a.small-box-footer',true).first().attr('href', href);
25213 setContent: function (value)
25215 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25218 initEvents: function()
25232 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25235 * @class Roo.bootstrap.dash.TabBox
25236 * @extends Roo.bootstrap.Component
25237 * Bootstrap TabBox class
25238 * @cfg {String} title Title of the TabBox
25239 * @cfg {String} icon Icon of the TabBox
25240 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25241 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25244 * Create a new TabBox
25245 * @param {Object} config The config object
25249 Roo.bootstrap.dash.TabBox = function(config){
25250 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25255 * When a pane is added
25256 * @param {Roo.bootstrap.dash.TabPane} pane
25260 * @event activatepane
25261 * When a pane is activated
25262 * @param {Roo.bootstrap.dash.TabPane} pane
25264 "activatepane" : true
25272 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25277 tabScrollable : false,
25279 getChildContainer : function()
25281 return this.el.select('.tab-content', true).first();
25284 getAutoCreate : function(){
25288 cls: 'pull-left header',
25296 cls: 'fa ' + this.icon
25302 cls: 'nav nav-tabs pull-right',
25308 if(this.tabScrollable){
25315 cls: 'nav nav-tabs pull-right',
25326 cls: 'nav-tabs-custom',
25331 cls: 'tab-content no-padding',
25339 initEvents : function()
25341 //Roo.log('add add pane handler');
25342 this.on('addpane', this.onAddPane, this);
25345 * Updates the box title
25346 * @param {String} html to set the title to.
25348 setTitle : function(value)
25350 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25352 onAddPane : function(pane)
25354 this.panes.push(pane);
25355 //Roo.log('addpane');
25357 // tabs are rendere left to right..
25358 if(!this.showtabs){
25362 var ctr = this.el.select('.nav-tabs', true).first();
25365 var existing = ctr.select('.nav-tab',true);
25366 var qty = existing.getCount();;
25369 var tab = ctr.createChild({
25371 cls : 'nav-tab' + (qty ? '' : ' active'),
25379 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25382 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25384 pane.el.addClass('active');
25389 onTabClick : function(ev,un,ob,pane)
25391 //Roo.log('tab - prev default');
25392 ev.preventDefault();
25395 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25396 pane.tab.addClass('active');
25397 //Roo.log(pane.title);
25398 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25399 // technically we should have a deactivate event.. but maybe add later.
25400 // and it should not de-activate the selected tab...
25401 this.fireEvent('activatepane', pane);
25402 pane.el.addClass('active');
25403 pane.fireEvent('activate');
25408 getActivePane : function()
25411 Roo.each(this.panes, function(p) {
25412 if(p.el.hasClass('active')){
25433 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25435 * @class Roo.bootstrap.TabPane
25436 * @extends Roo.bootstrap.Component
25437 * Bootstrap TabPane class
25438 * @cfg {Boolean} active (false | true) Default false
25439 * @cfg {String} title title of panel
25443 * Create a new TabPane
25444 * @param {Object} config The config object
25447 Roo.bootstrap.dash.TabPane = function(config){
25448 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25454 * When a pane is activated
25455 * @param {Roo.bootstrap.dash.TabPane} pane
25462 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25467 // the tabBox that this is attached to.
25470 getAutoCreate : function()
25478 cfg.cls += ' active';
25483 initEvents : function()
25485 //Roo.log('trigger add pane handler');
25486 this.parent().fireEvent('addpane', this)
25490 * Updates the tab title
25491 * @param {String} html to set the title to.
25493 setTitle: function(str)
25499 this.tab.select('a', true).first().dom.innerHTML = str;
25516 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25519 * @class Roo.bootstrap.menu.Menu
25520 * @extends Roo.bootstrap.Component
25521 * Bootstrap Menu class - container for Menu
25522 * @cfg {String} html Text of the menu
25523 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25524 * @cfg {String} icon Font awesome icon
25525 * @cfg {String} pos Menu align to (top | bottom) default bottom
25529 * Create a new Menu
25530 * @param {Object} config The config object
25534 Roo.bootstrap.menu.Menu = function(config){
25535 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25539 * @event beforeshow
25540 * Fires before this menu is displayed
25541 * @param {Roo.bootstrap.menu.Menu} this
25545 * @event beforehide
25546 * Fires before this menu is hidden
25547 * @param {Roo.bootstrap.menu.Menu} this
25552 * Fires after this menu is displayed
25553 * @param {Roo.bootstrap.menu.Menu} this
25558 * Fires after this menu is hidden
25559 * @param {Roo.bootstrap.menu.Menu} this
25564 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25565 * @param {Roo.bootstrap.menu.Menu} this
25566 * @param {Roo.EventObject} e
25573 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25577 weight : 'default',
25582 getChildContainer : function() {
25583 if(this.isSubMenu){
25587 return this.el.select('ul.dropdown-menu', true).first();
25590 getAutoCreate : function()
25595 cls : 'roo-menu-text',
25603 cls : 'fa ' + this.icon
25614 cls : 'dropdown-button btn btn-' + this.weight,
25619 cls : 'dropdown-toggle btn btn-' + this.weight,
25629 cls : 'dropdown-menu'
25635 if(this.pos == 'top'){
25636 cfg.cls += ' dropup';
25639 if(this.isSubMenu){
25642 cls : 'dropdown-menu'
25649 onRender : function(ct, position)
25651 this.isSubMenu = ct.hasClass('dropdown-submenu');
25653 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25656 initEvents : function()
25658 if(this.isSubMenu){
25662 this.hidden = true;
25664 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25665 this.triggerEl.on('click', this.onTriggerPress, this);
25667 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25668 this.buttonEl.on('click', this.onClick, this);
25674 if(this.isSubMenu){
25678 return this.el.select('ul.dropdown-menu', true).first();
25681 onClick : function(e)
25683 this.fireEvent("click", this, e);
25686 onTriggerPress : function(e)
25688 if (this.isVisible()) {
25695 isVisible : function(){
25696 return !this.hidden;
25701 this.fireEvent("beforeshow", this);
25703 this.hidden = false;
25704 this.el.addClass('open');
25706 Roo.get(document).on("mouseup", this.onMouseUp, this);
25708 this.fireEvent("show", this);
25715 this.fireEvent("beforehide", this);
25717 this.hidden = true;
25718 this.el.removeClass('open');
25720 Roo.get(document).un("mouseup", this.onMouseUp);
25722 this.fireEvent("hide", this);
25725 onMouseUp : function()
25739 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25742 * @class Roo.bootstrap.menu.Item
25743 * @extends Roo.bootstrap.Component
25744 * Bootstrap MenuItem class
25745 * @cfg {Boolean} submenu (true | false) default false
25746 * @cfg {String} html text of the item
25747 * @cfg {String} href the link
25748 * @cfg {Boolean} disable (true | false) default false
25749 * @cfg {Boolean} preventDefault (true | false) default true
25750 * @cfg {String} icon Font awesome icon
25751 * @cfg {String} pos Submenu align to (left | right) default right
25755 * Create a new Item
25756 * @param {Object} config The config object
25760 Roo.bootstrap.menu.Item = function(config){
25761 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25765 * Fires when the mouse is hovering over this menu
25766 * @param {Roo.bootstrap.menu.Item} this
25767 * @param {Roo.EventObject} e
25772 * Fires when the mouse exits this menu
25773 * @param {Roo.bootstrap.menu.Item} this
25774 * @param {Roo.EventObject} e
25780 * The raw click event for the entire grid.
25781 * @param {Roo.EventObject} e
25787 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25792 preventDefault: true,
25797 getAutoCreate : function()
25802 cls : 'roo-menu-item-text',
25810 cls : 'fa ' + this.icon
25819 href : this.href || '#',
25826 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25830 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25832 if(this.pos == 'left'){
25833 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25840 initEvents : function()
25842 this.el.on('mouseover', this.onMouseOver, this);
25843 this.el.on('mouseout', this.onMouseOut, this);
25845 this.el.select('a', true).first().on('click', this.onClick, this);
25849 onClick : function(e)
25851 if(this.preventDefault){
25852 e.preventDefault();
25855 this.fireEvent("click", this, e);
25858 onMouseOver : function(e)
25860 if(this.submenu && this.pos == 'left'){
25861 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25864 this.fireEvent("mouseover", this, e);
25867 onMouseOut : function(e)
25869 this.fireEvent("mouseout", this, e);
25881 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25884 * @class Roo.bootstrap.menu.Separator
25885 * @extends Roo.bootstrap.Component
25886 * Bootstrap Separator class
25889 * Create a new Separator
25890 * @param {Object} config The config object
25894 Roo.bootstrap.menu.Separator = function(config){
25895 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25898 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25900 getAutoCreate : function(){
25921 * @class Roo.bootstrap.Tooltip
25922 * Bootstrap Tooltip class
25923 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25924 * to determine which dom element triggers the tooltip.
25926 * It needs to add support for additional attributes like tooltip-position
25929 * Create a new Toolti
25930 * @param {Object} config The config object
25933 Roo.bootstrap.Tooltip = function(config){
25934 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25936 this.alignment = Roo.bootstrap.Tooltip.alignment;
25938 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25939 this.alignment = config.alignment;
25944 Roo.apply(Roo.bootstrap.Tooltip, {
25946 * @function init initialize tooltip monitoring.
25950 currentTip : false,
25951 currentRegion : false,
25957 Roo.get(document).on('mouseover', this.enter ,this);
25958 Roo.get(document).on('mouseout', this.leave, this);
25961 this.currentTip = new Roo.bootstrap.Tooltip();
25964 enter : function(ev)
25966 var dom = ev.getTarget();
25968 //Roo.log(['enter',dom]);
25969 var el = Roo.fly(dom);
25970 if (this.currentEl) {
25972 //Roo.log(this.currentEl);
25973 //Roo.log(this.currentEl.contains(dom));
25974 if (this.currentEl == el) {
25977 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25983 if (this.currentTip.el) {
25984 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25988 if(!el || el.dom == document){
25994 // you can not look for children, as if el is the body.. then everythign is the child..
25995 if (!el.attr('tooltip')) { //
25996 if (!el.select("[tooltip]").elements.length) {
25999 // is the mouse over this child...?
26000 bindEl = el.select("[tooltip]").first();
26001 var xy = ev.getXY();
26002 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26003 //Roo.log("not in region.");
26006 //Roo.log("child element over..");
26009 this.currentEl = bindEl;
26010 this.currentTip.bind(bindEl);
26011 this.currentRegion = Roo.lib.Region.getRegion(dom);
26012 this.currentTip.enter();
26015 leave : function(ev)
26017 var dom = ev.getTarget();
26018 //Roo.log(['leave',dom]);
26019 if (!this.currentEl) {
26024 if (dom != this.currentEl.dom) {
26027 var xy = ev.getXY();
26028 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26031 // only activate leave if mouse cursor is outside... bounding box..
26036 if (this.currentTip) {
26037 this.currentTip.leave();
26039 //Roo.log('clear currentEl');
26040 this.currentEl = false;
26045 'left' : ['r-l', [-2,0], 'right'],
26046 'right' : ['l-r', [2,0], 'left'],
26047 'bottom' : ['t-b', [0,2], 'top'],
26048 'top' : [ 'b-t', [0,-2], 'bottom']
26054 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26059 delay : null, // can be { show : 300 , hide: 500}
26063 hoverState : null, //???
26065 placement : 'bottom',
26069 getAutoCreate : function(){
26076 cls : 'tooltip-arrow'
26079 cls : 'tooltip-inner'
26086 bind : function(el)
26092 enter : function () {
26094 if (this.timeout != null) {
26095 clearTimeout(this.timeout);
26098 this.hoverState = 'in';
26099 //Roo.log("enter - show");
26100 if (!this.delay || !this.delay.show) {
26105 this.timeout = setTimeout(function () {
26106 if (_t.hoverState == 'in') {
26109 }, this.delay.show);
26113 clearTimeout(this.timeout);
26115 this.hoverState = 'out';
26116 if (!this.delay || !this.delay.hide) {
26122 this.timeout = setTimeout(function () {
26123 //Roo.log("leave - timeout");
26125 if (_t.hoverState == 'out') {
26127 Roo.bootstrap.Tooltip.currentEl = false;
26132 show : function (msg)
26135 this.render(document.body);
26138 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26140 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26142 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26144 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26146 var placement = typeof this.placement == 'function' ?
26147 this.placement.call(this, this.el, on_el) :
26150 var autoToken = /\s?auto?\s?/i;
26151 var autoPlace = autoToken.test(placement);
26153 placement = placement.replace(autoToken, '') || 'top';
26157 //this.el.setXY([0,0]);
26159 //this.el.dom.style.display='block';
26161 //this.el.appendTo(on_el);
26163 var p = this.getPosition();
26164 var box = this.el.getBox();
26170 var align = this.alignment[placement];
26172 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26174 if(placement == 'top' || placement == 'bottom'){
26176 placement = 'right';
26179 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26180 placement = 'left';
26183 var scroll = Roo.select('body', true).first().getScroll();
26185 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26191 this.el.alignTo(this.bindEl, align[0],align[1]);
26192 //var arrow = this.el.select('.arrow',true).first();
26193 //arrow.set(align[2],
26195 this.el.addClass(placement);
26197 this.el.addClass('in fade');
26199 this.hoverState = null;
26201 if (this.el.hasClass('fade')) {
26212 //this.el.setXY([0,0]);
26213 this.el.removeClass('in');
26229 * @class Roo.bootstrap.LocationPicker
26230 * @extends Roo.bootstrap.Component
26231 * Bootstrap LocationPicker class
26232 * @cfg {Number} latitude Position when init default 0
26233 * @cfg {Number} longitude Position when init default 0
26234 * @cfg {Number} zoom default 15
26235 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26236 * @cfg {Boolean} mapTypeControl default false
26237 * @cfg {Boolean} disableDoubleClickZoom default false
26238 * @cfg {Boolean} scrollwheel default true
26239 * @cfg {Boolean} streetViewControl default false
26240 * @cfg {Number} radius default 0
26241 * @cfg {String} locationName
26242 * @cfg {Boolean} draggable default true
26243 * @cfg {Boolean} enableAutocomplete default false
26244 * @cfg {Boolean} enableReverseGeocode default true
26245 * @cfg {String} markerTitle
26248 * Create a new LocationPicker
26249 * @param {Object} config The config object
26253 Roo.bootstrap.LocationPicker = function(config){
26255 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26260 * Fires when the picker initialized.
26261 * @param {Roo.bootstrap.LocationPicker} this
26262 * @param {Google Location} location
26266 * @event positionchanged
26267 * Fires when the picker position changed.
26268 * @param {Roo.bootstrap.LocationPicker} this
26269 * @param {Google Location} location
26271 positionchanged : true,
26274 * Fires when the map resize.
26275 * @param {Roo.bootstrap.LocationPicker} this
26280 * Fires when the map show.
26281 * @param {Roo.bootstrap.LocationPicker} this
26286 * Fires when the map hide.
26287 * @param {Roo.bootstrap.LocationPicker} this
26292 * Fires when click the map.
26293 * @param {Roo.bootstrap.LocationPicker} this
26294 * @param {Map event} e
26298 * @event mapRightClick
26299 * Fires when right click the map.
26300 * @param {Roo.bootstrap.LocationPicker} this
26301 * @param {Map event} e
26303 mapRightClick : true,
26305 * @event markerClick
26306 * Fires when click the marker.
26307 * @param {Roo.bootstrap.LocationPicker} this
26308 * @param {Map event} e
26310 markerClick : true,
26312 * @event markerRightClick
26313 * Fires when right click the marker.
26314 * @param {Roo.bootstrap.LocationPicker} this
26315 * @param {Map event} e
26317 markerRightClick : true,
26319 * @event OverlayViewDraw
26320 * Fires when OverlayView Draw
26321 * @param {Roo.bootstrap.LocationPicker} this
26323 OverlayViewDraw : true,
26325 * @event OverlayViewOnAdd
26326 * Fires when OverlayView Draw
26327 * @param {Roo.bootstrap.LocationPicker} this
26329 OverlayViewOnAdd : true,
26331 * @event OverlayViewOnRemove
26332 * Fires when OverlayView Draw
26333 * @param {Roo.bootstrap.LocationPicker} this
26335 OverlayViewOnRemove : true,
26337 * @event OverlayViewShow
26338 * Fires when OverlayView Draw
26339 * @param {Roo.bootstrap.LocationPicker} this
26340 * @param {Pixel} cpx
26342 OverlayViewShow : true,
26344 * @event OverlayViewHide
26345 * Fires when OverlayView Draw
26346 * @param {Roo.bootstrap.LocationPicker} this
26348 OverlayViewHide : true,
26350 * @event loadexception
26351 * Fires when load google lib failed.
26352 * @param {Roo.bootstrap.LocationPicker} this
26354 loadexception : true
26359 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26361 gMapContext: false,
26367 mapTypeControl: false,
26368 disableDoubleClickZoom: false,
26370 streetViewControl: false,
26374 enableAutocomplete: false,
26375 enableReverseGeocode: true,
26378 getAutoCreate: function()
26383 cls: 'roo-location-picker'
26389 initEvents: function(ct, position)
26391 if(!this.el.getWidth() || this.isApplied()){
26395 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26400 initial: function()
26402 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26403 this.fireEvent('loadexception', this);
26407 if(!this.mapTypeId){
26408 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26411 this.gMapContext = this.GMapContext();
26413 this.initOverlayView();
26415 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26419 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26420 _this.setPosition(_this.gMapContext.marker.position);
26423 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26424 _this.fireEvent('mapClick', this, event);
26428 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26429 _this.fireEvent('mapRightClick', this, event);
26433 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26434 _this.fireEvent('markerClick', this, event);
26438 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26439 _this.fireEvent('markerRightClick', this, event);
26443 this.setPosition(this.gMapContext.location);
26445 this.fireEvent('initial', this, this.gMapContext.location);
26448 initOverlayView: function()
26452 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26456 _this.fireEvent('OverlayViewDraw', _this);
26461 _this.fireEvent('OverlayViewOnAdd', _this);
26464 onRemove: function()
26466 _this.fireEvent('OverlayViewOnRemove', _this);
26469 show: function(cpx)
26471 _this.fireEvent('OverlayViewShow', _this, cpx);
26476 _this.fireEvent('OverlayViewHide', _this);
26482 fromLatLngToContainerPixel: function(event)
26484 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26487 isApplied: function()
26489 return this.getGmapContext() == false ? false : true;
26492 getGmapContext: function()
26494 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26497 GMapContext: function()
26499 var position = new google.maps.LatLng(this.latitude, this.longitude);
26501 var _map = new google.maps.Map(this.el.dom, {
26504 mapTypeId: this.mapTypeId,
26505 mapTypeControl: this.mapTypeControl,
26506 disableDoubleClickZoom: this.disableDoubleClickZoom,
26507 scrollwheel: this.scrollwheel,
26508 streetViewControl: this.streetViewControl,
26509 locationName: this.locationName,
26510 draggable: this.draggable,
26511 enableAutocomplete: this.enableAutocomplete,
26512 enableReverseGeocode: this.enableReverseGeocode
26515 var _marker = new google.maps.Marker({
26516 position: position,
26518 title: this.markerTitle,
26519 draggable: this.draggable
26526 location: position,
26527 radius: this.radius,
26528 locationName: this.locationName,
26529 addressComponents: {
26530 formatted_address: null,
26531 addressLine1: null,
26532 addressLine2: null,
26534 streetNumber: null,
26538 stateOrProvince: null
26541 domContainer: this.el.dom,
26542 geodecoder: new google.maps.Geocoder()
26546 drawCircle: function(center, radius, options)
26548 if (this.gMapContext.circle != null) {
26549 this.gMapContext.circle.setMap(null);
26553 options = Roo.apply({}, options, {
26554 strokeColor: "#0000FF",
26555 strokeOpacity: .35,
26557 fillColor: "#0000FF",
26561 options.map = this.gMapContext.map;
26562 options.radius = radius;
26563 options.center = center;
26564 this.gMapContext.circle = new google.maps.Circle(options);
26565 return this.gMapContext.circle;
26571 setPosition: function(location)
26573 this.gMapContext.location = location;
26574 this.gMapContext.marker.setPosition(location);
26575 this.gMapContext.map.panTo(location);
26576 this.drawCircle(location, this.gMapContext.radius, {});
26580 if (this.gMapContext.settings.enableReverseGeocode) {
26581 this.gMapContext.geodecoder.geocode({
26582 latLng: this.gMapContext.location
26583 }, function(results, status) {
26585 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26586 _this.gMapContext.locationName = results[0].formatted_address;
26587 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26589 _this.fireEvent('positionchanged', this, location);
26596 this.fireEvent('positionchanged', this, location);
26601 google.maps.event.trigger(this.gMapContext.map, "resize");
26603 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26605 this.fireEvent('resize', this);
26608 setPositionByLatLng: function(latitude, longitude)
26610 this.setPosition(new google.maps.LatLng(latitude, longitude));
26613 getCurrentPosition: function()
26616 latitude: this.gMapContext.location.lat(),
26617 longitude: this.gMapContext.location.lng()
26621 getAddressName: function()
26623 return this.gMapContext.locationName;
26626 getAddressComponents: function()
26628 return this.gMapContext.addressComponents;
26631 address_component_from_google_geocode: function(address_components)
26635 for (var i = 0; i < address_components.length; i++) {
26636 var component = address_components[i];
26637 if (component.types.indexOf("postal_code") >= 0) {
26638 result.postalCode = component.short_name;
26639 } else if (component.types.indexOf("street_number") >= 0) {
26640 result.streetNumber = component.short_name;
26641 } else if (component.types.indexOf("route") >= 0) {
26642 result.streetName = component.short_name;
26643 } else if (component.types.indexOf("neighborhood") >= 0) {
26644 result.city = component.short_name;
26645 } else if (component.types.indexOf("locality") >= 0) {
26646 result.city = component.short_name;
26647 } else if (component.types.indexOf("sublocality") >= 0) {
26648 result.district = component.short_name;
26649 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26650 result.stateOrProvince = component.short_name;
26651 } else if (component.types.indexOf("country") >= 0) {
26652 result.country = component.short_name;
26656 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26657 result.addressLine2 = "";
26661 setZoomLevel: function(zoom)
26663 this.gMapContext.map.setZoom(zoom);
26676 this.fireEvent('show', this);
26687 this.fireEvent('hide', this);
26692 Roo.apply(Roo.bootstrap.LocationPicker, {
26694 OverlayView : function(map, options)
26696 options = options || {};
26710 * @class Roo.bootstrap.Alert
26711 * @extends Roo.bootstrap.Component
26712 * Bootstrap Alert class
26713 * @cfg {String} title The title of alert
26714 * @cfg {String} html The content of alert
26715 * @cfg {String} weight ( success | info | warning | danger )
26716 * @cfg {String} faicon font-awesomeicon
26719 * Create a new alert
26720 * @param {Object} config The config object
26724 Roo.bootstrap.Alert = function(config){
26725 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26729 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26736 getAutoCreate : function()
26745 cls : 'roo-alert-icon'
26750 cls : 'roo-alert-title',
26755 cls : 'roo-alert-text',
26762 cfg.cn[0].cls += ' fa ' + this.faicon;
26766 cfg.cls += ' alert-' + this.weight;
26772 initEvents: function()
26774 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26777 setTitle : function(str)
26779 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26782 setText : function(str)
26784 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26787 setWeight : function(weight)
26790 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26793 this.weight = weight;
26795 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26798 setIcon : function(icon)
26801 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26804 this.faicon = icon;
26806 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26827 * @class Roo.bootstrap.UploadCropbox
26828 * @extends Roo.bootstrap.Component
26829 * Bootstrap UploadCropbox class
26830 * @cfg {String} emptyText show when image has been loaded
26831 * @cfg {String} rotateNotify show when image too small to rotate
26832 * @cfg {Number} errorTimeout default 3000
26833 * @cfg {Number} minWidth default 300
26834 * @cfg {Number} minHeight default 300
26835 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26836 * @cfg {Boolean} isDocument (true|false) default false
26837 * @cfg {String} url action url
26838 * @cfg {String} paramName default 'imageUpload'
26839 * @cfg {String} method default POST
26840 * @cfg {Boolean} loadMask (true|false) default true
26841 * @cfg {Boolean} loadingText default 'Loading...'
26844 * Create a new UploadCropbox
26845 * @param {Object} config The config object
26848 Roo.bootstrap.UploadCropbox = function(config){
26849 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26853 * @event beforeselectfile
26854 * Fire before select file
26855 * @param {Roo.bootstrap.UploadCropbox} this
26857 "beforeselectfile" : true,
26860 * Fire after initEvent
26861 * @param {Roo.bootstrap.UploadCropbox} this
26866 * Fire after initEvent
26867 * @param {Roo.bootstrap.UploadCropbox} this
26868 * @param {String} data
26873 * Fire when preparing the file data
26874 * @param {Roo.bootstrap.UploadCropbox} this
26875 * @param {Object} file
26880 * Fire when get exception
26881 * @param {Roo.bootstrap.UploadCropbox} this
26882 * @param {XMLHttpRequest} xhr
26884 "exception" : true,
26886 * @event beforeloadcanvas
26887 * Fire before load the canvas
26888 * @param {Roo.bootstrap.UploadCropbox} this
26889 * @param {String} src
26891 "beforeloadcanvas" : true,
26894 * Fire when trash image
26895 * @param {Roo.bootstrap.UploadCropbox} this
26900 * Fire when download the image
26901 * @param {Roo.bootstrap.UploadCropbox} this
26905 * @event footerbuttonclick
26906 * Fire when footerbuttonclick
26907 * @param {Roo.bootstrap.UploadCropbox} this
26908 * @param {String} type
26910 "footerbuttonclick" : true,
26914 * @param {Roo.bootstrap.UploadCropbox} this
26919 * Fire when rotate the image
26920 * @param {Roo.bootstrap.UploadCropbox} this
26921 * @param {String} pos
26926 * Fire when inspect the file
26927 * @param {Roo.bootstrap.UploadCropbox} this
26928 * @param {Object} file
26933 * Fire when xhr upload the file
26934 * @param {Roo.bootstrap.UploadCropbox} this
26935 * @param {Object} data
26940 * Fire when arrange the file data
26941 * @param {Roo.bootstrap.UploadCropbox} this
26942 * @param {Object} formData
26947 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26950 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26952 emptyText : 'Click to upload image',
26953 rotateNotify : 'Image is too small to rotate',
26954 errorTimeout : 3000,
26968 cropType : 'image/jpeg',
26970 canvasLoaded : false,
26971 isDocument : false,
26973 paramName : 'imageUpload',
26975 loadingText : 'Loading...',
26978 getAutoCreate : function()
26982 cls : 'roo-upload-cropbox',
26986 cls : 'roo-upload-cropbox-selector',
26991 cls : 'roo-upload-cropbox-body',
26992 style : 'cursor:pointer',
26996 cls : 'roo-upload-cropbox-preview'
27000 cls : 'roo-upload-cropbox-thumb'
27004 cls : 'roo-upload-cropbox-empty-notify',
27005 html : this.emptyText
27009 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27010 html : this.rotateNotify
27016 cls : 'roo-upload-cropbox-footer',
27019 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27029 onRender : function(ct, position)
27031 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27033 if (this.buttons.length) {
27035 Roo.each(this.buttons, function(bb) {
27037 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27039 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27045 this.maskEl = this.el;
27049 initEvents : function()
27051 this.urlAPI = (window.createObjectURL && window) ||
27052 (window.URL && URL.revokeObjectURL && URL) ||
27053 (window.webkitURL && webkitURL);
27055 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27056 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27058 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27059 this.selectorEl.hide();
27061 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27062 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27064 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27065 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27066 this.thumbEl.hide();
27068 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27069 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27071 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27072 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073 this.errorEl.hide();
27075 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27076 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27077 this.footerEl.hide();
27079 this.setThumbBoxSize();
27085 this.fireEvent('initial', this);
27092 window.addEventListener("resize", function() { _this.resize(); } );
27094 this.bodyEl.on('click', this.beforeSelectFile, this);
27097 this.bodyEl.on('touchstart', this.onTouchStart, this);
27098 this.bodyEl.on('touchmove', this.onTouchMove, this);
27099 this.bodyEl.on('touchend', this.onTouchEnd, this);
27103 this.bodyEl.on('mousedown', this.onMouseDown, this);
27104 this.bodyEl.on('mousemove', this.onMouseMove, this);
27105 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27106 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27107 Roo.get(document).on('mouseup', this.onMouseUp, this);
27110 this.selectorEl.on('change', this.onFileSelected, this);
27116 this.baseScale = 1;
27118 this.baseRotate = 1;
27119 this.dragable = false;
27120 this.pinching = false;
27123 this.cropData = false;
27124 this.notifyEl.dom.innerHTML = this.emptyText;
27126 this.selectorEl.dom.value = '';
27130 resize : function()
27132 if(this.fireEvent('resize', this) != false){
27133 this.setThumbBoxPosition();
27134 this.setCanvasPosition();
27138 onFooterButtonClick : function(e, el, o, type)
27141 case 'rotate-left' :
27142 this.onRotateLeft(e);
27144 case 'rotate-right' :
27145 this.onRotateRight(e);
27148 this.beforeSelectFile(e);
27163 this.fireEvent('footerbuttonclick', this, type);
27166 beforeSelectFile : function(e)
27168 e.preventDefault();
27170 if(this.fireEvent('beforeselectfile', this) != false){
27171 this.selectorEl.dom.click();
27175 onFileSelected : function(e)
27177 e.preventDefault();
27179 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27183 var file = this.selectorEl.dom.files[0];
27185 if(this.fireEvent('inspect', this, file) != false){
27186 this.prepare(file);
27191 trash : function(e)
27193 this.fireEvent('trash', this);
27196 download : function(e)
27198 this.fireEvent('download', this);
27201 loadCanvas : function(src)
27203 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27207 this.imageEl = document.createElement('img');
27211 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27213 this.imageEl.src = src;
27217 onLoadCanvas : function()
27219 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27220 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27222 this.bodyEl.un('click', this.beforeSelectFile, this);
27224 this.notifyEl.hide();
27225 this.thumbEl.show();
27226 this.footerEl.show();
27228 this.baseRotateLevel();
27230 if(this.isDocument){
27231 this.setThumbBoxSize();
27234 this.setThumbBoxPosition();
27236 this.baseScaleLevel();
27242 this.canvasLoaded = true;
27245 this.maskEl.unmask();
27250 setCanvasPosition : function()
27252 if(!this.canvasEl){
27256 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27257 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27259 this.previewEl.setLeft(pw);
27260 this.previewEl.setTop(ph);
27264 onMouseDown : function(e)
27268 this.dragable = true;
27269 this.pinching = false;
27271 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27272 this.dragable = false;
27276 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27277 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27281 onMouseMove : function(e)
27285 if(!this.canvasLoaded){
27289 if (!this.dragable){
27293 var minX = Math.ceil(this.thumbEl.getLeft(true));
27294 var minY = Math.ceil(this.thumbEl.getTop(true));
27296 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27297 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27299 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27300 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27302 x = x - this.mouseX;
27303 y = y - this.mouseY;
27305 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27306 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27308 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27309 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27311 this.previewEl.setLeft(bgX);
27312 this.previewEl.setTop(bgY);
27314 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27315 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27318 onMouseUp : function(e)
27322 this.dragable = false;
27325 onMouseWheel : function(e)
27329 this.startScale = this.scale;
27331 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27333 if(!this.zoomable()){
27334 this.scale = this.startScale;
27343 zoomable : function()
27345 var minScale = this.thumbEl.getWidth() / this.minWidth;
27347 if(this.minWidth < this.minHeight){
27348 minScale = this.thumbEl.getHeight() / this.minHeight;
27351 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27352 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27356 (this.rotate == 0 || this.rotate == 180) &&
27358 width > this.imageEl.OriginWidth ||
27359 height > this.imageEl.OriginHeight ||
27360 (width < this.minWidth && height < this.minHeight)
27368 (this.rotate == 90 || this.rotate == 270) &&
27370 width > this.imageEl.OriginWidth ||
27371 height > this.imageEl.OriginHeight ||
27372 (width < this.minHeight && height < this.minWidth)
27379 !this.isDocument &&
27380 (this.rotate == 0 || this.rotate == 180) &&
27382 width < this.minWidth ||
27383 width > this.imageEl.OriginWidth ||
27384 height < this.minHeight ||
27385 height > this.imageEl.OriginHeight
27392 !this.isDocument &&
27393 (this.rotate == 90 || this.rotate == 270) &&
27395 width < this.minHeight ||
27396 width > this.imageEl.OriginWidth ||
27397 height < this.minWidth ||
27398 height > this.imageEl.OriginHeight
27408 onRotateLeft : function(e)
27410 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27412 var minScale = this.thumbEl.getWidth() / this.minWidth;
27414 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27415 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27417 this.startScale = this.scale;
27419 while (this.getScaleLevel() < minScale){
27421 this.scale = this.scale + 1;
27423 if(!this.zoomable()){
27428 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27429 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27434 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27441 this.scale = this.startScale;
27443 this.onRotateFail();
27448 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27450 if(this.isDocument){
27451 this.setThumbBoxSize();
27452 this.setThumbBoxPosition();
27453 this.setCanvasPosition();
27458 this.fireEvent('rotate', this, 'left');
27462 onRotateRight : function(e)
27464 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27466 var minScale = this.thumbEl.getWidth() / this.minWidth;
27468 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27469 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27471 this.startScale = this.scale;
27473 while (this.getScaleLevel() < minScale){
27475 this.scale = this.scale + 1;
27477 if(!this.zoomable()){
27482 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27483 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27488 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27495 this.scale = this.startScale;
27497 this.onRotateFail();
27502 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27504 if(this.isDocument){
27505 this.setThumbBoxSize();
27506 this.setThumbBoxPosition();
27507 this.setCanvasPosition();
27512 this.fireEvent('rotate', this, 'right');
27515 onRotateFail : function()
27517 this.errorEl.show(true);
27521 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27526 this.previewEl.dom.innerHTML = '';
27528 var canvasEl = document.createElement("canvas");
27530 var contextEl = canvasEl.getContext("2d");
27532 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27533 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27534 var center = this.imageEl.OriginWidth / 2;
27536 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27537 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27538 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27539 center = this.imageEl.OriginHeight / 2;
27542 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27544 contextEl.translate(center, center);
27545 contextEl.rotate(this.rotate * Math.PI / 180);
27547 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27549 this.canvasEl = document.createElement("canvas");
27551 this.contextEl = this.canvasEl.getContext("2d");
27553 switch (this.rotate) {
27556 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27557 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27559 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27564 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27565 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27567 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27568 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);
27572 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27577 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27578 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27580 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27581 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);
27585 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);
27590 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27591 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27593 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27594 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27598 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);
27605 this.previewEl.appendChild(this.canvasEl);
27607 this.setCanvasPosition();
27612 if(!this.canvasLoaded){
27616 var imageCanvas = document.createElement("canvas");
27618 var imageContext = imageCanvas.getContext("2d");
27620 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27621 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27623 var center = imageCanvas.width / 2;
27625 imageContext.translate(center, center);
27627 imageContext.rotate(this.rotate * Math.PI / 180);
27629 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27631 var canvas = document.createElement("canvas");
27633 var context = canvas.getContext("2d");
27635 canvas.width = this.minWidth;
27636 canvas.height = this.minHeight;
27638 switch (this.rotate) {
27641 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27642 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27644 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27645 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27647 var targetWidth = this.minWidth - 2 * x;
27648 var targetHeight = this.minHeight - 2 * y;
27652 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27653 scale = targetWidth / width;
27656 if(x > 0 && y == 0){
27657 scale = targetHeight / height;
27660 if(x > 0 && y > 0){
27661 scale = targetWidth / width;
27663 if(width < height){
27664 scale = targetHeight / height;
27668 context.scale(scale, scale);
27670 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27671 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27673 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27674 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27676 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27681 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27682 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27684 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27685 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27687 var targetWidth = this.minWidth - 2 * x;
27688 var targetHeight = this.minHeight - 2 * y;
27692 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27693 scale = targetWidth / width;
27696 if(x > 0 && y == 0){
27697 scale = targetHeight / height;
27700 if(x > 0 && y > 0){
27701 scale = targetWidth / width;
27703 if(width < height){
27704 scale = targetHeight / height;
27708 context.scale(scale, scale);
27710 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27711 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27713 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27714 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27716 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27718 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27723 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27724 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27726 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27727 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27729 var targetWidth = this.minWidth - 2 * x;
27730 var targetHeight = this.minHeight - 2 * y;
27734 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27735 scale = targetWidth / width;
27738 if(x > 0 && y == 0){
27739 scale = targetHeight / height;
27742 if(x > 0 && y > 0){
27743 scale = targetWidth / width;
27745 if(width < height){
27746 scale = targetHeight / height;
27750 context.scale(scale, scale);
27752 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27753 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27755 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27756 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27758 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27759 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27761 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27766 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27767 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27769 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27770 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27772 var targetWidth = this.minWidth - 2 * x;
27773 var targetHeight = this.minHeight - 2 * y;
27777 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27778 scale = targetWidth / width;
27781 if(x > 0 && y == 0){
27782 scale = targetHeight / height;
27785 if(x > 0 && y > 0){
27786 scale = targetWidth / width;
27788 if(width < height){
27789 scale = targetHeight / height;
27793 context.scale(scale, scale);
27795 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27796 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27798 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27799 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27801 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27803 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27810 this.cropData = canvas.toDataURL(this.cropType);
27812 if(this.fireEvent('crop', this, this.cropData) !== false){
27813 this.process(this.file, this.cropData);
27820 setThumbBoxSize : function()
27824 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27825 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27826 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27828 this.minWidth = width;
27829 this.minHeight = height;
27831 if(this.rotate == 90 || this.rotate == 270){
27832 this.minWidth = height;
27833 this.minHeight = width;
27838 width = Math.ceil(this.minWidth * height / this.minHeight);
27840 if(this.minWidth > this.minHeight){
27842 height = Math.ceil(this.minHeight * width / this.minWidth);
27845 this.thumbEl.setStyle({
27846 width : width + 'px',
27847 height : height + 'px'
27854 setThumbBoxPosition : function()
27856 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27857 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27859 this.thumbEl.setLeft(x);
27860 this.thumbEl.setTop(y);
27864 baseRotateLevel : function()
27866 this.baseRotate = 1;
27869 typeof(this.exif) != 'undefined' &&
27870 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27871 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27873 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27876 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27880 baseScaleLevel : function()
27884 if(this.isDocument){
27886 if(this.baseRotate == 6 || this.baseRotate == 8){
27888 height = this.thumbEl.getHeight();
27889 this.baseScale = height / this.imageEl.OriginWidth;
27891 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27892 width = this.thumbEl.getWidth();
27893 this.baseScale = width / this.imageEl.OriginHeight;
27899 height = this.thumbEl.getHeight();
27900 this.baseScale = height / this.imageEl.OriginHeight;
27902 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27903 width = this.thumbEl.getWidth();
27904 this.baseScale = width / this.imageEl.OriginWidth;
27910 if(this.baseRotate == 6 || this.baseRotate == 8){
27912 width = this.thumbEl.getHeight();
27913 this.baseScale = width / this.imageEl.OriginHeight;
27915 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27916 height = this.thumbEl.getWidth();
27917 this.baseScale = height / this.imageEl.OriginHeight;
27920 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27921 height = this.thumbEl.getWidth();
27922 this.baseScale = height / this.imageEl.OriginHeight;
27924 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27925 width = this.thumbEl.getHeight();
27926 this.baseScale = width / this.imageEl.OriginWidth;
27933 width = this.thumbEl.getWidth();
27934 this.baseScale = width / this.imageEl.OriginWidth;
27936 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27937 height = this.thumbEl.getHeight();
27938 this.baseScale = height / this.imageEl.OriginHeight;
27941 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27943 height = this.thumbEl.getHeight();
27944 this.baseScale = height / this.imageEl.OriginHeight;
27946 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27947 width = this.thumbEl.getWidth();
27948 this.baseScale = width / this.imageEl.OriginWidth;
27956 getScaleLevel : function()
27958 return this.baseScale * Math.pow(1.1, this.scale);
27961 onTouchStart : function(e)
27963 if(!this.canvasLoaded){
27964 this.beforeSelectFile(e);
27968 var touches = e.browserEvent.touches;
27974 if(touches.length == 1){
27975 this.onMouseDown(e);
27979 if(touches.length != 2){
27985 for(var i = 0, finger; finger = touches[i]; i++){
27986 coords.push(finger.pageX, finger.pageY);
27989 var x = Math.pow(coords[0] - coords[2], 2);
27990 var y = Math.pow(coords[1] - coords[3], 2);
27992 this.startDistance = Math.sqrt(x + y);
27994 this.startScale = this.scale;
27996 this.pinching = true;
27997 this.dragable = false;
28001 onTouchMove : function(e)
28003 if(!this.pinching && !this.dragable){
28007 var touches = e.browserEvent.touches;
28014 this.onMouseMove(e);
28020 for(var i = 0, finger; finger = touches[i]; i++){
28021 coords.push(finger.pageX, finger.pageY);
28024 var x = Math.pow(coords[0] - coords[2], 2);
28025 var y = Math.pow(coords[1] - coords[3], 2);
28027 this.endDistance = Math.sqrt(x + y);
28029 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28031 if(!this.zoomable()){
28032 this.scale = this.startScale;
28040 onTouchEnd : function(e)
28042 this.pinching = false;
28043 this.dragable = false;
28047 process : function(file, crop)
28050 this.maskEl.mask(this.loadingText);
28053 this.xhr = new XMLHttpRequest();
28055 file.xhr = this.xhr;
28057 this.xhr.open(this.method, this.url, true);
28060 "Accept": "application/json",
28061 "Cache-Control": "no-cache",
28062 "X-Requested-With": "XMLHttpRequest"
28065 for (var headerName in headers) {
28066 var headerValue = headers[headerName];
28068 this.xhr.setRequestHeader(headerName, headerValue);
28074 this.xhr.onload = function()
28076 _this.xhrOnLoad(_this.xhr);
28079 this.xhr.onerror = function()
28081 _this.xhrOnError(_this.xhr);
28084 var formData = new FormData();
28086 formData.append('returnHTML', 'NO');
28089 formData.append('crop', crop);
28092 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28093 formData.append(this.paramName, file, file.name);
28096 if(typeof(file.filename) != 'undefined'){
28097 formData.append('filename', file.filename);
28100 if(typeof(file.mimetype) != 'undefined'){
28101 formData.append('mimetype', file.mimetype);
28104 if(this.fireEvent('arrange', this, formData) != false){
28105 this.xhr.send(formData);
28109 xhrOnLoad : function(xhr)
28112 this.maskEl.unmask();
28115 if (xhr.readyState !== 4) {
28116 this.fireEvent('exception', this, xhr);
28120 var response = Roo.decode(xhr.responseText);
28122 if(!response.success){
28123 this.fireEvent('exception', this, xhr);
28127 var response = Roo.decode(xhr.responseText);
28129 this.fireEvent('upload', this, response);
28133 xhrOnError : function()
28136 this.maskEl.unmask();
28139 Roo.log('xhr on error');
28141 var response = Roo.decode(xhr.responseText);
28147 prepare : function(file)
28150 this.maskEl.mask(this.loadingText);
28156 if(typeof(file) === 'string'){
28157 this.loadCanvas(file);
28161 if(!file || !this.urlAPI){
28166 this.cropType = file.type;
28170 if(this.fireEvent('prepare', this, this.file) != false){
28172 var reader = new FileReader();
28174 reader.onload = function (e) {
28175 if (e.target.error) {
28176 Roo.log(e.target.error);
28180 var buffer = e.target.result,
28181 dataView = new DataView(buffer),
28183 maxOffset = dataView.byteLength - 4,
28187 if (dataView.getUint16(0) === 0xffd8) {
28188 while (offset < maxOffset) {
28189 markerBytes = dataView.getUint16(offset);
28191 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28192 markerLength = dataView.getUint16(offset + 2) + 2;
28193 if (offset + markerLength > dataView.byteLength) {
28194 Roo.log('Invalid meta data: Invalid segment size.');
28198 if(markerBytes == 0xffe1){
28199 _this.parseExifData(
28206 offset += markerLength;
28216 var url = _this.urlAPI.createObjectURL(_this.file);
28218 _this.loadCanvas(url);
28223 reader.readAsArrayBuffer(this.file);
28229 parseExifData : function(dataView, offset, length)
28231 var tiffOffset = offset + 10,
28235 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28236 // No Exif data, might be XMP data instead
28240 // Check for the ASCII code for "Exif" (0x45786966):
28241 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28242 // No Exif data, might be XMP data instead
28245 if (tiffOffset + 8 > dataView.byteLength) {
28246 Roo.log('Invalid Exif data: Invalid segment size.');
28249 // Check for the two null bytes:
28250 if (dataView.getUint16(offset + 8) !== 0x0000) {
28251 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28254 // Check the byte alignment:
28255 switch (dataView.getUint16(tiffOffset)) {
28257 littleEndian = true;
28260 littleEndian = false;
28263 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28266 // Check for the TIFF tag marker (0x002A):
28267 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28268 Roo.log('Invalid Exif data: Missing TIFF marker.');
28271 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28272 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28274 this.parseExifTags(
28277 tiffOffset + dirOffset,
28282 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28287 if (dirOffset + 6 > dataView.byteLength) {
28288 Roo.log('Invalid Exif data: Invalid directory offset.');
28291 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28292 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28293 if (dirEndOffset + 4 > dataView.byteLength) {
28294 Roo.log('Invalid Exif data: Invalid directory size.');
28297 for (i = 0; i < tagsNumber; i += 1) {
28301 dirOffset + 2 + 12 * i, // tag offset
28305 // Return the offset to the next directory:
28306 return dataView.getUint32(dirEndOffset, littleEndian);
28309 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28311 var tag = dataView.getUint16(offset, littleEndian);
28313 this.exif[tag] = this.getExifValue(
28317 dataView.getUint16(offset + 2, littleEndian), // tag type
28318 dataView.getUint32(offset + 4, littleEndian), // tag length
28323 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28325 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28334 Roo.log('Invalid Exif data: Invalid tag type.');
28338 tagSize = tagType.size * length;
28339 // Determine if the value is contained in the dataOffset bytes,
28340 // or if the value at the dataOffset is a pointer to the actual data:
28341 dataOffset = tagSize > 4 ?
28342 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28343 if (dataOffset + tagSize > dataView.byteLength) {
28344 Roo.log('Invalid Exif data: Invalid data offset.');
28347 if (length === 1) {
28348 return tagType.getValue(dataView, dataOffset, littleEndian);
28351 for (i = 0; i < length; i += 1) {
28352 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28355 if (tagType.ascii) {
28357 // Concatenate the chars:
28358 for (i = 0; i < values.length; i += 1) {
28360 // Ignore the terminating NULL byte(s):
28361 if (c === '\u0000') {
28373 Roo.apply(Roo.bootstrap.UploadCropbox, {
28375 'Orientation': 0x0112
28379 1: 0, //'top-left',
28381 3: 180, //'bottom-right',
28382 // 4: 'bottom-left',
28384 6: 90, //'right-top',
28385 // 7: 'right-bottom',
28386 8: 270 //'left-bottom'
28390 // byte, 8-bit unsigned int:
28392 getValue: function (dataView, dataOffset) {
28393 return dataView.getUint8(dataOffset);
28397 // ascii, 8-bit byte:
28399 getValue: function (dataView, dataOffset) {
28400 return String.fromCharCode(dataView.getUint8(dataOffset));
28405 // short, 16 bit int:
28407 getValue: function (dataView, dataOffset, littleEndian) {
28408 return dataView.getUint16(dataOffset, littleEndian);
28412 // long, 32 bit int:
28414 getValue: function (dataView, dataOffset, littleEndian) {
28415 return dataView.getUint32(dataOffset, littleEndian);
28419 // rational = two long values, first is numerator, second is denominator:
28421 getValue: function (dataView, dataOffset, littleEndian) {
28422 return dataView.getUint32(dataOffset, littleEndian) /
28423 dataView.getUint32(dataOffset + 4, littleEndian);
28427 // slong, 32 bit signed int:
28429 getValue: function (dataView, dataOffset, littleEndian) {
28430 return dataView.getInt32(dataOffset, littleEndian);
28434 // srational, two slongs, first is numerator, second is denominator:
28436 getValue: function (dataView, dataOffset, littleEndian) {
28437 return dataView.getInt32(dataOffset, littleEndian) /
28438 dataView.getInt32(dataOffset + 4, littleEndian);
28448 cls : 'btn-group roo-upload-cropbox-rotate-left',
28449 action : 'rotate-left',
28453 cls : 'btn btn-default',
28454 html : '<i class="fa fa-undo"></i>'
28460 cls : 'btn-group roo-upload-cropbox-picture',
28461 action : 'picture',
28465 cls : 'btn btn-default',
28466 html : '<i class="fa fa-picture-o"></i>'
28472 cls : 'btn-group roo-upload-cropbox-rotate-right',
28473 action : 'rotate-right',
28477 cls : 'btn btn-default',
28478 html : '<i class="fa fa-repeat"></i>'
28486 cls : 'btn-group roo-upload-cropbox-rotate-left',
28487 action : 'rotate-left',
28491 cls : 'btn btn-default',
28492 html : '<i class="fa fa-undo"></i>'
28498 cls : 'btn-group roo-upload-cropbox-download',
28499 action : 'download',
28503 cls : 'btn btn-default',
28504 html : '<i class="fa fa-download"></i>'
28510 cls : 'btn-group roo-upload-cropbox-crop',
28515 cls : 'btn btn-default',
28516 html : '<i class="fa fa-crop"></i>'
28522 cls : 'btn-group roo-upload-cropbox-trash',
28527 cls : 'btn btn-default',
28528 html : '<i class="fa fa-trash"></i>'
28534 cls : 'btn-group roo-upload-cropbox-rotate-right',
28535 action : 'rotate-right',
28539 cls : 'btn btn-default',
28540 html : '<i class="fa fa-repeat"></i>'
28548 cls : 'btn-group roo-upload-cropbox-rotate-left',
28549 action : 'rotate-left',
28553 cls : 'btn btn-default',
28554 html : '<i class="fa fa-undo"></i>'
28560 cls : 'btn-group roo-upload-cropbox-rotate-right',
28561 action : 'rotate-right',
28565 cls : 'btn btn-default',
28566 html : '<i class="fa fa-repeat"></i>'
28579 * @class Roo.bootstrap.DocumentManager
28580 * @extends Roo.bootstrap.Component
28581 * Bootstrap DocumentManager class
28582 * @cfg {String} paramName default 'imageUpload'
28583 * @cfg {String} toolTipName default 'filename'
28584 * @cfg {String} method default POST
28585 * @cfg {String} url action url
28586 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28587 * @cfg {Boolean} multiple multiple upload default true
28588 * @cfg {Number} thumbSize default 300
28589 * @cfg {String} fieldLabel
28590 * @cfg {Number} labelWidth default 4
28591 * @cfg {String} labelAlign (left|top) default left
28592 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28593 * @cfg {Number} labellg set the width of label (1-12)
28594 * @cfg {Number} labelmd set the width of label (1-12)
28595 * @cfg {Number} labelsm set the width of label (1-12)
28596 * @cfg {Number} labelxs set the width of label (1-12)
28599 * Create a new DocumentManager
28600 * @param {Object} config The config object
28603 Roo.bootstrap.DocumentManager = function(config){
28604 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28607 this.delegates = [];
28612 * Fire when initial the DocumentManager
28613 * @param {Roo.bootstrap.DocumentManager} this
28618 * inspect selected file
28619 * @param {Roo.bootstrap.DocumentManager} this
28620 * @param {File} file
28625 * Fire when xhr load exception
28626 * @param {Roo.bootstrap.DocumentManager} this
28627 * @param {XMLHttpRequest} xhr
28629 "exception" : true,
28631 * @event afterupload
28632 * Fire when xhr load exception
28633 * @param {Roo.bootstrap.DocumentManager} this
28634 * @param {XMLHttpRequest} xhr
28636 "afterupload" : true,
28639 * prepare the form data
28640 * @param {Roo.bootstrap.DocumentManager} this
28641 * @param {Object} formData
28646 * Fire when remove the file
28647 * @param {Roo.bootstrap.DocumentManager} this
28648 * @param {Object} file
28653 * Fire after refresh the file
28654 * @param {Roo.bootstrap.DocumentManager} this
28659 * Fire after click the image
28660 * @param {Roo.bootstrap.DocumentManager} this
28661 * @param {Object} file
28666 * Fire when upload a image and editable set to true
28667 * @param {Roo.bootstrap.DocumentManager} this
28668 * @param {Object} file
28672 * @event beforeselectfile
28673 * Fire before select file
28674 * @param {Roo.bootstrap.DocumentManager} this
28676 "beforeselectfile" : true,
28679 * Fire before process file
28680 * @param {Roo.bootstrap.DocumentManager} this
28681 * @param {Object} file
28685 * @event previewrendered
28686 * Fire when preview rendered
28687 * @param {Roo.bootstrap.DocumentManager} this
28688 * @param {Object} file
28690 "previewrendered" : true,
28693 "previewResize" : true
28698 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28707 paramName : 'imageUpload',
28708 toolTipName : 'filename',
28711 labelAlign : 'left',
28721 getAutoCreate : function()
28723 var managerWidget = {
28725 cls : 'roo-document-manager',
28729 cls : 'roo-document-manager-selector',
28734 cls : 'roo-document-manager-uploader',
28738 cls : 'roo-document-manager-upload-btn',
28739 html : '<i class="fa fa-plus"></i>'
28750 cls : 'column col-md-12',
28755 if(this.fieldLabel.length){
28760 cls : 'column col-md-12',
28761 html : this.fieldLabel
28765 cls : 'column col-md-12',
28770 if(this.labelAlign == 'left'){
28775 html : this.fieldLabel
28784 if(this.labelWidth > 12){
28785 content[0].style = "width: " + this.labelWidth + 'px';
28788 if(this.labelWidth < 13 && this.labelmd == 0){
28789 this.labelmd = this.labelWidth;
28792 if(this.labellg > 0){
28793 content[0].cls += ' col-lg-' + this.labellg;
28794 content[1].cls += ' col-lg-' + (12 - this.labellg);
28797 if(this.labelmd > 0){
28798 content[0].cls += ' col-md-' + this.labelmd;
28799 content[1].cls += ' col-md-' + (12 - this.labelmd);
28802 if(this.labelsm > 0){
28803 content[0].cls += ' col-sm-' + this.labelsm;
28804 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28807 if(this.labelxs > 0){
28808 content[0].cls += ' col-xs-' + this.labelxs;
28809 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28817 cls : 'row clearfix',
28825 initEvents : function()
28827 this.managerEl = this.el.select('.roo-document-manager', true).first();
28828 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28830 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28831 this.selectorEl.hide();
28834 this.selectorEl.attr('multiple', 'multiple');
28837 this.selectorEl.on('change', this.onFileSelected, this);
28839 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28840 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28842 this.uploader.on('click', this.onUploaderClick, this);
28844 this.renderProgressDialog();
28848 window.addEventListener("resize", function() { _this.refresh(); } );
28850 this.fireEvent('initial', this);
28853 renderProgressDialog : function()
28857 this.progressDialog = new Roo.bootstrap.Modal({
28858 cls : 'roo-document-manager-progress-dialog',
28859 allow_close : false,
28869 btnclick : function() {
28870 _this.uploadCancel();
28876 this.progressDialog.render(Roo.get(document.body));
28878 this.progress = new Roo.bootstrap.Progress({
28879 cls : 'roo-document-manager-progress',
28884 this.progress.render(this.progressDialog.getChildContainer());
28886 this.progressBar = new Roo.bootstrap.ProgressBar({
28887 cls : 'roo-document-manager-progress-bar',
28890 aria_valuemax : 12,
28894 this.progressBar.render(this.progress.getChildContainer());
28897 onUploaderClick : function(e)
28899 e.preventDefault();
28901 if(this.fireEvent('beforeselectfile', this) != false){
28902 this.selectorEl.dom.click();
28907 onFileSelected : function(e)
28909 e.preventDefault();
28911 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28915 Roo.each(this.selectorEl.dom.files, function(file){
28916 if(this.fireEvent('inspect', this, file) != false){
28917 this.files.push(file);
28927 this.selectorEl.dom.value = '';
28929 if(!this.files || !this.files.length){
28933 if(this.boxes > 0 && this.files.length > this.boxes){
28934 this.files = this.files.slice(0, this.boxes);
28937 this.uploader.show();
28939 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28940 this.uploader.hide();
28949 Roo.each(this.files, function(file){
28951 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28952 var f = this.renderPreview(file);
28957 if(file.type.indexOf('image') != -1){
28958 this.delegates.push(
28960 _this.process(file);
28961 }).createDelegate(this)
28969 _this.process(file);
28970 }).createDelegate(this)
28975 this.files = files;
28977 this.delegates = this.delegates.concat(docs);
28979 if(!this.delegates.length){
28984 this.progressBar.aria_valuemax = this.delegates.length;
28991 arrange : function()
28993 if(!this.delegates.length){
28994 this.progressDialog.hide();
28999 var delegate = this.delegates.shift();
29001 this.progressDialog.show();
29003 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29005 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29010 refresh : function()
29012 this.uploader.show();
29014 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29015 this.uploader.hide();
29018 Roo.isTouch ? this.closable(false) : this.closable(true);
29020 this.fireEvent('refresh', this);
29023 onRemove : function(e, el, o)
29025 e.preventDefault();
29027 this.fireEvent('remove', this, o);
29031 remove : function(o)
29035 Roo.each(this.files, function(file){
29036 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29045 this.files = files;
29052 Roo.each(this.files, function(file){
29057 file.target.remove();
29066 onClick : function(e, el, o)
29068 e.preventDefault();
29070 this.fireEvent('click', this, o);
29074 closable : function(closable)
29076 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29078 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29090 xhrOnLoad : function(xhr)
29092 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29096 if (xhr.readyState !== 4) {
29098 this.fireEvent('exception', this, xhr);
29102 var response = Roo.decode(xhr.responseText);
29104 if(!response.success){
29106 this.fireEvent('exception', this, xhr);
29110 var file = this.renderPreview(response.data);
29112 this.files.push(file);
29116 this.fireEvent('afterupload', this, xhr);
29120 xhrOnError : function(xhr)
29122 Roo.log('xhr on error');
29124 var response = Roo.decode(xhr.responseText);
29131 process : function(file)
29133 if(this.fireEvent('process', this, file) !== false){
29134 if(this.editable && file.type.indexOf('image') != -1){
29135 this.fireEvent('edit', this, file);
29139 this.uploadStart(file, false);
29146 uploadStart : function(file, crop)
29148 this.xhr = new XMLHttpRequest();
29150 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29155 file.xhr = this.xhr;
29157 this.managerEl.createChild({
29159 cls : 'roo-document-manager-loading',
29163 tooltip : file.name,
29164 cls : 'roo-document-manager-thumb',
29165 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29171 this.xhr.open(this.method, this.url, true);
29174 "Accept": "application/json",
29175 "Cache-Control": "no-cache",
29176 "X-Requested-With": "XMLHttpRequest"
29179 for (var headerName in headers) {
29180 var headerValue = headers[headerName];
29182 this.xhr.setRequestHeader(headerName, headerValue);
29188 this.xhr.onload = function()
29190 _this.xhrOnLoad(_this.xhr);
29193 this.xhr.onerror = function()
29195 _this.xhrOnError(_this.xhr);
29198 var formData = new FormData();
29200 formData.append('returnHTML', 'NO');
29203 formData.append('crop', crop);
29206 formData.append(this.paramName, file, file.name);
29213 if(this.fireEvent('prepare', this, formData, options) != false){
29215 if(options.manually){
29219 this.xhr.send(formData);
29223 this.uploadCancel();
29226 uploadCancel : function()
29232 this.delegates = [];
29234 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29241 renderPreview : function(file)
29243 if(typeof(file.target) != 'undefined' && file.target){
29247 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29249 var previewEl = this.managerEl.createChild({
29251 cls : 'roo-document-manager-preview',
29255 tooltip : file[this.toolTipName],
29256 cls : 'roo-document-manager-thumb',
29257 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29262 html : '<i class="fa fa-times-circle"></i>'
29267 var close = previewEl.select('button.close', true).first();
29269 close.on('click', this.onRemove, this, file);
29271 file.target = previewEl;
29273 var image = previewEl.select('img', true).first();
29277 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29279 image.on('click', this.onClick, this, file);
29281 this.fireEvent('previewrendered', this, file);
29287 onPreviewLoad : function(file, image)
29289 if(typeof(file.target) == 'undefined' || !file.target){
29293 var width = image.dom.naturalWidth || image.dom.width;
29294 var height = image.dom.naturalHeight || image.dom.height;
29296 if(!this.previewResize) {
29300 if(width > height){
29301 file.target.addClass('wide');
29305 file.target.addClass('tall');
29310 uploadFromSource : function(file, crop)
29312 this.xhr = new XMLHttpRequest();
29314 this.managerEl.createChild({
29316 cls : 'roo-document-manager-loading',
29320 tooltip : file.name,
29321 cls : 'roo-document-manager-thumb',
29322 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29328 this.xhr.open(this.method, this.url, true);
29331 "Accept": "application/json",
29332 "Cache-Control": "no-cache",
29333 "X-Requested-With": "XMLHttpRequest"
29336 for (var headerName in headers) {
29337 var headerValue = headers[headerName];
29339 this.xhr.setRequestHeader(headerName, headerValue);
29345 this.xhr.onload = function()
29347 _this.xhrOnLoad(_this.xhr);
29350 this.xhr.onerror = function()
29352 _this.xhrOnError(_this.xhr);
29355 var formData = new FormData();
29357 formData.append('returnHTML', 'NO');
29359 formData.append('crop', crop);
29361 if(typeof(file.filename) != 'undefined'){
29362 formData.append('filename', file.filename);
29365 if(typeof(file.mimetype) != 'undefined'){
29366 formData.append('mimetype', file.mimetype);
29371 if(this.fireEvent('prepare', this, formData) != false){
29372 this.xhr.send(formData);
29382 * @class Roo.bootstrap.DocumentViewer
29383 * @extends Roo.bootstrap.Component
29384 * Bootstrap DocumentViewer class
29385 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29386 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29389 * Create a new DocumentViewer
29390 * @param {Object} config The config object
29393 Roo.bootstrap.DocumentViewer = function(config){
29394 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29399 * Fire after initEvent
29400 * @param {Roo.bootstrap.DocumentViewer} this
29406 * @param {Roo.bootstrap.DocumentViewer} this
29411 * Fire after download button
29412 * @param {Roo.bootstrap.DocumentViewer} this
29417 * Fire after trash button
29418 * @param {Roo.bootstrap.DocumentViewer} this
29425 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29427 showDownload : true,
29431 getAutoCreate : function()
29435 cls : 'roo-document-viewer',
29439 cls : 'roo-document-viewer-body',
29443 cls : 'roo-document-viewer-thumb',
29447 cls : 'roo-document-viewer-image'
29455 cls : 'roo-document-viewer-footer',
29458 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29462 cls : 'btn-group roo-document-viewer-download',
29466 cls : 'btn btn-default',
29467 html : '<i class="fa fa-download"></i>'
29473 cls : 'btn-group roo-document-viewer-trash',
29477 cls : 'btn btn-default',
29478 html : '<i class="fa fa-trash"></i>'
29491 initEvents : function()
29493 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29494 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29496 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29497 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29499 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29500 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29502 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29503 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29505 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29506 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29508 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29509 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29511 this.bodyEl.on('click', this.onClick, this);
29512 this.downloadBtn.on('click', this.onDownload, this);
29513 this.trashBtn.on('click', this.onTrash, this);
29515 this.downloadBtn.hide();
29516 this.trashBtn.hide();
29518 if(this.showDownload){
29519 this.downloadBtn.show();
29522 if(this.showTrash){
29523 this.trashBtn.show();
29526 if(!this.showDownload && !this.showTrash) {
29527 this.footerEl.hide();
29532 initial : function()
29534 this.fireEvent('initial', this);
29538 onClick : function(e)
29540 e.preventDefault();
29542 this.fireEvent('click', this);
29545 onDownload : function(e)
29547 e.preventDefault();
29549 this.fireEvent('download', this);
29552 onTrash : function(e)
29554 e.preventDefault();
29556 this.fireEvent('trash', this);
29568 * @class Roo.bootstrap.NavProgressBar
29569 * @extends Roo.bootstrap.Component
29570 * Bootstrap NavProgressBar class
29573 * Create a new nav progress bar
29574 * @param {Object} config The config object
29577 Roo.bootstrap.NavProgressBar = function(config){
29578 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29580 this.bullets = this.bullets || [];
29582 // Roo.bootstrap.NavProgressBar.register(this);
29586 * Fires when the active item changes
29587 * @param {Roo.bootstrap.NavProgressBar} this
29588 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29589 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29596 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29601 getAutoCreate : function()
29603 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29607 cls : 'roo-navigation-bar-group',
29611 cls : 'roo-navigation-top-bar'
29615 cls : 'roo-navigation-bullets-bar',
29619 cls : 'roo-navigation-bar'
29626 cls : 'roo-navigation-bottom-bar'
29636 initEvents: function()
29641 onRender : function(ct, position)
29643 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29645 if(this.bullets.length){
29646 Roo.each(this.bullets, function(b){
29655 addItem : function(cfg)
29657 var item = new Roo.bootstrap.NavProgressItem(cfg);
29659 item.parentId = this.id;
29660 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29663 var top = new Roo.bootstrap.Element({
29665 cls : 'roo-navigation-bar-text'
29668 var bottom = new Roo.bootstrap.Element({
29670 cls : 'roo-navigation-bar-text'
29673 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29674 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29676 var topText = new Roo.bootstrap.Element({
29678 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29681 var bottomText = new Roo.bootstrap.Element({
29683 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29686 topText.onRender(top.el, null);
29687 bottomText.onRender(bottom.el, null);
29690 item.bottomEl = bottom;
29693 this.barItems.push(item);
29698 getActive : function()
29700 var active = false;
29702 Roo.each(this.barItems, function(v){
29704 if (!v.isActive()) {
29716 setActiveItem : function(item)
29720 Roo.each(this.barItems, function(v){
29721 if (v.rid == item.rid) {
29725 if (v.isActive()) {
29726 v.setActive(false);
29731 item.setActive(true);
29733 this.fireEvent('changed', this, item, prev);
29736 getBarItem: function(rid)
29740 Roo.each(this.barItems, function(e) {
29741 if (e.rid != rid) {
29752 indexOfItem : function(item)
29756 Roo.each(this.barItems, function(v, i){
29758 if (v.rid != item.rid) {
29769 setActiveNext : function()
29771 var i = this.indexOfItem(this.getActive());
29773 if (i > this.barItems.length) {
29777 this.setActiveItem(this.barItems[i+1]);
29780 setActivePrev : function()
29782 var i = this.indexOfItem(this.getActive());
29788 this.setActiveItem(this.barItems[i-1]);
29791 format : function()
29793 if(!this.barItems.length){
29797 var width = 100 / this.barItems.length;
29799 Roo.each(this.barItems, function(i){
29800 i.el.setStyle('width', width + '%');
29801 i.topEl.el.setStyle('width', width + '%');
29802 i.bottomEl.el.setStyle('width', width + '%');
29811 * Nav Progress Item
29816 * @class Roo.bootstrap.NavProgressItem
29817 * @extends Roo.bootstrap.Component
29818 * Bootstrap NavProgressItem class
29819 * @cfg {String} rid the reference id
29820 * @cfg {Boolean} active (true|false) Is item active default false
29821 * @cfg {Boolean} disabled (true|false) Is item active default false
29822 * @cfg {String} html
29823 * @cfg {String} position (top|bottom) text position default bottom
29824 * @cfg {String} icon show icon instead of number
29827 * Create a new NavProgressItem
29828 * @param {Object} config The config object
29830 Roo.bootstrap.NavProgressItem = function(config){
29831 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29836 * The raw click event for the entire grid.
29837 * @param {Roo.bootstrap.NavProgressItem} this
29838 * @param {Roo.EventObject} e
29845 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29851 position : 'bottom',
29854 getAutoCreate : function()
29856 var iconCls = 'roo-navigation-bar-item-icon';
29858 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29862 cls: 'roo-navigation-bar-item',
29872 cfg.cls += ' active';
29875 cfg.cls += ' disabled';
29881 disable : function()
29883 this.setDisabled(true);
29886 enable : function()
29888 this.setDisabled(false);
29891 initEvents: function()
29893 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29895 this.iconEl.on('click', this.onClick, this);
29898 onClick : function(e)
29900 e.preventDefault();
29906 if(this.fireEvent('click', this, e) === false){
29910 this.parent().setActiveItem(this);
29913 isActive: function ()
29915 return this.active;
29918 setActive : function(state)
29920 if(this.active == state){
29924 this.active = state;
29927 this.el.addClass('active');
29931 this.el.removeClass('active');
29936 setDisabled : function(state)
29938 if(this.disabled == state){
29942 this.disabled = state;
29945 this.el.addClass('disabled');
29949 this.el.removeClass('disabled');
29952 tooltipEl : function()
29954 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29967 * @class Roo.bootstrap.FieldLabel
29968 * @extends Roo.bootstrap.Component
29969 * Bootstrap FieldLabel class
29970 * @cfg {String} html contents of the element
29971 * @cfg {String} tag tag of the element default label
29972 * @cfg {String} cls class of the element
29973 * @cfg {String} target label target
29974 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29975 * @cfg {String} invalidClass default "text-warning"
29976 * @cfg {String} validClass default "text-success"
29977 * @cfg {String} iconTooltip default "This field is required"
29978 * @cfg {String} indicatorpos (left|right) default left
29981 * Create a new FieldLabel
29982 * @param {Object} config The config object
29985 Roo.bootstrap.FieldLabel = function(config){
29986 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29991 * Fires after the field has been marked as invalid.
29992 * @param {Roo.form.FieldLabel} this
29993 * @param {String} msg The validation message
29998 * Fires after the field has been validated with no errors.
29999 * @param {Roo.form.FieldLabel} this
30005 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30012 invalidClass : 'has-warning',
30013 validClass : 'has-success',
30014 iconTooltip : 'This field is required',
30015 indicatorpos : 'left',
30017 getAutoCreate : function(){
30020 if (!this.allowBlank) {
30026 cls : 'roo-bootstrap-field-label ' + this.cls,
30031 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30032 tooltip : this.iconTooltip
30041 if(this.indicatorpos == 'right'){
30044 cls : 'roo-bootstrap-field-label ' + this.cls,
30053 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30054 tooltip : this.iconTooltip
30063 initEvents: function()
30065 Roo.bootstrap.Element.superclass.initEvents.call(this);
30067 this.indicator = this.indicatorEl();
30069 if(this.indicator){
30070 this.indicator.removeClass('visible');
30071 this.indicator.addClass('invisible');
30074 Roo.bootstrap.FieldLabel.register(this);
30077 indicatorEl : function()
30079 var indicator = this.el.select('i.roo-required-indicator',true).first();
30090 * Mark this field as valid
30092 markValid : function()
30094 if(this.indicator){
30095 this.indicator.removeClass('visible');
30096 this.indicator.addClass('invisible');
30099 this.el.removeClass(this.invalidClass);
30101 this.el.addClass(this.validClass);
30103 this.fireEvent('valid', this);
30107 * Mark this field as invalid
30108 * @param {String} msg The validation message
30110 markInvalid : function(msg)
30112 if(this.indicator){
30113 this.indicator.removeClass('invisible');
30114 this.indicator.addClass('visible');
30117 this.el.removeClass(this.validClass);
30119 this.el.addClass(this.invalidClass);
30121 this.fireEvent('invalid', this, msg);
30127 Roo.apply(Roo.bootstrap.FieldLabel, {
30132 * register a FieldLabel Group
30133 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30135 register : function(label)
30137 if(this.groups.hasOwnProperty(label.target)){
30141 this.groups[label.target] = label;
30145 * fetch a FieldLabel Group based on the target
30146 * @param {string} target
30147 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30149 get: function(target) {
30150 if (typeof(this.groups[target]) == 'undefined') {
30154 return this.groups[target] ;
30163 * page DateSplitField.
30169 * @class Roo.bootstrap.DateSplitField
30170 * @extends Roo.bootstrap.Component
30171 * Bootstrap DateSplitField class
30172 * @cfg {string} fieldLabel - the label associated
30173 * @cfg {Number} labelWidth set the width of label (0-12)
30174 * @cfg {String} labelAlign (top|left)
30175 * @cfg {Boolean} dayAllowBlank (true|false) default false
30176 * @cfg {Boolean} monthAllowBlank (true|false) default false
30177 * @cfg {Boolean} yearAllowBlank (true|false) default false
30178 * @cfg {string} dayPlaceholder
30179 * @cfg {string} monthPlaceholder
30180 * @cfg {string} yearPlaceholder
30181 * @cfg {string} dayFormat default 'd'
30182 * @cfg {string} monthFormat default 'm'
30183 * @cfg {string} yearFormat default 'Y'
30184 * @cfg {Number} labellg set the width of label (1-12)
30185 * @cfg {Number} labelmd set the width of label (1-12)
30186 * @cfg {Number} labelsm set the width of label (1-12)
30187 * @cfg {Number} labelxs set the width of label (1-12)
30191 * Create a new DateSplitField
30192 * @param {Object} config The config object
30195 Roo.bootstrap.DateSplitField = function(config){
30196 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30202 * getting the data of years
30203 * @param {Roo.bootstrap.DateSplitField} this
30204 * @param {Object} years
30209 * getting the data of days
30210 * @param {Roo.bootstrap.DateSplitField} this
30211 * @param {Object} days
30216 * Fires after the field has been marked as invalid.
30217 * @param {Roo.form.Field} this
30218 * @param {String} msg The validation message
30223 * Fires after the field has been validated with no errors.
30224 * @param {Roo.form.Field} this
30230 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30233 labelAlign : 'top',
30235 dayAllowBlank : false,
30236 monthAllowBlank : false,
30237 yearAllowBlank : false,
30238 dayPlaceholder : '',
30239 monthPlaceholder : '',
30240 yearPlaceholder : '',
30244 isFormField : true,
30250 getAutoCreate : function()
30254 cls : 'row roo-date-split-field-group',
30259 cls : 'form-hidden-field roo-date-split-field-group-value',
30265 var labelCls = 'col-md-12';
30266 var contentCls = 'col-md-4';
30268 if(this.fieldLabel){
30272 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30276 html : this.fieldLabel
30281 if(this.labelAlign == 'left'){
30283 if(this.labelWidth > 12){
30284 label.style = "width: " + this.labelWidth + 'px';
30287 if(this.labelWidth < 13 && this.labelmd == 0){
30288 this.labelmd = this.labelWidth;
30291 if(this.labellg > 0){
30292 labelCls = ' col-lg-' + this.labellg;
30293 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30296 if(this.labelmd > 0){
30297 labelCls = ' col-md-' + this.labelmd;
30298 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30301 if(this.labelsm > 0){
30302 labelCls = ' col-sm-' + this.labelsm;
30303 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30306 if(this.labelxs > 0){
30307 labelCls = ' col-xs-' + this.labelxs;
30308 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30312 label.cls += ' ' + labelCls;
30314 cfg.cn.push(label);
30317 Roo.each(['day', 'month', 'year'], function(t){
30320 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30327 inputEl: function ()
30329 return this.el.select('.roo-date-split-field-group-value', true).first();
30332 onRender : function(ct, position)
30336 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30338 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30340 this.dayField = new Roo.bootstrap.ComboBox({
30341 allowBlank : this.dayAllowBlank,
30342 alwaysQuery : true,
30343 displayField : 'value',
30346 forceSelection : true,
30348 placeholder : this.dayPlaceholder,
30349 selectOnFocus : true,
30350 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30351 triggerAction : 'all',
30353 valueField : 'value',
30354 store : new Roo.data.SimpleStore({
30355 data : (function() {
30357 _this.fireEvent('days', _this, days);
30360 fields : [ 'value' ]
30363 select : function (_self, record, index)
30365 _this.setValue(_this.getValue());
30370 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30372 this.monthField = new Roo.bootstrap.MonthField({
30373 after : '<i class=\"fa fa-calendar\"></i>',
30374 allowBlank : this.monthAllowBlank,
30375 placeholder : this.monthPlaceholder,
30378 render : function (_self)
30380 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30381 e.preventDefault();
30385 select : function (_self, oldvalue, newvalue)
30387 _this.setValue(_this.getValue());
30392 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30394 this.yearField = new Roo.bootstrap.ComboBox({
30395 allowBlank : this.yearAllowBlank,
30396 alwaysQuery : true,
30397 displayField : 'value',
30400 forceSelection : true,
30402 placeholder : this.yearPlaceholder,
30403 selectOnFocus : true,
30404 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30405 triggerAction : 'all',
30407 valueField : 'value',
30408 store : new Roo.data.SimpleStore({
30409 data : (function() {
30411 _this.fireEvent('years', _this, years);
30414 fields : [ 'value' ]
30417 select : function (_self, record, index)
30419 _this.setValue(_this.getValue());
30424 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30427 setValue : function(v, format)
30429 this.inputEl.dom.value = v;
30431 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30433 var d = Date.parseDate(v, f);
30440 this.setDay(d.format(this.dayFormat));
30441 this.setMonth(d.format(this.monthFormat));
30442 this.setYear(d.format(this.yearFormat));
30449 setDay : function(v)
30451 this.dayField.setValue(v);
30452 this.inputEl.dom.value = this.getValue();
30457 setMonth : function(v)
30459 this.monthField.setValue(v, true);
30460 this.inputEl.dom.value = this.getValue();
30465 setYear : function(v)
30467 this.yearField.setValue(v);
30468 this.inputEl.dom.value = this.getValue();
30473 getDay : function()
30475 return this.dayField.getValue();
30478 getMonth : function()
30480 return this.monthField.getValue();
30483 getYear : function()
30485 return this.yearField.getValue();
30488 getValue : function()
30490 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30492 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30502 this.inputEl.dom.value = '';
30507 validate : function()
30509 var d = this.dayField.validate();
30510 var m = this.monthField.validate();
30511 var y = this.yearField.validate();
30516 (!this.dayAllowBlank && !d) ||
30517 (!this.monthAllowBlank && !m) ||
30518 (!this.yearAllowBlank && !y)
30523 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30532 this.markInvalid();
30537 markValid : function()
30540 var label = this.el.select('label', true).first();
30541 var icon = this.el.select('i.fa-star', true).first();
30547 this.fireEvent('valid', this);
30551 * Mark this field as invalid
30552 * @param {String} msg The validation message
30554 markInvalid : function(msg)
30557 var label = this.el.select('label', true).first();
30558 var icon = this.el.select('i.fa-star', true).first();
30560 if(label && !icon){
30561 this.el.select('.roo-date-split-field-label', true).createChild({
30563 cls : 'text-danger fa fa-lg fa-star',
30564 tooltip : 'This field is required',
30565 style : 'margin-right:5px;'
30569 this.fireEvent('invalid', this, msg);
30572 clearInvalid : function()
30574 var label = this.el.select('label', true).first();
30575 var icon = this.el.select('i.fa-star', true).first();
30581 this.fireEvent('valid', this);
30584 getName: function()
30594 * http://masonry.desandro.com
30596 * The idea is to render all the bricks based on vertical width...
30598 * The original code extends 'outlayer' - we might need to use that....
30604 * @class Roo.bootstrap.LayoutMasonry
30605 * @extends Roo.bootstrap.Component
30606 * Bootstrap Layout Masonry class
30609 * Create a new Element
30610 * @param {Object} config The config object
30613 Roo.bootstrap.LayoutMasonry = function(config){
30615 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30619 Roo.bootstrap.LayoutMasonry.register(this);
30625 * Fire after layout the items
30626 * @param {Roo.bootstrap.LayoutMasonry} this
30627 * @param {Roo.EventObject} e
30634 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30637 * @cfg {Boolean} isLayoutInstant = no animation?
30639 isLayoutInstant : false, // needed?
30642 * @cfg {Number} boxWidth width of the columns
30647 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30652 * @cfg {Number} padWidth padding below box..
30657 * @cfg {Number} gutter gutter width..
30662 * @cfg {Number} maxCols maximum number of columns
30668 * @cfg {Boolean} isAutoInitial defalut true
30670 isAutoInitial : true,
30675 * @cfg {Boolean} isHorizontal defalut false
30677 isHorizontal : false,
30679 currentSize : null,
30685 bricks: null, //CompositeElement
30689 _isLayoutInited : false,
30691 // isAlternative : false, // only use for vertical layout...
30694 * @cfg {Number} alternativePadWidth padding below box..
30696 alternativePadWidth : 50,
30698 selectedBrick : [],
30700 getAutoCreate : function(){
30702 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30706 cls: 'blog-masonary-wrapper ' + this.cls,
30708 cls : 'mas-boxes masonary'
30715 getChildContainer: function( )
30717 if (this.boxesEl) {
30718 return this.boxesEl;
30721 this.boxesEl = this.el.select('.mas-boxes').first();
30723 return this.boxesEl;
30727 initEvents : function()
30731 if(this.isAutoInitial){
30732 Roo.log('hook children rendered');
30733 this.on('childrenrendered', function() {
30734 Roo.log('children rendered');
30740 initial : function()
30742 this.selectedBrick = [];
30744 this.currentSize = this.el.getBox(true);
30746 Roo.EventManager.onWindowResize(this.resize, this);
30748 if(!this.isAutoInitial){
30756 //this.layout.defer(500,this);
30760 resize : function()
30762 var cs = this.el.getBox(true);
30765 this.currentSize.width == cs.width &&
30766 this.currentSize.x == cs.x &&
30767 this.currentSize.height == cs.height &&
30768 this.currentSize.y == cs.y
30770 Roo.log("no change in with or X or Y");
30774 this.currentSize = cs;
30780 layout : function()
30782 this._resetLayout();
30784 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30786 this.layoutItems( isInstant );
30788 this._isLayoutInited = true;
30790 this.fireEvent('layout', this);
30794 _resetLayout : function()
30796 if(this.isHorizontal){
30797 this.horizontalMeasureColumns();
30801 this.verticalMeasureColumns();
30805 verticalMeasureColumns : function()
30807 this.getContainerWidth();
30809 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30810 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30814 var boxWidth = this.boxWidth + this.padWidth;
30816 if(this.containerWidth < this.boxWidth){
30817 boxWidth = this.containerWidth
30820 var containerWidth = this.containerWidth;
30822 var cols = Math.floor(containerWidth / boxWidth);
30824 this.cols = Math.max( cols, 1 );
30826 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30828 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30830 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30832 this.colWidth = boxWidth + avail - this.padWidth;
30834 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30835 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30838 horizontalMeasureColumns : function()
30840 this.getContainerWidth();
30842 var boxWidth = this.boxWidth;
30844 if(this.containerWidth < boxWidth){
30845 boxWidth = this.containerWidth;
30848 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30850 this.el.setHeight(boxWidth);
30854 getContainerWidth : function()
30856 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30859 layoutItems : function( isInstant )
30861 Roo.log(this.bricks);
30863 var items = Roo.apply([], this.bricks);
30865 if(this.isHorizontal){
30866 this._horizontalLayoutItems( items , isInstant );
30870 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30871 // this._verticalAlternativeLayoutItems( items , isInstant );
30875 this._verticalLayoutItems( items , isInstant );
30879 _verticalLayoutItems : function ( items , isInstant)
30881 if ( !items || !items.length ) {
30886 ['xs', 'xs', 'xs', 'tall'],
30887 ['xs', 'xs', 'tall'],
30888 ['xs', 'xs', 'sm'],
30889 ['xs', 'xs', 'xs'],
30895 ['sm', 'xs', 'xs'],
30899 ['tall', 'xs', 'xs', 'xs'],
30900 ['tall', 'xs', 'xs'],
30912 Roo.each(items, function(item, k){
30914 switch (item.size) {
30915 // these layouts take up a full box,
30926 boxes.push([item]);
30949 var filterPattern = function(box, length)
30957 var pattern = box.slice(0, length);
30961 Roo.each(pattern, function(i){
30962 format.push(i.size);
30965 Roo.each(standard, function(s){
30967 if(String(s) != String(format)){
30976 if(!match && length == 1){
30981 filterPattern(box, length - 1);
30985 queue.push(pattern);
30987 box = box.slice(length, box.length);
30989 filterPattern(box, 4);
30995 Roo.each(boxes, function(box, k){
31001 if(box.length == 1){
31006 filterPattern(box, 4);
31010 this._processVerticalLayoutQueue( queue, isInstant );
31014 // _verticalAlternativeLayoutItems : function( items , isInstant )
31016 // if ( !items || !items.length ) {
31020 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31024 _horizontalLayoutItems : function ( items , isInstant)
31026 if ( !items || !items.length || items.length < 3) {
31032 var eItems = items.slice(0, 3);
31034 items = items.slice(3, items.length);
31037 ['xs', 'xs', 'xs', 'wide'],
31038 ['xs', 'xs', 'wide'],
31039 ['xs', 'xs', 'sm'],
31040 ['xs', 'xs', 'xs'],
31046 ['sm', 'xs', 'xs'],
31050 ['wide', 'xs', 'xs', 'xs'],
31051 ['wide', 'xs', 'xs'],
31064 Roo.each(items, function(item, k){
31066 switch (item.size) {
31077 boxes.push([item]);
31101 var filterPattern = function(box, length)
31109 var pattern = box.slice(0, length);
31113 Roo.each(pattern, function(i){
31114 format.push(i.size);
31117 Roo.each(standard, function(s){
31119 if(String(s) != String(format)){
31128 if(!match && length == 1){
31133 filterPattern(box, length - 1);
31137 queue.push(pattern);
31139 box = box.slice(length, box.length);
31141 filterPattern(box, 4);
31147 Roo.each(boxes, function(box, k){
31153 if(box.length == 1){
31158 filterPattern(box, 4);
31165 var pos = this.el.getBox(true);
31169 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31171 var hit_end = false;
31173 Roo.each(queue, function(box){
31177 Roo.each(box, function(b){
31179 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31189 Roo.each(box, function(b){
31191 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31194 mx = Math.max(mx, b.x);
31198 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31202 Roo.each(box, function(b){
31204 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31218 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31221 /** Sets position of item in DOM
31222 * @param {Element} item
31223 * @param {Number} x - horizontal position
31224 * @param {Number} y - vertical position
31225 * @param {Boolean} isInstant - disables transitions
31227 _processVerticalLayoutQueue : function( queue, isInstant )
31229 var pos = this.el.getBox(true);
31234 for (var i = 0; i < this.cols; i++){
31238 Roo.each(queue, function(box, k){
31240 var col = k % this.cols;
31242 Roo.each(box, function(b,kk){
31244 b.el.position('absolute');
31246 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31247 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31249 if(b.size == 'md-left' || b.size == 'md-right'){
31250 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31251 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31254 b.el.setWidth(width);
31255 b.el.setHeight(height);
31257 b.el.select('iframe',true).setSize(width,height);
31261 for (var i = 0; i < this.cols; i++){
31263 if(maxY[i] < maxY[col]){
31268 col = Math.min(col, i);
31272 x = pos.x + col * (this.colWidth + this.padWidth);
31276 var positions = [];
31278 switch (box.length){
31280 positions = this.getVerticalOneBoxColPositions(x, y, box);
31283 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31286 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31289 positions = this.getVerticalFourBoxColPositions(x, y, box);
31295 Roo.each(box, function(b,kk){
31297 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31299 var sz = b.el.getSize();
31301 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31309 for (var i = 0; i < this.cols; i++){
31310 mY = Math.max(mY, maxY[i]);
31313 this.el.setHeight(mY - pos.y);
31317 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31319 // var pos = this.el.getBox(true);
31322 // var maxX = pos.right;
31324 // var maxHeight = 0;
31326 // Roo.each(items, function(item, k){
31330 // item.el.position('absolute');
31332 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31334 // item.el.setWidth(width);
31336 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31338 // item.el.setHeight(height);
31341 // item.el.setXY([x, y], isInstant ? false : true);
31343 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31346 // y = y + height + this.alternativePadWidth;
31348 // maxHeight = maxHeight + height + this.alternativePadWidth;
31352 // this.el.setHeight(maxHeight);
31356 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31358 var pos = this.el.getBox(true);
31363 var maxX = pos.right;
31365 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31367 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31369 Roo.each(queue, function(box, k){
31371 Roo.each(box, function(b, kk){
31373 b.el.position('absolute');
31375 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31376 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31378 if(b.size == 'md-left' || b.size == 'md-right'){
31379 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31380 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31383 b.el.setWidth(width);
31384 b.el.setHeight(height);
31392 var positions = [];
31394 switch (box.length){
31396 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31399 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31402 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31405 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31411 Roo.each(box, function(b,kk){
31413 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31415 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31423 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31425 Roo.each(eItems, function(b,k){
31427 b.size = (k == 0) ? 'sm' : 'xs';
31428 b.x = (k == 0) ? 2 : 1;
31429 b.y = (k == 0) ? 2 : 1;
31431 b.el.position('absolute');
31433 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31435 b.el.setWidth(width);
31437 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31439 b.el.setHeight(height);
31443 var positions = [];
31446 x : maxX - this.unitWidth * 2 - this.gutter,
31451 x : maxX - this.unitWidth,
31452 y : minY + (this.unitWidth + this.gutter) * 2
31456 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31460 Roo.each(eItems, function(b,k){
31462 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31468 getVerticalOneBoxColPositions : function(x, y, box)
31472 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31474 if(box[0].size == 'md-left'){
31478 if(box[0].size == 'md-right'){
31483 x : x + (this.unitWidth + this.gutter) * rand,
31490 getVerticalTwoBoxColPositions : function(x, y, box)
31494 if(box[0].size == 'xs'){
31498 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31502 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31516 x : x + (this.unitWidth + this.gutter) * 2,
31517 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31524 getVerticalThreeBoxColPositions : function(x, y, box)
31528 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31536 x : x + (this.unitWidth + this.gutter) * 1,
31541 x : x + (this.unitWidth + this.gutter) * 2,
31549 if(box[0].size == 'xs' && box[1].size == 'xs'){
31558 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31562 x : x + (this.unitWidth + this.gutter) * 1,
31576 x : x + (this.unitWidth + this.gutter) * 2,
31581 x : x + (this.unitWidth + this.gutter) * 2,
31582 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31589 getVerticalFourBoxColPositions : function(x, y, box)
31593 if(box[0].size == 'xs'){
31602 y : y + (this.unitHeight + this.gutter) * 1
31607 y : y + (this.unitHeight + this.gutter) * 2
31611 x : x + (this.unitWidth + this.gutter) * 1,
31625 x : x + (this.unitWidth + this.gutter) * 2,
31630 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31631 y : y + (this.unitHeight + this.gutter) * 1
31635 x : x + (this.unitWidth + this.gutter) * 2,
31636 y : y + (this.unitWidth + this.gutter) * 2
31643 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31647 if(box[0].size == 'md-left'){
31649 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31656 if(box[0].size == 'md-right'){
31658 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31659 y : minY + (this.unitWidth + this.gutter) * 1
31665 var rand = Math.floor(Math.random() * (4 - box[0].y));
31668 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31669 y : minY + (this.unitWidth + this.gutter) * rand
31676 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31680 if(box[0].size == 'xs'){
31683 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31688 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31689 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31697 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31702 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31703 y : minY + (this.unitWidth + this.gutter) * 2
31710 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31714 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31717 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31722 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31723 y : minY + (this.unitWidth + this.gutter) * 1
31727 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31728 y : minY + (this.unitWidth + this.gutter) * 2
31735 if(box[0].size == 'xs' && box[1].size == 'xs'){
31738 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31743 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31748 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31749 y : minY + (this.unitWidth + this.gutter) * 1
31757 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31762 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31763 y : minY + (this.unitWidth + this.gutter) * 2
31767 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31768 y : minY + (this.unitWidth + this.gutter) * 2
31775 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31779 if(box[0].size == 'xs'){
31782 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31787 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31792 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),
31797 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31798 y : minY + (this.unitWidth + this.gutter) * 1
31806 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31811 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31812 y : minY + (this.unitWidth + this.gutter) * 2
31816 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31817 y : minY + (this.unitWidth + this.gutter) * 2
31821 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),
31822 y : minY + (this.unitWidth + this.gutter) * 2
31830 * remove a Masonry Brick
31831 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31833 removeBrick : function(brick_id)
31839 for (var i = 0; i<this.bricks.length; i++) {
31840 if (this.bricks[i].id == brick_id) {
31841 this.bricks.splice(i,1);
31842 this.el.dom.removeChild(Roo.get(brick_id).dom);
31849 * adds a Masonry Brick
31850 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31852 addBrick : function(cfg)
31854 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31855 //this.register(cn);
31856 cn.parentId = this.id;
31857 cn.onRender(this.el, null);
31862 * register a Masonry Brick
31863 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31866 register : function(brick)
31868 this.bricks.push(brick);
31869 brick.masonryId = this.id;
31873 * clear all the Masonry Brick
31875 clearAll : function()
31878 //this.getChildContainer().dom.innerHTML = "";
31879 this.el.dom.innerHTML = '';
31882 getSelected : function()
31884 if (!this.selectedBrick) {
31888 return this.selectedBrick;
31892 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31896 * register a Masonry Layout
31897 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31900 register : function(layout)
31902 this.groups[layout.id] = layout;
31905 * fetch a Masonry Layout based on the masonry layout ID
31906 * @param {string} the masonry layout to add
31907 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31910 get: function(layout_id) {
31911 if (typeof(this.groups[layout_id]) == 'undefined') {
31914 return this.groups[layout_id] ;
31926 * http://masonry.desandro.com
31928 * The idea is to render all the bricks based on vertical width...
31930 * The original code extends 'outlayer' - we might need to use that....
31936 * @class Roo.bootstrap.LayoutMasonryAuto
31937 * @extends Roo.bootstrap.Component
31938 * Bootstrap Layout Masonry class
31941 * Create a new Element
31942 * @param {Object} config The config object
31945 Roo.bootstrap.LayoutMasonryAuto = function(config){
31946 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31949 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31952 * @cfg {Boolean} isFitWidth - resize the width..
31954 isFitWidth : false, // options..
31956 * @cfg {Boolean} isOriginLeft = left align?
31958 isOriginLeft : true,
31960 * @cfg {Boolean} isOriginTop = top align?
31962 isOriginTop : false,
31964 * @cfg {Boolean} isLayoutInstant = no animation?
31966 isLayoutInstant : false, // needed?
31968 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31970 isResizingContainer : true,
31972 * @cfg {Number} columnWidth width of the columns
31978 * @cfg {Number} maxCols maximum number of columns
31983 * @cfg {Number} padHeight padding below box..
31989 * @cfg {Boolean} isAutoInitial defalut true
31992 isAutoInitial : true,
31998 initialColumnWidth : 0,
31999 currentSize : null,
32001 colYs : null, // array.
32008 bricks: null, //CompositeElement
32009 cols : 0, // array?
32010 // element : null, // wrapped now this.el
32011 _isLayoutInited : null,
32014 getAutoCreate : function(){
32018 cls: 'blog-masonary-wrapper ' + this.cls,
32020 cls : 'mas-boxes masonary'
32027 getChildContainer: function( )
32029 if (this.boxesEl) {
32030 return this.boxesEl;
32033 this.boxesEl = this.el.select('.mas-boxes').first();
32035 return this.boxesEl;
32039 initEvents : function()
32043 if(this.isAutoInitial){
32044 Roo.log('hook children rendered');
32045 this.on('childrenrendered', function() {
32046 Roo.log('children rendered');
32053 initial : function()
32055 this.reloadItems();
32057 this.currentSize = this.el.getBox(true);
32059 /// was window resize... - let's see if this works..
32060 Roo.EventManager.onWindowResize(this.resize, this);
32062 if(!this.isAutoInitial){
32067 this.layout.defer(500,this);
32070 reloadItems: function()
32072 this.bricks = this.el.select('.masonry-brick', true);
32074 this.bricks.each(function(b) {
32075 //Roo.log(b.getSize());
32076 if (!b.attr('originalwidth')) {
32077 b.attr('originalwidth', b.getSize().width);
32082 Roo.log(this.bricks.elements.length);
32085 resize : function()
32088 var cs = this.el.getBox(true);
32090 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32091 Roo.log("no change in with or X");
32094 this.currentSize = cs;
32098 layout : function()
32101 this._resetLayout();
32102 //this._manageStamps();
32104 // don't animate first layout
32105 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32106 this.layoutItems( isInstant );
32108 // flag for initalized
32109 this._isLayoutInited = true;
32112 layoutItems : function( isInstant )
32114 //var items = this._getItemsForLayout( this.items );
32115 // original code supports filtering layout items.. we just ignore it..
32117 this._layoutItems( this.bricks , isInstant );
32119 this._postLayout();
32121 _layoutItems : function ( items , isInstant)
32123 //this.fireEvent( 'layout', this, items );
32126 if ( !items || !items.elements.length ) {
32127 // no items, emit event with empty array
32132 items.each(function(item) {
32133 Roo.log("layout item");
32135 // get x/y object from method
32136 var position = this._getItemLayoutPosition( item );
32138 position.item = item;
32139 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32140 queue.push( position );
32143 this._processLayoutQueue( queue );
32145 /** Sets position of item in DOM
32146 * @param {Element} item
32147 * @param {Number} x - horizontal position
32148 * @param {Number} y - vertical position
32149 * @param {Boolean} isInstant - disables transitions
32151 _processLayoutQueue : function( queue )
32153 for ( var i=0, len = queue.length; i < len; i++ ) {
32154 var obj = queue[i];
32155 obj.item.position('absolute');
32156 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32162 * Any logic you want to do after each layout,
32163 * i.e. size the container
32165 _postLayout : function()
32167 this.resizeContainer();
32170 resizeContainer : function()
32172 if ( !this.isResizingContainer ) {
32175 var size = this._getContainerSize();
32177 this.el.setSize(size.width,size.height);
32178 this.boxesEl.setSize(size.width,size.height);
32184 _resetLayout : function()
32186 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32187 this.colWidth = this.el.getWidth();
32188 //this.gutter = this.el.getWidth();
32190 this.measureColumns();
32196 this.colYs.push( 0 );
32202 measureColumns : function()
32204 this.getContainerWidth();
32205 // if columnWidth is 0, default to outerWidth of first item
32206 if ( !this.columnWidth ) {
32207 var firstItem = this.bricks.first();
32208 Roo.log(firstItem);
32209 this.columnWidth = this.containerWidth;
32210 if (firstItem && firstItem.attr('originalwidth') ) {
32211 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32213 // columnWidth fall back to item of first element
32214 Roo.log("set column width?");
32215 this.initialColumnWidth = this.columnWidth ;
32217 // if first elem has no width, default to size of container
32222 if (this.initialColumnWidth) {
32223 this.columnWidth = this.initialColumnWidth;
32228 // column width is fixed at the top - however if container width get's smaller we should
32231 // this bit calcs how man columns..
32233 var columnWidth = this.columnWidth += this.gutter;
32235 // calculate columns
32236 var containerWidth = this.containerWidth + this.gutter;
32238 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32239 // fix rounding errors, typically with gutters
32240 var excess = columnWidth - containerWidth % columnWidth;
32243 // if overshoot is less than a pixel, round up, otherwise floor it
32244 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32245 cols = Math[ mathMethod ]( cols );
32246 this.cols = Math.max( cols, 1 );
32247 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32249 // padding positioning..
32250 var totalColWidth = this.cols * this.columnWidth;
32251 var padavail = this.containerWidth - totalColWidth;
32252 // so for 2 columns - we need 3 'pads'
32254 var padNeeded = (1+this.cols) * this.padWidth;
32256 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32258 this.columnWidth += padExtra
32259 //this.padWidth = Math.floor(padavail / ( this.cols));
32261 // adjust colum width so that padding is fixed??
32263 // we have 3 columns ... total = width * 3
32264 // we have X left over... that should be used by
32266 //if (this.expandC) {
32274 getContainerWidth : function()
32276 /* // container is parent if fit width
32277 var container = this.isFitWidth ? this.element.parentNode : this.element;
32278 // check that this.size and size are there
32279 // IE8 triggers resize on body size change, so they might not be
32281 var size = getSize( container ); //FIXME
32282 this.containerWidth = size && size.innerWidth; //FIXME
32285 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32289 _getItemLayoutPosition : function( item ) // what is item?
32291 // we resize the item to our columnWidth..
32293 item.setWidth(this.columnWidth);
32294 item.autoBoxAdjust = false;
32296 var sz = item.getSize();
32298 // how many columns does this brick span
32299 var remainder = this.containerWidth % this.columnWidth;
32301 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32302 // round if off by 1 pixel, otherwise use ceil
32303 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32304 colSpan = Math.min( colSpan, this.cols );
32306 // normally this should be '1' as we dont' currently allow multi width columns..
32308 var colGroup = this._getColGroup( colSpan );
32309 // get the minimum Y value from the columns
32310 var minimumY = Math.min.apply( Math, colGroup );
32311 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32313 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32315 // position the brick
32317 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32318 y: this.currentSize.y + minimumY + this.padHeight
32322 // apply setHeight to necessary columns
32323 var setHeight = minimumY + sz.height + this.padHeight;
32324 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32326 var setSpan = this.cols + 1 - colGroup.length;
32327 for ( var i = 0; i < setSpan; i++ ) {
32328 this.colYs[ shortColIndex + i ] = setHeight ;
32335 * @param {Number} colSpan - number of columns the element spans
32336 * @returns {Array} colGroup
32338 _getColGroup : function( colSpan )
32340 if ( colSpan < 2 ) {
32341 // if brick spans only one column, use all the column Ys
32346 // how many different places could this brick fit horizontally
32347 var groupCount = this.cols + 1 - colSpan;
32348 // for each group potential horizontal position
32349 for ( var i = 0; i < groupCount; i++ ) {
32350 // make an array of colY values for that one group
32351 var groupColYs = this.colYs.slice( i, i + colSpan );
32352 // and get the max value of the array
32353 colGroup[i] = Math.max.apply( Math, groupColYs );
32358 _manageStamp : function( stamp )
32360 var stampSize = stamp.getSize();
32361 var offset = stamp.getBox();
32362 // get the columns that this stamp affects
32363 var firstX = this.isOriginLeft ? offset.x : offset.right;
32364 var lastX = firstX + stampSize.width;
32365 var firstCol = Math.floor( firstX / this.columnWidth );
32366 firstCol = Math.max( 0, firstCol );
32368 var lastCol = Math.floor( lastX / this.columnWidth );
32369 // lastCol should not go over if multiple of columnWidth #425
32370 lastCol -= lastX % this.columnWidth ? 0 : 1;
32371 lastCol = Math.min( this.cols - 1, lastCol );
32373 // set colYs to bottom of the stamp
32374 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32377 for ( var i = firstCol; i <= lastCol; i++ ) {
32378 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32383 _getContainerSize : function()
32385 this.maxY = Math.max.apply( Math, this.colYs );
32390 if ( this.isFitWidth ) {
32391 size.width = this._getContainerFitWidth();
32397 _getContainerFitWidth : function()
32399 var unusedCols = 0;
32400 // count unused columns
32403 if ( this.colYs[i] !== 0 ) {
32408 // fit container to columns that have been used
32409 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32412 needsResizeLayout : function()
32414 var previousWidth = this.containerWidth;
32415 this.getContainerWidth();
32416 return previousWidth !== this.containerWidth;
32431 * @class Roo.bootstrap.MasonryBrick
32432 * @extends Roo.bootstrap.Component
32433 * Bootstrap MasonryBrick class
32436 * Create a new MasonryBrick
32437 * @param {Object} config The config object
32440 Roo.bootstrap.MasonryBrick = function(config){
32442 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32444 Roo.bootstrap.MasonryBrick.register(this);
32450 * When a MasonryBrick is clcik
32451 * @param {Roo.bootstrap.MasonryBrick} this
32452 * @param {Roo.EventObject} e
32458 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32461 * @cfg {String} title
32465 * @cfg {String} html
32469 * @cfg {String} bgimage
32473 * @cfg {String} videourl
32477 * @cfg {String} cls
32481 * @cfg {String} href
32485 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32490 * @cfg {String} placetitle (center|bottom)
32495 * @cfg {Boolean} isFitContainer defalut true
32497 isFitContainer : true,
32500 * @cfg {Boolean} preventDefault defalut false
32502 preventDefault : false,
32505 * @cfg {Boolean} inverse defalut false
32507 maskInverse : false,
32509 getAutoCreate : function()
32511 if(!this.isFitContainer){
32512 return this.getSplitAutoCreate();
32515 var cls = 'masonry-brick masonry-brick-full';
32517 if(this.href.length){
32518 cls += ' masonry-brick-link';
32521 if(this.bgimage.length){
32522 cls += ' masonry-brick-image';
32525 if(this.maskInverse){
32526 cls += ' mask-inverse';
32529 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32530 cls += ' enable-mask';
32534 cls += ' masonry-' + this.size + '-brick';
32537 if(this.placetitle.length){
32539 switch (this.placetitle) {
32541 cls += ' masonry-center-title';
32544 cls += ' masonry-bottom-title';
32551 if(!this.html.length && !this.bgimage.length){
32552 cls += ' masonry-center-title';
32555 if(!this.html.length && this.bgimage.length){
32556 cls += ' masonry-bottom-title';
32561 cls += ' ' + this.cls;
32565 tag: (this.href.length) ? 'a' : 'div',
32570 cls: 'masonry-brick-mask'
32574 cls: 'masonry-brick-paragraph',
32580 if(this.href.length){
32581 cfg.href = this.href;
32584 var cn = cfg.cn[1].cn;
32586 if(this.title.length){
32589 cls: 'masonry-brick-title',
32594 if(this.html.length){
32597 cls: 'masonry-brick-text',
32602 if (!this.title.length && !this.html.length) {
32603 cfg.cn[1].cls += ' hide';
32606 if(this.bgimage.length){
32609 cls: 'masonry-brick-image-view',
32614 if(this.videourl.length){
32615 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32616 // youtube support only?
32619 cls: 'masonry-brick-image-view',
32622 allowfullscreen : true
32630 getSplitAutoCreate : function()
32632 var cls = 'masonry-brick masonry-brick-split';
32634 if(this.href.length){
32635 cls += ' masonry-brick-link';
32638 if(this.bgimage.length){
32639 cls += ' masonry-brick-image';
32643 cls += ' masonry-' + this.size + '-brick';
32646 switch (this.placetitle) {
32648 cls += ' masonry-center-title';
32651 cls += ' masonry-bottom-title';
32654 if(!this.bgimage.length){
32655 cls += ' masonry-center-title';
32658 if(this.bgimage.length){
32659 cls += ' masonry-bottom-title';
32665 cls += ' ' + this.cls;
32669 tag: (this.href.length) ? 'a' : 'div',
32674 cls: 'masonry-brick-split-head',
32678 cls: 'masonry-brick-paragraph',
32685 cls: 'masonry-brick-split-body',
32691 if(this.href.length){
32692 cfg.href = this.href;
32695 if(this.title.length){
32696 cfg.cn[0].cn[0].cn.push({
32698 cls: 'masonry-brick-title',
32703 if(this.html.length){
32704 cfg.cn[1].cn.push({
32706 cls: 'masonry-brick-text',
32711 if(this.bgimage.length){
32712 cfg.cn[0].cn.push({
32714 cls: 'masonry-brick-image-view',
32719 if(this.videourl.length){
32720 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32721 // youtube support only?
32722 cfg.cn[0].cn.cn.push({
32724 cls: 'masonry-brick-image-view',
32727 allowfullscreen : true
32734 initEvents: function()
32736 switch (this.size) {
32769 this.el.on('touchstart', this.onTouchStart, this);
32770 this.el.on('touchmove', this.onTouchMove, this);
32771 this.el.on('touchend', this.onTouchEnd, this);
32772 this.el.on('contextmenu', this.onContextMenu, this);
32774 this.el.on('mouseenter' ,this.enter, this);
32775 this.el.on('mouseleave', this.leave, this);
32776 this.el.on('click', this.onClick, this);
32779 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32780 this.parent().bricks.push(this);
32785 onClick: function(e, el)
32787 var time = this.endTimer - this.startTimer;
32788 // Roo.log(e.preventDefault());
32791 e.preventDefault();
32796 if(!this.preventDefault){
32800 e.preventDefault();
32802 if (this.activeClass != '') {
32803 this.selectBrick();
32806 this.fireEvent('click', this, e);
32809 enter: function(e, el)
32811 e.preventDefault();
32813 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32817 if(this.bgimage.length && this.html.length){
32818 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32822 leave: function(e, el)
32824 e.preventDefault();
32826 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32830 if(this.bgimage.length && this.html.length){
32831 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32835 onTouchStart: function(e, el)
32837 // e.preventDefault();
32839 this.touchmoved = false;
32841 if(!this.isFitContainer){
32845 if(!this.bgimage.length || !this.html.length){
32849 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32851 this.timer = new Date().getTime();
32855 onTouchMove: function(e, el)
32857 this.touchmoved = true;
32860 onContextMenu : function(e,el)
32862 e.preventDefault();
32863 e.stopPropagation();
32867 onTouchEnd: function(e, el)
32869 // e.preventDefault();
32871 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32878 if(!this.bgimage.length || !this.html.length){
32880 if(this.href.length){
32881 window.location.href = this.href;
32887 if(!this.isFitContainer){
32891 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32893 window.location.href = this.href;
32896 //selection on single brick only
32897 selectBrick : function() {
32899 if (!this.parentId) {
32903 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32904 var index = m.selectedBrick.indexOf(this.id);
32907 m.selectedBrick.splice(index,1);
32908 this.el.removeClass(this.activeClass);
32912 for(var i = 0; i < m.selectedBrick.length; i++) {
32913 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32914 b.el.removeClass(b.activeClass);
32917 m.selectedBrick = [];
32919 m.selectedBrick.push(this.id);
32920 this.el.addClass(this.activeClass);
32924 isSelected : function(){
32925 return this.el.hasClass(this.activeClass);
32930 Roo.apply(Roo.bootstrap.MasonryBrick, {
32933 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32935 * register a Masonry Brick
32936 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32939 register : function(brick)
32941 //this.groups[brick.id] = brick;
32942 this.groups.add(brick.id, brick);
32945 * fetch a masonry brick based on the masonry brick ID
32946 * @param {string} the masonry brick to add
32947 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32950 get: function(brick_id)
32952 // if (typeof(this.groups[brick_id]) == 'undefined') {
32955 // return this.groups[brick_id] ;
32957 if(this.groups.key(brick_id)) {
32958 return this.groups.key(brick_id);
32976 * @class Roo.bootstrap.Brick
32977 * @extends Roo.bootstrap.Component
32978 * Bootstrap Brick class
32981 * Create a new Brick
32982 * @param {Object} config The config object
32985 Roo.bootstrap.Brick = function(config){
32986 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32992 * When a Brick is click
32993 * @param {Roo.bootstrap.Brick} this
32994 * @param {Roo.EventObject} e
33000 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33003 * @cfg {String} title
33007 * @cfg {String} html
33011 * @cfg {String} bgimage
33015 * @cfg {String} cls
33019 * @cfg {String} href
33023 * @cfg {String} video
33027 * @cfg {Boolean} square
33031 getAutoCreate : function()
33033 var cls = 'roo-brick';
33035 if(this.href.length){
33036 cls += ' roo-brick-link';
33039 if(this.bgimage.length){
33040 cls += ' roo-brick-image';
33043 if(!this.html.length && !this.bgimage.length){
33044 cls += ' roo-brick-center-title';
33047 if(!this.html.length && this.bgimage.length){
33048 cls += ' roo-brick-bottom-title';
33052 cls += ' ' + this.cls;
33056 tag: (this.href.length) ? 'a' : 'div',
33061 cls: 'roo-brick-paragraph',
33067 if(this.href.length){
33068 cfg.href = this.href;
33071 var cn = cfg.cn[0].cn;
33073 if(this.title.length){
33076 cls: 'roo-brick-title',
33081 if(this.html.length){
33084 cls: 'roo-brick-text',
33091 if(this.bgimage.length){
33094 cls: 'roo-brick-image-view',
33102 initEvents: function()
33104 if(this.title.length || this.html.length){
33105 this.el.on('mouseenter' ,this.enter, this);
33106 this.el.on('mouseleave', this.leave, this);
33109 Roo.EventManager.onWindowResize(this.resize, this);
33111 if(this.bgimage.length){
33112 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33113 this.imageEl.on('load', this.onImageLoad, this);
33120 onImageLoad : function()
33125 resize : function()
33127 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33129 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33131 if(this.bgimage.length){
33132 var image = this.el.select('.roo-brick-image-view', true).first();
33134 image.setWidth(paragraph.getWidth());
33137 image.setHeight(paragraph.getWidth());
33140 this.el.setHeight(image.getHeight());
33141 paragraph.setHeight(image.getHeight());
33147 enter: function(e, el)
33149 e.preventDefault();
33151 if(this.bgimage.length){
33152 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33153 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33157 leave: function(e, el)
33159 e.preventDefault();
33161 if(this.bgimage.length){
33162 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33163 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33178 * @class Roo.bootstrap.NumberField
33179 * @extends Roo.bootstrap.Input
33180 * Bootstrap NumberField class
33186 * Create a new NumberField
33187 * @param {Object} config The config object
33190 Roo.bootstrap.NumberField = function(config){
33191 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33194 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33197 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33199 allowDecimals : true,
33201 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33203 decimalSeparator : ".",
33205 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33207 decimalPrecision : 2,
33209 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33211 allowNegative : true,
33214 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33218 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33220 minValue : Number.NEGATIVE_INFINITY,
33222 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33224 maxValue : Number.MAX_VALUE,
33226 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33228 minText : "The minimum value for this field is {0}",
33230 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33232 maxText : "The maximum value for this field is {0}",
33234 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33235 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33237 nanText : "{0} is not a valid number",
33239 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33241 thousandsDelimiter : false,
33243 * @cfg {String} valueAlign alignment of value
33245 valueAlign : "left",
33247 getAutoCreate : function()
33249 var hiddenInput = {
33253 cls: 'hidden-number-input'
33257 hiddenInput.name = this.name;
33262 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33264 this.name = hiddenInput.name;
33266 if(cfg.cn.length > 0) {
33267 cfg.cn.push(hiddenInput);
33274 initEvents : function()
33276 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33278 var allowed = "0123456789";
33280 if(this.allowDecimals){
33281 allowed += this.decimalSeparator;
33284 if(this.allowNegative){
33288 if(this.thousandsDelimiter) {
33292 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33294 var keyPress = function(e){
33296 var k = e.getKey();
33298 var c = e.getCharCode();
33301 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33302 allowed.indexOf(String.fromCharCode(c)) === -1
33308 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33312 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33317 this.el.on("keypress", keyPress, this);
33320 validateValue : function(value)
33323 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33327 var num = this.parseValue(value);
33330 this.markInvalid(String.format(this.nanText, value));
33334 if(num < this.minValue){
33335 this.markInvalid(String.format(this.minText, this.minValue));
33339 if(num > this.maxValue){
33340 this.markInvalid(String.format(this.maxText, this.maxValue));
33347 getValue : function()
33349 var v = this.hiddenEl().getValue();
33351 return this.fixPrecision(this.parseValue(v));
33354 parseValue : function(value)
33356 if(this.thousandsDelimiter) {
33358 r = new RegExp(",", "g");
33359 value = value.replace(r, "");
33362 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33363 return isNaN(value) ? '' : value;
33366 fixPrecision : function(value)
33368 if(this.thousandsDelimiter) {
33370 r = new RegExp(",", "g");
33371 value = value.replace(r, "");
33374 var nan = isNaN(value);
33376 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33377 return nan ? '' : value;
33379 return parseFloat(value).toFixed(this.decimalPrecision);
33382 setValue : function(v)
33384 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33390 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33392 this.inputEl().dom.value = (v == '') ? '' :
33393 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33395 if(!this.allowZero && v === '0') {
33396 this.hiddenEl().dom.value = '';
33397 this.inputEl().dom.value = '';
33404 decimalPrecisionFcn : function(v)
33406 return Math.floor(v);
33409 beforeBlur : function()
33411 var v = this.parseValue(this.getRawValue());
33413 if(v || v === 0 || v === ''){
33418 hiddenEl : function()
33420 return this.el.select('input.hidden-number-input',true).first();
33432 * @class Roo.bootstrap.DocumentSlider
33433 * @extends Roo.bootstrap.Component
33434 * Bootstrap DocumentSlider class
33437 * Create a new DocumentViewer
33438 * @param {Object} config The config object
33441 Roo.bootstrap.DocumentSlider = function(config){
33442 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33449 * Fire after initEvent
33450 * @param {Roo.bootstrap.DocumentSlider} this
33455 * Fire after update
33456 * @param {Roo.bootstrap.DocumentSlider} this
33462 * @param {Roo.bootstrap.DocumentSlider} this
33468 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33474 getAutoCreate : function()
33478 cls : 'roo-document-slider',
33482 cls : 'roo-document-slider-header',
33486 cls : 'roo-document-slider-header-title'
33492 cls : 'roo-document-slider-body',
33496 cls : 'roo-document-slider-prev',
33500 cls : 'fa fa-chevron-left'
33506 cls : 'roo-document-slider-thumb',
33510 cls : 'roo-document-slider-image'
33516 cls : 'roo-document-slider-next',
33520 cls : 'fa fa-chevron-right'
33532 initEvents : function()
33534 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33535 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33537 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33538 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33540 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33541 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33543 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33544 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33546 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33547 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33549 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33550 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33552 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33553 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33555 this.thumbEl.on('click', this.onClick, this);
33557 this.prevIndicator.on('click', this.prev, this);
33559 this.nextIndicator.on('click', this.next, this);
33563 initial : function()
33565 if(this.files.length){
33566 this.indicator = 1;
33570 this.fireEvent('initial', this);
33573 update : function()
33575 this.imageEl.attr('src', this.files[this.indicator - 1]);
33577 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33579 this.prevIndicator.show();
33581 if(this.indicator == 1){
33582 this.prevIndicator.hide();
33585 this.nextIndicator.show();
33587 if(this.indicator == this.files.length){
33588 this.nextIndicator.hide();
33591 this.thumbEl.scrollTo('top');
33593 this.fireEvent('update', this);
33596 onClick : function(e)
33598 e.preventDefault();
33600 this.fireEvent('click', this);
33605 e.preventDefault();
33607 this.indicator = Math.max(1, this.indicator - 1);
33614 e.preventDefault();
33616 this.indicator = Math.min(this.files.length, this.indicator + 1);
33630 * @class Roo.bootstrap.RadioSet
33631 * @extends Roo.bootstrap.Input
33632 * Bootstrap RadioSet class
33633 * @cfg {String} indicatorpos (left|right) default left
33634 * @cfg {Boolean} inline (true|false) inline the element (default true)
33635 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33637 * Create a new RadioSet
33638 * @param {Object} config The config object
33641 Roo.bootstrap.RadioSet = function(config){
33643 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33647 Roo.bootstrap.RadioSet.register(this);
33652 * Fires when the element is checked or unchecked.
33653 * @param {Roo.bootstrap.RadioSet} this This radio
33654 * @param {Roo.bootstrap.Radio} item The checked item
33659 * Fires when the element is click.
33660 * @param {Roo.bootstrap.RadioSet} this This radio set
33661 * @param {Roo.bootstrap.Radio} item The checked item
33662 * @param {Roo.EventObject} e The event object
33669 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33677 indicatorpos : 'left',
33679 getAutoCreate : function()
33683 cls : 'roo-radio-set-label',
33687 html : this.fieldLabel
33692 if(this.indicatorpos == 'left'){
33695 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33696 tooltip : 'This field is required'
33701 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33702 tooltip : 'This field is required'
33708 cls : 'roo-radio-set-items'
33711 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33713 if (align === 'left' && this.fieldLabel.length) {
33716 cls : "roo-radio-set-right",
33722 if(this.labelWidth > 12){
33723 label.style = "width: " + this.labelWidth + 'px';
33726 if(this.labelWidth < 13 && this.labelmd == 0){
33727 this.labelmd = this.labelWidth;
33730 if(this.labellg > 0){
33731 label.cls += ' col-lg-' + this.labellg;
33732 items.cls += ' col-lg-' + (12 - this.labellg);
33735 if(this.labelmd > 0){
33736 label.cls += ' col-md-' + this.labelmd;
33737 items.cls += ' col-md-' + (12 - this.labelmd);
33740 if(this.labelsm > 0){
33741 label.cls += ' col-sm-' + this.labelsm;
33742 items.cls += ' col-sm-' + (12 - this.labelsm);
33745 if(this.labelxs > 0){
33746 label.cls += ' col-xs-' + this.labelxs;
33747 items.cls += ' col-xs-' + (12 - this.labelxs);
33753 cls : 'roo-radio-set',
33757 cls : 'roo-radio-set-input',
33760 value : this.value ? this.value : ''
33767 if(this.weight.length){
33768 cfg.cls += ' roo-radio-' + this.weight;
33772 cfg.cls += ' roo-radio-set-inline';
33776 ['xs','sm','md','lg'].map(function(size){
33777 if (settings[size]) {
33778 cfg.cls += ' col-' + size + '-' + settings[size];
33786 initEvents : function()
33788 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33789 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33791 if(!this.fieldLabel.length){
33792 this.labelEl.hide();
33795 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33796 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33798 this.indicator = this.indicatorEl();
33800 if(this.indicator){
33801 this.indicator.addClass('invisible');
33804 this.originalValue = this.getValue();
33808 inputEl: function ()
33810 return this.el.select('.roo-radio-set-input', true).first();
33813 getChildContainer : function()
33815 return this.itemsEl;
33818 register : function(item)
33820 this.radioes.push(item);
33824 validate : function()
33826 if(this.getVisibilityEl().hasClass('hidden')){
33832 Roo.each(this.radioes, function(i){
33841 if(this.allowBlank) {
33845 if(this.disabled || valid){
33850 this.markInvalid();
33855 markValid : function()
33857 if(this.labelEl.isVisible(true)){
33858 this.indicatorEl().removeClass('visible');
33859 this.indicatorEl().addClass('invisible');
33862 this.el.removeClass([this.invalidClass, this.validClass]);
33863 this.el.addClass(this.validClass);
33865 this.fireEvent('valid', this);
33868 markInvalid : function(msg)
33870 if(this.allowBlank || this.disabled){
33874 if(this.labelEl.isVisible(true)){
33875 this.indicatorEl().removeClass('invisible');
33876 this.indicatorEl().addClass('visible');
33879 this.el.removeClass([this.invalidClass, this.validClass]);
33880 this.el.addClass(this.invalidClass);
33882 this.fireEvent('invalid', this, msg);
33886 setValue : function(v, suppressEvent)
33888 if(this.value === v){
33895 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33898 Roo.each(this.radioes, function(i){
33900 i.el.removeClass('checked');
33903 Roo.each(this.radioes, function(i){
33905 if(i.value === v || i.value.toString() === v.toString()){
33907 i.el.addClass('checked');
33909 if(suppressEvent !== true){
33910 this.fireEvent('check', this, i);
33921 clearInvalid : function(){
33923 if(!this.el || this.preventMark){
33927 this.el.removeClass([this.invalidClass]);
33929 this.fireEvent('valid', this);
33934 Roo.apply(Roo.bootstrap.RadioSet, {
33938 register : function(set)
33940 this.groups[set.name] = set;
33943 get: function(name)
33945 if (typeof(this.groups[name]) == 'undefined') {
33949 return this.groups[name] ;
33955 * Ext JS Library 1.1.1
33956 * Copyright(c) 2006-2007, Ext JS, LLC.
33958 * Originally Released Under LGPL - original licence link has changed is not relivant.
33961 * <script type="text/javascript">
33966 * @class Roo.bootstrap.SplitBar
33967 * @extends Roo.util.Observable
33968 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33972 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33973 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33974 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33975 split.minSize = 100;
33976 split.maxSize = 600;
33977 split.animate = true;
33978 split.on('moved', splitterMoved);
33981 * Create a new SplitBar
33982 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33983 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33984 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33985 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33986 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33987 position of the SplitBar).
33989 Roo.bootstrap.SplitBar = function(cfg){
33994 // dragElement : elm
33995 // resizingElement: el,
33997 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33998 // placement : Roo.bootstrap.SplitBar.LEFT ,
33999 // existingProxy ???
34002 this.el = Roo.get(cfg.dragElement, true);
34003 this.el.dom.unselectable = "on";
34005 this.resizingEl = Roo.get(cfg.resizingElement, true);
34009 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34010 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34013 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34016 * The minimum size of the resizing element. (Defaults to 0)
34022 * The maximum size of the resizing element. (Defaults to 2000)
34025 this.maxSize = 2000;
34028 * Whether to animate the transition to the new size
34031 this.animate = false;
34034 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34037 this.useShim = false;
34042 if(!cfg.existingProxy){
34044 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34046 this.proxy = Roo.get(cfg.existingProxy).dom;
34049 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34052 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34055 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34058 this.dragSpecs = {};
34061 * @private The adapter to use to positon and resize elements
34063 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34064 this.adapter.init(this);
34066 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34068 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34069 this.el.addClass("roo-splitbar-h");
34072 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34073 this.el.addClass("roo-splitbar-v");
34079 * Fires when the splitter is moved (alias for {@link #event-moved})
34080 * @param {Roo.bootstrap.SplitBar} this
34081 * @param {Number} newSize the new width or height
34086 * Fires when the splitter is moved
34087 * @param {Roo.bootstrap.SplitBar} this
34088 * @param {Number} newSize the new width or height
34092 * @event beforeresize
34093 * Fires before the splitter is dragged
34094 * @param {Roo.bootstrap.SplitBar} this
34096 "beforeresize" : true,
34098 "beforeapply" : true
34101 Roo.util.Observable.call(this);
34104 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34105 onStartProxyDrag : function(x, y){
34106 this.fireEvent("beforeresize", this);
34108 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34110 o.enableDisplayMode("block");
34111 // all splitbars share the same overlay
34112 Roo.bootstrap.SplitBar.prototype.overlay = o;
34114 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34115 this.overlay.show();
34116 Roo.get(this.proxy).setDisplayed("block");
34117 var size = this.adapter.getElementSize(this);
34118 this.activeMinSize = this.getMinimumSize();;
34119 this.activeMaxSize = this.getMaximumSize();;
34120 var c1 = size - this.activeMinSize;
34121 var c2 = Math.max(this.activeMaxSize - size, 0);
34122 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34123 this.dd.resetConstraints();
34124 this.dd.setXConstraint(
34125 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34126 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34128 this.dd.setYConstraint(0, 0);
34130 this.dd.resetConstraints();
34131 this.dd.setXConstraint(0, 0);
34132 this.dd.setYConstraint(
34133 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34134 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34137 this.dragSpecs.startSize = size;
34138 this.dragSpecs.startPoint = [x, y];
34139 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34143 * @private Called after the drag operation by the DDProxy
34145 onEndProxyDrag : function(e){
34146 Roo.get(this.proxy).setDisplayed(false);
34147 var endPoint = Roo.lib.Event.getXY(e);
34149 this.overlay.hide();
34152 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34153 newSize = this.dragSpecs.startSize +
34154 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34155 endPoint[0] - this.dragSpecs.startPoint[0] :
34156 this.dragSpecs.startPoint[0] - endPoint[0]
34159 newSize = this.dragSpecs.startSize +
34160 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34161 endPoint[1] - this.dragSpecs.startPoint[1] :
34162 this.dragSpecs.startPoint[1] - endPoint[1]
34165 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34166 if(newSize != this.dragSpecs.startSize){
34167 if(this.fireEvent('beforeapply', this, newSize) !== false){
34168 this.adapter.setElementSize(this, newSize);
34169 this.fireEvent("moved", this, newSize);
34170 this.fireEvent("resize", this, newSize);
34176 * Get the adapter this SplitBar uses
34177 * @return The adapter object
34179 getAdapter : function(){
34180 return this.adapter;
34184 * Set the adapter this SplitBar uses
34185 * @param {Object} adapter A SplitBar adapter object
34187 setAdapter : function(adapter){
34188 this.adapter = adapter;
34189 this.adapter.init(this);
34193 * Gets the minimum size for the resizing element
34194 * @return {Number} The minimum size
34196 getMinimumSize : function(){
34197 return this.minSize;
34201 * Sets the minimum size for the resizing element
34202 * @param {Number} minSize The minimum size
34204 setMinimumSize : function(minSize){
34205 this.minSize = minSize;
34209 * Gets the maximum size for the resizing element
34210 * @return {Number} The maximum size
34212 getMaximumSize : function(){
34213 return this.maxSize;
34217 * Sets the maximum size for the resizing element
34218 * @param {Number} maxSize The maximum size
34220 setMaximumSize : function(maxSize){
34221 this.maxSize = maxSize;
34225 * Sets the initialize size for the resizing element
34226 * @param {Number} size The initial size
34228 setCurrentSize : function(size){
34229 var oldAnimate = this.animate;
34230 this.animate = false;
34231 this.adapter.setElementSize(this, size);
34232 this.animate = oldAnimate;
34236 * Destroy this splitbar.
34237 * @param {Boolean} removeEl True to remove the element
34239 destroy : function(removeEl){
34241 this.shim.remove();
34244 this.proxy.parentNode.removeChild(this.proxy);
34252 * @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.
34254 Roo.bootstrap.SplitBar.createProxy = function(dir){
34255 var proxy = new Roo.Element(document.createElement("div"));
34256 proxy.unselectable();
34257 var cls = 'roo-splitbar-proxy';
34258 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34259 document.body.appendChild(proxy.dom);
34264 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34265 * Default Adapter. It assumes the splitter and resizing element are not positioned
34266 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34268 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34271 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34272 // do nothing for now
34273 init : function(s){
34277 * Called before drag operations to get the current size of the resizing element.
34278 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34280 getElementSize : function(s){
34281 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34282 return s.resizingEl.getWidth();
34284 return s.resizingEl.getHeight();
34289 * Called after drag operations to set the size of the resizing element.
34290 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34291 * @param {Number} newSize The new size to set
34292 * @param {Function} onComplete A function to be invoked when resizing is complete
34294 setElementSize : function(s, newSize, onComplete){
34295 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34297 s.resizingEl.setWidth(newSize);
34299 onComplete(s, newSize);
34302 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34307 s.resizingEl.setHeight(newSize);
34309 onComplete(s, newSize);
34312 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34319 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34320 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34321 * Adapter that moves the splitter element to align with the resized sizing element.
34322 * Used with an absolute positioned SplitBar.
34323 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34324 * document.body, make sure you assign an id to the body element.
34326 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34327 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34328 this.container = Roo.get(container);
34331 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34332 init : function(s){
34333 this.basic.init(s);
34336 getElementSize : function(s){
34337 return this.basic.getElementSize(s);
34340 setElementSize : function(s, newSize, onComplete){
34341 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34344 moveSplitter : function(s){
34345 var yes = Roo.bootstrap.SplitBar;
34346 switch(s.placement){
34348 s.el.setX(s.resizingEl.getRight());
34351 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34354 s.el.setY(s.resizingEl.getBottom());
34357 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34364 * Orientation constant - Create a vertical SplitBar
34368 Roo.bootstrap.SplitBar.VERTICAL = 1;
34371 * Orientation constant - Create a horizontal SplitBar
34375 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34378 * Placement constant - The resizing element is to the left of the splitter element
34382 Roo.bootstrap.SplitBar.LEFT = 1;
34385 * Placement constant - The resizing element is to the right of the splitter element
34389 Roo.bootstrap.SplitBar.RIGHT = 2;
34392 * Placement constant - The resizing element is positioned above the splitter element
34396 Roo.bootstrap.SplitBar.TOP = 3;
34399 * Placement constant - The resizing element is positioned under splitter element
34403 Roo.bootstrap.SplitBar.BOTTOM = 4;
34404 Roo.namespace("Roo.bootstrap.layout");/*
34406 * Ext JS Library 1.1.1
34407 * Copyright(c) 2006-2007, Ext JS, LLC.
34409 * Originally Released Under LGPL - original licence link has changed is not relivant.
34412 * <script type="text/javascript">
34416 * @class Roo.bootstrap.layout.Manager
34417 * @extends Roo.bootstrap.Component
34418 * Base class for layout managers.
34420 Roo.bootstrap.layout.Manager = function(config)
34422 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34428 /** false to disable window resize monitoring @type Boolean */
34429 this.monitorWindowResize = true;
34434 * Fires when a layout is performed.
34435 * @param {Roo.LayoutManager} this
34439 * @event regionresized
34440 * Fires when the user resizes a region.
34441 * @param {Roo.LayoutRegion} region The resized region
34442 * @param {Number} newSize The new size (width for east/west, height for north/south)
34444 "regionresized" : true,
34446 * @event regioncollapsed
34447 * Fires when a region is collapsed.
34448 * @param {Roo.LayoutRegion} region The collapsed region
34450 "regioncollapsed" : true,
34452 * @event regionexpanded
34453 * Fires when a region is expanded.
34454 * @param {Roo.LayoutRegion} region The expanded region
34456 "regionexpanded" : true
34458 this.updating = false;
34461 this.el = Roo.get(config.el);
34467 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34472 monitorWindowResize : true,
34478 onRender : function(ct, position)
34481 this.el = Roo.get(ct);
34484 //this.fireEvent('render',this);
34488 initEvents: function()
34492 // ie scrollbar fix
34493 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34494 document.body.scroll = "no";
34495 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34496 this.el.position('relative');
34498 this.id = this.el.id;
34499 this.el.addClass("roo-layout-container");
34500 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34501 if(this.el.dom != document.body ) {
34502 this.el.on('resize', this.layout,this);
34503 this.el.on('show', this.layout,this);
34509 * Returns true if this layout is currently being updated
34510 * @return {Boolean}
34512 isUpdating : function(){
34513 return this.updating;
34517 * Suspend the LayoutManager from doing auto-layouts while
34518 * making multiple add or remove calls
34520 beginUpdate : function(){
34521 this.updating = true;
34525 * Restore auto-layouts and optionally disable the manager from performing a layout
34526 * @param {Boolean} noLayout true to disable a layout update
34528 endUpdate : function(noLayout){
34529 this.updating = false;
34535 layout: function(){
34539 onRegionResized : function(region, newSize){
34540 this.fireEvent("regionresized", region, newSize);
34544 onRegionCollapsed : function(region){
34545 this.fireEvent("regioncollapsed", region);
34548 onRegionExpanded : function(region){
34549 this.fireEvent("regionexpanded", region);
34553 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34554 * performs box-model adjustments.
34555 * @return {Object} The size as an object {width: (the width), height: (the height)}
34557 getViewSize : function()
34560 if(this.el.dom != document.body){
34561 size = this.el.getSize();
34563 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34565 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34566 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34571 * Returns the Element this layout is bound to.
34572 * @return {Roo.Element}
34574 getEl : function(){
34579 * Returns the specified region.
34580 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34581 * @return {Roo.LayoutRegion}
34583 getRegion : function(target){
34584 return this.regions[target.toLowerCase()];
34587 onWindowResize : function(){
34588 if(this.monitorWindowResize){
34595 * Ext JS Library 1.1.1
34596 * Copyright(c) 2006-2007, Ext JS, LLC.
34598 * Originally Released Under LGPL - original licence link has changed is not relivant.
34601 * <script type="text/javascript">
34604 * @class Roo.bootstrap.layout.Border
34605 * @extends Roo.bootstrap.layout.Manager
34606 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34607 * please see: examples/bootstrap/nested.html<br><br>
34609 <b>The container the layout is rendered into can be either the body element or any other element.
34610 If it is not the body element, the container needs to either be an absolute positioned element,
34611 or you will need to add "position:relative" to the css of the container. You will also need to specify
34612 the container size if it is not the body element.</b>
34615 * Create a new Border
34616 * @param {Object} config Configuration options
34618 Roo.bootstrap.layout.Border = function(config){
34619 config = config || {};
34620 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34624 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34625 if(config[region]){
34626 config[region].region = region;
34627 this.addRegion(config[region]);
34633 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34635 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34637 * Creates and adds a new region if it doesn't already exist.
34638 * @param {String} target The target region key (north, south, east, west or center).
34639 * @param {Object} config The regions config object
34640 * @return {BorderLayoutRegion} The new region
34642 addRegion : function(config)
34644 if(!this.regions[config.region]){
34645 var r = this.factory(config);
34646 this.bindRegion(r);
34648 return this.regions[config.region];
34652 bindRegion : function(r){
34653 this.regions[r.config.region] = r;
34655 r.on("visibilitychange", this.layout, this);
34656 r.on("paneladded", this.layout, this);
34657 r.on("panelremoved", this.layout, this);
34658 r.on("invalidated", this.layout, this);
34659 r.on("resized", this.onRegionResized, this);
34660 r.on("collapsed", this.onRegionCollapsed, this);
34661 r.on("expanded", this.onRegionExpanded, this);
34665 * Performs a layout update.
34667 layout : function()
34669 if(this.updating) {
34673 // render all the rebions if they have not been done alreayd?
34674 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34675 if(this.regions[region] && !this.regions[region].bodyEl){
34676 this.regions[region].onRender(this.el)
34680 var size = this.getViewSize();
34681 var w = size.width;
34682 var h = size.height;
34687 //var x = 0, y = 0;
34689 var rs = this.regions;
34690 var north = rs["north"];
34691 var south = rs["south"];
34692 var west = rs["west"];
34693 var east = rs["east"];
34694 var center = rs["center"];
34695 //if(this.hideOnLayout){ // not supported anymore
34696 //c.el.setStyle("display", "none");
34698 if(north && north.isVisible()){
34699 var b = north.getBox();
34700 var m = north.getMargins();
34701 b.width = w - (m.left+m.right);
34704 centerY = b.height + b.y + m.bottom;
34705 centerH -= centerY;
34706 north.updateBox(this.safeBox(b));
34708 if(south && south.isVisible()){
34709 var b = south.getBox();
34710 var m = south.getMargins();
34711 b.width = w - (m.left+m.right);
34713 var totalHeight = (b.height + m.top + m.bottom);
34714 b.y = h - totalHeight + m.top;
34715 centerH -= totalHeight;
34716 south.updateBox(this.safeBox(b));
34718 if(west && west.isVisible()){
34719 var b = west.getBox();
34720 var m = west.getMargins();
34721 b.height = centerH - (m.top+m.bottom);
34723 b.y = centerY + m.top;
34724 var totalWidth = (b.width + m.left + m.right);
34725 centerX += totalWidth;
34726 centerW -= totalWidth;
34727 west.updateBox(this.safeBox(b));
34729 if(east && east.isVisible()){
34730 var b = east.getBox();
34731 var m = east.getMargins();
34732 b.height = centerH - (m.top+m.bottom);
34733 var totalWidth = (b.width + m.left + m.right);
34734 b.x = w - totalWidth + m.left;
34735 b.y = centerY + m.top;
34736 centerW -= totalWidth;
34737 east.updateBox(this.safeBox(b));
34740 var m = center.getMargins();
34742 x: centerX + m.left,
34743 y: centerY + m.top,
34744 width: centerW - (m.left+m.right),
34745 height: centerH - (m.top+m.bottom)
34747 //if(this.hideOnLayout){
34748 //center.el.setStyle("display", "block");
34750 center.updateBox(this.safeBox(centerBox));
34753 this.fireEvent("layout", this);
34757 safeBox : function(box){
34758 box.width = Math.max(0, box.width);
34759 box.height = Math.max(0, box.height);
34764 * Adds a ContentPanel (or subclass) to this layout.
34765 * @param {String} target The target region key (north, south, east, west or center).
34766 * @param {Roo.ContentPanel} panel The panel to add
34767 * @return {Roo.ContentPanel} The added panel
34769 add : function(target, panel){
34771 target = target.toLowerCase();
34772 return this.regions[target].add(panel);
34776 * Remove a ContentPanel (or subclass) to this layout.
34777 * @param {String} target The target region key (north, south, east, west or center).
34778 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34779 * @return {Roo.ContentPanel} The removed panel
34781 remove : function(target, panel){
34782 target = target.toLowerCase();
34783 return this.regions[target].remove(panel);
34787 * Searches all regions for a panel with the specified id
34788 * @param {String} panelId
34789 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34791 findPanel : function(panelId){
34792 var rs = this.regions;
34793 for(var target in rs){
34794 if(typeof rs[target] != "function"){
34795 var p = rs[target].getPanel(panelId);
34805 * Searches all regions for a panel with the specified id and activates (shows) it.
34806 * @param {String/ContentPanel} panelId The panels id or the panel itself
34807 * @return {Roo.ContentPanel} The shown panel or null
34809 showPanel : function(panelId) {
34810 var rs = this.regions;
34811 for(var target in rs){
34812 var r = rs[target];
34813 if(typeof r != "function"){
34814 if(r.hasPanel(panelId)){
34815 return r.showPanel(panelId);
34823 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34824 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34827 restoreState : function(provider){
34829 provider = Roo.state.Manager;
34831 var sm = new Roo.LayoutStateManager();
34832 sm.init(this, provider);
34838 * Adds a xtype elements to the layout.
34842 xtype : 'ContentPanel',
34849 xtype : 'NestedLayoutPanel',
34855 items : [ ... list of content panels or nested layout panels.. ]
34859 * @param {Object} cfg Xtype definition of item to add.
34861 addxtype : function(cfg)
34863 // basically accepts a pannel...
34864 // can accept a layout region..!?!?
34865 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34868 // theory? children can only be panels??
34870 //if (!cfg.xtype.match(/Panel$/)) {
34875 if (typeof(cfg.region) == 'undefined') {
34876 Roo.log("Failed to add Panel, region was not set");
34880 var region = cfg.region;
34886 xitems = cfg.items;
34893 case 'Content': // ContentPanel (el, cfg)
34894 case 'Scroll': // ContentPanel (el, cfg)
34896 cfg.autoCreate = true;
34897 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34899 // var el = this.el.createChild();
34900 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34903 this.add(region, ret);
34907 case 'TreePanel': // our new panel!
34908 cfg.el = this.el.createChild();
34909 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34910 this.add(region, ret);
34915 // create a new Layout (which is a Border Layout...
34917 var clayout = cfg.layout;
34918 clayout.el = this.el.createChild();
34919 clayout.items = clayout.items || [];
34923 // replace this exitems with the clayout ones..
34924 xitems = clayout.items;
34926 // force background off if it's in center...
34927 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34928 cfg.background = false;
34930 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34933 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34934 //console.log('adding nested layout panel ' + cfg.toSource());
34935 this.add(region, ret);
34936 nb = {}; /// find first...
34941 // needs grid and region
34943 //var el = this.getRegion(region).el.createChild();
34945 *var el = this.el.createChild();
34946 // create the grid first...
34947 cfg.grid.container = el;
34948 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34951 if (region == 'center' && this.active ) {
34952 cfg.background = false;
34955 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34957 this.add(region, ret);
34959 if (cfg.background) {
34960 // render grid on panel activation (if panel background)
34961 ret.on('activate', function(gp) {
34962 if (!gp.grid.rendered) {
34963 // gp.grid.render(el);
34967 // cfg.grid.render(el);
34973 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34974 // it was the old xcomponent building that caused this before.
34975 // espeically if border is the top element in the tree.
34985 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34987 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34988 this.add(region, ret);
34992 throw "Can not add '" + cfg.xtype + "' to Border";
34998 this.beginUpdate();
35002 Roo.each(xitems, function(i) {
35003 region = nb && i.region ? i.region : false;
35005 var add = ret.addxtype(i);
35008 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35009 if (!i.background) {
35010 abn[region] = nb[region] ;
35017 // make the last non-background panel active..
35018 //if (nb) { Roo.log(abn); }
35021 for(var r in abn) {
35022 region = this.getRegion(r);
35024 // tried using nb[r], but it does not work..
35026 region.showPanel(abn[r]);
35037 factory : function(cfg)
35040 var validRegions = Roo.bootstrap.layout.Border.regions;
35042 var target = cfg.region;
35045 var r = Roo.bootstrap.layout;
35049 return new r.North(cfg);
35051 return new r.South(cfg);
35053 return new r.East(cfg);
35055 return new r.West(cfg);
35057 return new r.Center(cfg);
35059 throw 'Layout region "'+target+'" not supported.';
35066 * Ext JS Library 1.1.1
35067 * Copyright(c) 2006-2007, Ext JS, LLC.
35069 * Originally Released Under LGPL - original licence link has changed is not relivant.
35072 * <script type="text/javascript">
35076 * @class Roo.bootstrap.layout.Basic
35077 * @extends Roo.util.Observable
35078 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35079 * and does not have a titlebar, tabs or any other features. All it does is size and position
35080 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35081 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35082 * @cfg {string} region the region that it inhabits..
35083 * @cfg {bool} skipConfig skip config?
35087 Roo.bootstrap.layout.Basic = function(config){
35089 this.mgr = config.mgr;
35091 this.position = config.region;
35093 var skipConfig = config.skipConfig;
35097 * @scope Roo.BasicLayoutRegion
35101 * @event beforeremove
35102 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35103 * @param {Roo.LayoutRegion} this
35104 * @param {Roo.ContentPanel} panel The panel
35105 * @param {Object} e The cancel event object
35107 "beforeremove" : true,
35109 * @event invalidated
35110 * Fires when the layout for this region is changed.
35111 * @param {Roo.LayoutRegion} this
35113 "invalidated" : true,
35115 * @event visibilitychange
35116 * Fires when this region is shown or hidden
35117 * @param {Roo.LayoutRegion} this
35118 * @param {Boolean} visibility true or false
35120 "visibilitychange" : true,
35122 * @event paneladded
35123 * Fires when a panel is added.
35124 * @param {Roo.LayoutRegion} this
35125 * @param {Roo.ContentPanel} panel The panel
35127 "paneladded" : true,
35129 * @event panelremoved
35130 * Fires when a panel is removed.
35131 * @param {Roo.LayoutRegion} this
35132 * @param {Roo.ContentPanel} panel The panel
35134 "panelremoved" : true,
35136 * @event beforecollapse
35137 * Fires when this region before collapse.
35138 * @param {Roo.LayoutRegion} this
35140 "beforecollapse" : true,
35143 * Fires when this region is collapsed.
35144 * @param {Roo.LayoutRegion} this
35146 "collapsed" : true,
35149 * Fires when this region is expanded.
35150 * @param {Roo.LayoutRegion} this
35155 * Fires when this region is slid into view.
35156 * @param {Roo.LayoutRegion} this
35158 "slideshow" : true,
35161 * Fires when this region slides out of view.
35162 * @param {Roo.LayoutRegion} this
35164 "slidehide" : true,
35166 * @event panelactivated
35167 * Fires when a panel is activated.
35168 * @param {Roo.LayoutRegion} this
35169 * @param {Roo.ContentPanel} panel The activated panel
35171 "panelactivated" : true,
35174 * Fires when the user resizes this region.
35175 * @param {Roo.LayoutRegion} this
35176 * @param {Number} newSize The new size (width for east/west, height for north/south)
35180 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35181 this.panels = new Roo.util.MixedCollection();
35182 this.panels.getKey = this.getPanelId.createDelegate(this);
35184 this.activePanel = null;
35185 // ensure listeners are added...
35187 if (config.listeners || config.events) {
35188 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35189 listeners : config.listeners || {},
35190 events : config.events || {}
35194 if(skipConfig !== true){
35195 this.applyConfig(config);
35199 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35201 getPanelId : function(p){
35205 applyConfig : function(config){
35206 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35207 this.config = config;
35212 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35213 * the width, for horizontal (north, south) the height.
35214 * @param {Number} newSize The new width or height
35216 resizeTo : function(newSize){
35217 var el = this.el ? this.el :
35218 (this.activePanel ? this.activePanel.getEl() : null);
35220 switch(this.position){
35223 el.setWidth(newSize);
35224 this.fireEvent("resized", this, newSize);
35228 el.setHeight(newSize);
35229 this.fireEvent("resized", this, newSize);
35235 getBox : function(){
35236 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35239 getMargins : function(){
35240 return this.margins;
35243 updateBox : function(box){
35245 var el = this.activePanel.getEl();
35246 el.dom.style.left = box.x + "px";
35247 el.dom.style.top = box.y + "px";
35248 this.activePanel.setSize(box.width, box.height);
35252 * Returns the container element for this region.
35253 * @return {Roo.Element}
35255 getEl : function(){
35256 return this.activePanel;
35260 * Returns true if this region is currently visible.
35261 * @return {Boolean}
35263 isVisible : function(){
35264 return this.activePanel ? true : false;
35267 setActivePanel : function(panel){
35268 panel = this.getPanel(panel);
35269 if(this.activePanel && this.activePanel != panel){
35270 this.activePanel.setActiveState(false);
35271 this.activePanel.getEl().setLeftTop(-10000,-10000);
35273 this.activePanel = panel;
35274 panel.setActiveState(true);
35276 panel.setSize(this.box.width, this.box.height);
35278 this.fireEvent("panelactivated", this, panel);
35279 this.fireEvent("invalidated");
35283 * Show the specified panel.
35284 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35285 * @return {Roo.ContentPanel} The shown panel or null
35287 showPanel : function(panel){
35288 panel = this.getPanel(panel);
35290 this.setActivePanel(panel);
35296 * Get the active panel for this region.
35297 * @return {Roo.ContentPanel} The active panel or null
35299 getActivePanel : function(){
35300 return this.activePanel;
35304 * Add the passed ContentPanel(s)
35305 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35306 * @return {Roo.ContentPanel} The panel added (if only one was added)
35308 add : function(panel){
35309 if(arguments.length > 1){
35310 for(var i = 0, len = arguments.length; i < len; i++) {
35311 this.add(arguments[i]);
35315 if(this.hasPanel(panel)){
35316 this.showPanel(panel);
35319 var el = panel.getEl();
35320 if(el.dom.parentNode != this.mgr.el.dom){
35321 this.mgr.el.dom.appendChild(el.dom);
35323 if(panel.setRegion){
35324 panel.setRegion(this);
35326 this.panels.add(panel);
35327 el.setStyle("position", "absolute");
35328 if(!panel.background){
35329 this.setActivePanel(panel);
35330 if(this.config.initialSize && this.panels.getCount()==1){
35331 this.resizeTo(this.config.initialSize);
35334 this.fireEvent("paneladded", this, panel);
35339 * Returns true if the panel is in this region.
35340 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35341 * @return {Boolean}
35343 hasPanel : function(panel){
35344 if(typeof panel == "object"){ // must be panel obj
35345 panel = panel.getId();
35347 return this.getPanel(panel) ? true : false;
35351 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35352 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35353 * @param {Boolean} preservePanel Overrides the config preservePanel option
35354 * @return {Roo.ContentPanel} The panel that was removed
35356 remove : function(panel, preservePanel){
35357 panel = this.getPanel(panel);
35362 this.fireEvent("beforeremove", this, panel, e);
35363 if(e.cancel === true){
35366 var panelId = panel.getId();
35367 this.panels.removeKey(panelId);
35372 * Returns the panel specified or null if it's not in this region.
35373 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35374 * @return {Roo.ContentPanel}
35376 getPanel : function(id){
35377 if(typeof id == "object"){ // must be panel obj
35380 return this.panels.get(id);
35384 * Returns this regions position (north/south/east/west/center).
35387 getPosition: function(){
35388 return this.position;
35392 * Ext JS Library 1.1.1
35393 * Copyright(c) 2006-2007, Ext JS, LLC.
35395 * Originally Released Under LGPL - original licence link has changed is not relivant.
35398 * <script type="text/javascript">
35402 * @class Roo.bootstrap.layout.Region
35403 * @extends Roo.bootstrap.layout.Basic
35404 * This class represents a region in a layout manager.
35406 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35407 * @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})
35408 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35409 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35410 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35411 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35412 * @cfg {String} title The title for the region (overrides panel titles)
35413 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35414 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35415 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35416 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35417 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35418 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35419 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35420 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35421 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35422 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35424 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35425 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35426 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35427 * @cfg {Number} width For East/West panels
35428 * @cfg {Number} height For North/South panels
35429 * @cfg {Boolean} split To show the splitter
35430 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35432 * @cfg {string} cls Extra CSS classes to add to region
35434 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35435 * @cfg {string} region the region that it inhabits..
35438 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35439 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35441 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35442 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35443 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35445 Roo.bootstrap.layout.Region = function(config)
35447 this.applyConfig(config);
35449 var mgr = config.mgr;
35450 var pos = config.region;
35451 config.skipConfig = true;
35452 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35455 this.onRender(mgr.el);
35458 this.visible = true;
35459 this.collapsed = false;
35460 this.unrendered_panels = [];
35463 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35465 position: '', // set by wrapper (eg. north/south etc..)
35466 unrendered_panels : null, // unrendered panels.
35467 createBody : function(){
35468 /** This region's body element
35469 * @type Roo.Element */
35470 this.bodyEl = this.el.createChild({
35472 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35476 onRender: function(ctr, pos)
35478 var dh = Roo.DomHelper;
35479 /** This region's container element
35480 * @type Roo.Element */
35481 this.el = dh.append(ctr.dom, {
35483 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35485 /** This region's title element
35486 * @type Roo.Element */
35488 this.titleEl = dh.append(this.el.dom,
35491 unselectable: "on",
35492 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35494 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35495 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35498 this.titleEl.enableDisplayMode();
35499 /** This region's title text element
35500 * @type HTMLElement */
35501 this.titleTextEl = this.titleEl.dom.firstChild;
35502 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35504 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35505 this.closeBtn.enableDisplayMode();
35506 this.closeBtn.on("click", this.closeClicked, this);
35507 this.closeBtn.hide();
35509 this.createBody(this.config);
35510 if(this.config.hideWhenEmpty){
35512 this.on("paneladded", this.validateVisibility, this);
35513 this.on("panelremoved", this.validateVisibility, this);
35515 if(this.autoScroll){
35516 this.bodyEl.setStyle("overflow", "auto");
35518 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35520 //if(c.titlebar !== false){
35521 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35522 this.titleEl.hide();
35524 this.titleEl.show();
35525 if(this.config.title){
35526 this.titleTextEl.innerHTML = this.config.title;
35530 if(this.config.collapsed){
35531 this.collapse(true);
35533 if(this.config.hidden){
35537 if (this.unrendered_panels && this.unrendered_panels.length) {
35538 for (var i =0;i< this.unrendered_panels.length; i++) {
35539 this.add(this.unrendered_panels[i]);
35541 this.unrendered_panels = null;
35547 applyConfig : function(c)
35550 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35551 var dh = Roo.DomHelper;
35552 if(c.titlebar !== false){
35553 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35554 this.collapseBtn.on("click", this.collapse, this);
35555 this.collapseBtn.enableDisplayMode();
35557 if(c.showPin === true || this.showPin){
35558 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35559 this.stickBtn.enableDisplayMode();
35560 this.stickBtn.on("click", this.expand, this);
35561 this.stickBtn.hide();
35566 /** This region's collapsed element
35567 * @type Roo.Element */
35570 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35571 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35574 if(c.floatable !== false){
35575 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35576 this.collapsedEl.on("click", this.collapseClick, this);
35579 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35580 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35581 id: "message", unselectable: "on", style:{"float":"left"}});
35582 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35584 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35585 this.expandBtn.on("click", this.expand, this);
35589 if(this.collapseBtn){
35590 this.collapseBtn.setVisible(c.collapsible == true);
35593 this.cmargins = c.cmargins || this.cmargins ||
35594 (this.position == "west" || this.position == "east" ?
35595 {top: 0, left: 2, right:2, bottom: 0} :
35596 {top: 2, left: 0, right:0, bottom: 2});
35598 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35601 this.bottomTabs = c.tabPosition != "top";
35603 this.autoScroll = c.autoScroll || false;
35608 this.duration = c.duration || .30;
35609 this.slideDuration = c.slideDuration || .45;
35614 * Returns true if this region is currently visible.
35615 * @return {Boolean}
35617 isVisible : function(){
35618 return this.visible;
35622 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35623 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35625 //setCollapsedTitle : function(title){
35626 // title = title || " ";
35627 // if(this.collapsedTitleTextEl){
35628 // this.collapsedTitleTextEl.innerHTML = title;
35632 getBox : function(){
35634 // if(!this.collapsed){
35635 b = this.el.getBox(false, true);
35637 // b = this.collapsedEl.getBox(false, true);
35642 getMargins : function(){
35643 return this.margins;
35644 //return this.collapsed ? this.cmargins : this.margins;
35647 highlight : function(){
35648 this.el.addClass("x-layout-panel-dragover");
35651 unhighlight : function(){
35652 this.el.removeClass("x-layout-panel-dragover");
35655 updateBox : function(box)
35657 if (!this.bodyEl) {
35658 return; // not rendered yet..
35662 if(!this.collapsed){
35663 this.el.dom.style.left = box.x + "px";
35664 this.el.dom.style.top = box.y + "px";
35665 this.updateBody(box.width, box.height);
35667 this.collapsedEl.dom.style.left = box.x + "px";
35668 this.collapsedEl.dom.style.top = box.y + "px";
35669 this.collapsedEl.setSize(box.width, box.height);
35672 this.tabs.autoSizeTabs();
35676 updateBody : function(w, h)
35679 this.el.setWidth(w);
35680 w -= this.el.getBorderWidth("rl");
35681 if(this.config.adjustments){
35682 w += this.config.adjustments[0];
35685 if(h !== null && h > 0){
35686 this.el.setHeight(h);
35687 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35688 h -= this.el.getBorderWidth("tb");
35689 if(this.config.adjustments){
35690 h += this.config.adjustments[1];
35692 this.bodyEl.setHeight(h);
35694 h = this.tabs.syncHeight(h);
35697 if(this.panelSize){
35698 w = w !== null ? w : this.panelSize.width;
35699 h = h !== null ? h : this.panelSize.height;
35701 if(this.activePanel){
35702 var el = this.activePanel.getEl();
35703 w = w !== null ? w : el.getWidth();
35704 h = h !== null ? h : el.getHeight();
35705 this.panelSize = {width: w, height: h};
35706 this.activePanel.setSize(w, h);
35708 if(Roo.isIE && this.tabs){
35709 this.tabs.el.repaint();
35714 * Returns the container element for this region.
35715 * @return {Roo.Element}
35717 getEl : function(){
35722 * Hides this region.
35725 //if(!this.collapsed){
35726 this.el.dom.style.left = "-2000px";
35729 // this.collapsedEl.dom.style.left = "-2000px";
35730 // this.collapsedEl.hide();
35732 this.visible = false;
35733 this.fireEvent("visibilitychange", this, false);
35737 * Shows this region if it was previously hidden.
35740 //if(!this.collapsed){
35743 // this.collapsedEl.show();
35745 this.visible = true;
35746 this.fireEvent("visibilitychange", this, true);
35749 closeClicked : function(){
35750 if(this.activePanel){
35751 this.remove(this.activePanel);
35755 collapseClick : function(e){
35757 e.stopPropagation();
35760 e.stopPropagation();
35766 * Collapses this region.
35767 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35770 collapse : function(skipAnim, skipCheck = false){
35771 if(this.collapsed) {
35775 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35777 this.collapsed = true;
35779 this.split.el.hide();
35781 if(this.config.animate && skipAnim !== true){
35782 this.fireEvent("invalidated", this);
35783 this.animateCollapse();
35785 this.el.setLocation(-20000,-20000);
35787 this.collapsedEl.show();
35788 this.fireEvent("collapsed", this);
35789 this.fireEvent("invalidated", this);
35795 animateCollapse : function(){
35800 * Expands this region if it was previously collapsed.
35801 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35802 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35805 expand : function(e, skipAnim){
35807 e.stopPropagation();
35809 if(!this.collapsed || this.el.hasActiveFx()) {
35813 this.afterSlideIn();
35816 this.collapsed = false;
35817 if(this.config.animate && skipAnim !== true){
35818 this.animateExpand();
35822 this.split.el.show();
35824 this.collapsedEl.setLocation(-2000,-2000);
35825 this.collapsedEl.hide();
35826 this.fireEvent("invalidated", this);
35827 this.fireEvent("expanded", this);
35831 animateExpand : function(){
35835 initTabs : function()
35837 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35839 var ts = new Roo.bootstrap.panel.Tabs({
35840 el: this.bodyEl.dom,
35841 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35842 disableTooltips: this.config.disableTabTips,
35843 toolbar : this.config.toolbar
35846 if(this.config.hideTabs){
35847 ts.stripWrap.setDisplayed(false);
35850 ts.resizeTabs = this.config.resizeTabs === true;
35851 ts.minTabWidth = this.config.minTabWidth || 40;
35852 ts.maxTabWidth = this.config.maxTabWidth || 250;
35853 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35854 ts.monitorResize = false;
35855 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35856 ts.bodyEl.addClass('roo-layout-tabs-body');
35857 this.panels.each(this.initPanelAsTab, this);
35860 initPanelAsTab : function(panel){
35861 var ti = this.tabs.addTab(
35865 this.config.closeOnTab && panel.isClosable(),
35868 if(panel.tabTip !== undefined){
35869 ti.setTooltip(panel.tabTip);
35871 ti.on("activate", function(){
35872 this.setActivePanel(panel);
35875 if(this.config.closeOnTab){
35876 ti.on("beforeclose", function(t, e){
35878 this.remove(panel);
35882 panel.tabItem = ti;
35887 updatePanelTitle : function(panel, title)
35889 if(this.activePanel == panel){
35890 this.updateTitle(title);
35893 var ti = this.tabs.getTab(panel.getEl().id);
35895 if(panel.tabTip !== undefined){
35896 ti.setTooltip(panel.tabTip);
35901 updateTitle : function(title){
35902 if(this.titleTextEl && !this.config.title){
35903 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35907 setActivePanel : function(panel)
35909 panel = this.getPanel(panel);
35910 if(this.activePanel && this.activePanel != panel){
35911 if(this.activePanel.setActiveState(false) === false){
35915 this.activePanel = panel;
35916 panel.setActiveState(true);
35917 if(this.panelSize){
35918 panel.setSize(this.panelSize.width, this.panelSize.height);
35921 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35923 this.updateTitle(panel.getTitle());
35925 this.fireEvent("invalidated", this);
35927 this.fireEvent("panelactivated", this, panel);
35931 * Shows the specified panel.
35932 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35933 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35935 showPanel : function(panel)
35937 panel = this.getPanel(panel);
35940 var tab = this.tabs.getTab(panel.getEl().id);
35941 if(tab.isHidden()){
35942 this.tabs.unhideTab(tab.id);
35946 this.setActivePanel(panel);
35953 * Get the active panel for this region.
35954 * @return {Roo.ContentPanel} The active panel or null
35956 getActivePanel : function(){
35957 return this.activePanel;
35960 validateVisibility : function(){
35961 if(this.panels.getCount() < 1){
35962 this.updateTitle(" ");
35963 this.closeBtn.hide();
35966 if(!this.isVisible()){
35973 * Adds the passed ContentPanel(s) to this region.
35974 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35975 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35977 add : function(panel)
35979 if(arguments.length > 1){
35980 for(var i = 0, len = arguments.length; i < len; i++) {
35981 this.add(arguments[i]);
35986 // if we have not been rendered yet, then we can not really do much of this..
35987 if (!this.bodyEl) {
35988 this.unrendered_panels.push(panel);
35995 if(this.hasPanel(panel)){
35996 this.showPanel(panel);
35999 panel.setRegion(this);
36000 this.panels.add(panel);
36001 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36002 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36003 // and hide them... ???
36004 this.bodyEl.dom.appendChild(panel.getEl().dom);
36005 if(panel.background !== true){
36006 this.setActivePanel(panel);
36008 this.fireEvent("paneladded", this, panel);
36015 this.initPanelAsTab(panel);
36019 if(panel.background !== true){
36020 this.tabs.activate(panel.getEl().id);
36022 this.fireEvent("paneladded", this, panel);
36027 * Hides the tab for the specified panel.
36028 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36030 hidePanel : function(panel){
36031 if(this.tabs && (panel = this.getPanel(panel))){
36032 this.tabs.hideTab(panel.getEl().id);
36037 * Unhides the tab for a previously hidden panel.
36038 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36040 unhidePanel : function(panel){
36041 if(this.tabs && (panel = this.getPanel(panel))){
36042 this.tabs.unhideTab(panel.getEl().id);
36046 clearPanels : function(){
36047 while(this.panels.getCount() > 0){
36048 this.remove(this.panels.first());
36053 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36054 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36055 * @param {Boolean} preservePanel Overrides the config preservePanel option
36056 * @return {Roo.ContentPanel} The panel that was removed
36058 remove : function(panel, preservePanel)
36060 panel = this.getPanel(panel);
36065 this.fireEvent("beforeremove", this, panel, e);
36066 if(e.cancel === true){
36069 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36070 var panelId = panel.getId();
36071 this.panels.removeKey(panelId);
36073 document.body.appendChild(panel.getEl().dom);
36076 this.tabs.removeTab(panel.getEl().id);
36077 }else if (!preservePanel){
36078 this.bodyEl.dom.removeChild(panel.getEl().dom);
36080 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36081 var p = this.panels.first();
36082 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36083 tempEl.appendChild(p.getEl().dom);
36084 this.bodyEl.update("");
36085 this.bodyEl.dom.appendChild(p.getEl().dom);
36087 this.updateTitle(p.getTitle());
36089 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36090 this.setActivePanel(p);
36092 panel.setRegion(null);
36093 if(this.activePanel == panel){
36094 this.activePanel = null;
36096 if(this.config.autoDestroy !== false && preservePanel !== true){
36097 try{panel.destroy();}catch(e){}
36099 this.fireEvent("panelremoved", this, panel);
36104 * Returns the TabPanel component used by this region
36105 * @return {Roo.TabPanel}
36107 getTabs : function(){
36111 createTool : function(parentEl, className){
36112 var btn = Roo.DomHelper.append(parentEl, {
36114 cls: "x-layout-tools-button",
36117 cls: "roo-layout-tools-button-inner " + className,
36121 btn.addClassOnOver("roo-layout-tools-button-over");
36126 * Ext JS Library 1.1.1
36127 * Copyright(c) 2006-2007, Ext JS, LLC.
36129 * Originally Released Under LGPL - original licence link has changed is not relivant.
36132 * <script type="text/javascript">
36138 * @class Roo.SplitLayoutRegion
36139 * @extends Roo.LayoutRegion
36140 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36142 Roo.bootstrap.layout.Split = function(config){
36143 this.cursor = config.cursor;
36144 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36147 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36149 splitTip : "Drag to resize.",
36150 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36151 useSplitTips : false,
36153 applyConfig : function(config){
36154 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36157 onRender : function(ctr,pos) {
36159 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36160 if(!this.config.split){
36165 var splitEl = Roo.DomHelper.append(ctr.dom, {
36167 id: this.el.id + "-split",
36168 cls: "roo-layout-split roo-layout-split-"+this.position,
36171 /** The SplitBar for this region
36172 * @type Roo.SplitBar */
36173 // does not exist yet...
36174 Roo.log([this.position, this.orientation]);
36176 this.split = new Roo.bootstrap.SplitBar({
36177 dragElement : splitEl,
36178 resizingElement: this.el,
36179 orientation : this.orientation
36182 this.split.on("moved", this.onSplitMove, this);
36183 this.split.useShim = this.config.useShim === true;
36184 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36185 if(this.useSplitTips){
36186 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36188 //if(config.collapsible){
36189 // this.split.el.on("dblclick", this.collapse, this);
36192 if(typeof this.config.minSize != "undefined"){
36193 this.split.minSize = this.config.minSize;
36195 if(typeof this.config.maxSize != "undefined"){
36196 this.split.maxSize = this.config.maxSize;
36198 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36199 this.hideSplitter();
36204 getHMaxSize : function(){
36205 var cmax = this.config.maxSize || 10000;
36206 var center = this.mgr.getRegion("center");
36207 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36210 getVMaxSize : function(){
36211 var cmax = this.config.maxSize || 10000;
36212 var center = this.mgr.getRegion("center");
36213 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36216 onSplitMove : function(split, newSize){
36217 this.fireEvent("resized", this, newSize);
36221 * Returns the {@link Roo.SplitBar} for this region.
36222 * @return {Roo.SplitBar}
36224 getSplitBar : function(){
36229 this.hideSplitter();
36230 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36233 hideSplitter : function(){
36235 this.split.el.setLocation(-2000,-2000);
36236 this.split.el.hide();
36242 this.split.el.show();
36244 Roo.bootstrap.layout.Split.superclass.show.call(this);
36247 beforeSlide: function(){
36248 if(Roo.isGecko){// firefox overflow auto bug workaround
36249 this.bodyEl.clip();
36251 this.tabs.bodyEl.clip();
36253 if(this.activePanel){
36254 this.activePanel.getEl().clip();
36256 if(this.activePanel.beforeSlide){
36257 this.activePanel.beforeSlide();
36263 afterSlide : function(){
36264 if(Roo.isGecko){// firefox overflow auto bug workaround
36265 this.bodyEl.unclip();
36267 this.tabs.bodyEl.unclip();
36269 if(this.activePanel){
36270 this.activePanel.getEl().unclip();
36271 if(this.activePanel.afterSlide){
36272 this.activePanel.afterSlide();
36278 initAutoHide : function(){
36279 if(this.autoHide !== false){
36280 if(!this.autoHideHd){
36281 var st = new Roo.util.DelayedTask(this.slideIn, this);
36282 this.autoHideHd = {
36283 "mouseout": function(e){
36284 if(!e.within(this.el, true)){
36288 "mouseover" : function(e){
36294 this.el.on(this.autoHideHd);
36298 clearAutoHide : function(){
36299 if(this.autoHide !== false){
36300 this.el.un("mouseout", this.autoHideHd.mouseout);
36301 this.el.un("mouseover", this.autoHideHd.mouseover);
36305 clearMonitor : function(){
36306 Roo.get(document).un("click", this.slideInIf, this);
36309 // these names are backwards but not changed for compat
36310 slideOut : function(){
36311 if(this.isSlid || this.el.hasActiveFx()){
36314 this.isSlid = true;
36315 if(this.collapseBtn){
36316 this.collapseBtn.hide();
36318 this.closeBtnState = this.closeBtn.getStyle('display');
36319 this.closeBtn.hide();
36321 this.stickBtn.show();
36324 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36325 this.beforeSlide();
36326 this.el.setStyle("z-index", 10001);
36327 this.el.slideIn(this.getSlideAnchor(), {
36328 callback: function(){
36330 this.initAutoHide();
36331 Roo.get(document).on("click", this.slideInIf, this);
36332 this.fireEvent("slideshow", this);
36339 afterSlideIn : function(){
36340 this.clearAutoHide();
36341 this.isSlid = false;
36342 this.clearMonitor();
36343 this.el.setStyle("z-index", "");
36344 if(this.collapseBtn){
36345 this.collapseBtn.show();
36347 this.closeBtn.setStyle('display', this.closeBtnState);
36349 this.stickBtn.hide();
36351 this.fireEvent("slidehide", this);
36354 slideIn : function(cb){
36355 if(!this.isSlid || this.el.hasActiveFx()){
36359 this.isSlid = false;
36360 this.beforeSlide();
36361 this.el.slideOut(this.getSlideAnchor(), {
36362 callback: function(){
36363 this.el.setLeftTop(-10000, -10000);
36365 this.afterSlideIn();
36373 slideInIf : function(e){
36374 if(!e.within(this.el)){
36379 animateCollapse : function(){
36380 this.beforeSlide();
36381 this.el.setStyle("z-index", 20000);
36382 var anchor = this.getSlideAnchor();
36383 this.el.slideOut(anchor, {
36384 callback : function(){
36385 this.el.setStyle("z-index", "");
36386 this.collapsedEl.slideIn(anchor, {duration:.3});
36388 this.el.setLocation(-10000,-10000);
36390 this.fireEvent("collapsed", this);
36397 animateExpand : function(){
36398 this.beforeSlide();
36399 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36400 this.el.setStyle("z-index", 20000);
36401 this.collapsedEl.hide({
36404 this.el.slideIn(this.getSlideAnchor(), {
36405 callback : function(){
36406 this.el.setStyle("z-index", "");
36409 this.split.el.show();
36411 this.fireEvent("invalidated", this);
36412 this.fireEvent("expanded", this);
36440 getAnchor : function(){
36441 return this.anchors[this.position];
36444 getCollapseAnchor : function(){
36445 return this.canchors[this.position];
36448 getSlideAnchor : function(){
36449 return this.sanchors[this.position];
36452 getAlignAdj : function(){
36453 var cm = this.cmargins;
36454 switch(this.position){
36470 getExpandAdj : function(){
36471 var c = this.collapsedEl, cm = this.cmargins;
36472 switch(this.position){
36474 return [-(cm.right+c.getWidth()+cm.left), 0];
36477 return [cm.right+c.getWidth()+cm.left, 0];
36480 return [0, -(cm.top+cm.bottom+c.getHeight())];
36483 return [0, cm.top+cm.bottom+c.getHeight()];
36489 * Ext JS Library 1.1.1
36490 * Copyright(c) 2006-2007, Ext JS, LLC.
36492 * Originally Released Under LGPL - original licence link has changed is not relivant.
36495 * <script type="text/javascript">
36498 * These classes are private internal classes
36500 Roo.bootstrap.layout.Center = function(config){
36501 config.region = "center";
36502 Roo.bootstrap.layout.Region.call(this, config);
36503 this.visible = true;
36504 this.minWidth = config.minWidth || 20;
36505 this.minHeight = config.minHeight || 20;
36508 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36510 // center panel can't be hidden
36514 // center panel can't be hidden
36517 getMinWidth: function(){
36518 return this.minWidth;
36521 getMinHeight: function(){
36522 return this.minHeight;
36535 Roo.bootstrap.layout.North = function(config)
36537 config.region = 'north';
36538 config.cursor = 'n-resize';
36540 Roo.bootstrap.layout.Split.call(this, config);
36544 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36545 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36546 this.split.el.addClass("roo-layout-split-v");
36548 var size = config.initialSize || config.height;
36549 if(typeof size != "undefined"){
36550 this.el.setHeight(size);
36553 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36555 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36559 getBox : function(){
36560 if(this.collapsed){
36561 return this.collapsedEl.getBox();
36563 var box = this.el.getBox();
36565 box.height += this.split.el.getHeight();
36570 updateBox : function(box){
36571 if(this.split && !this.collapsed){
36572 box.height -= this.split.el.getHeight();
36573 this.split.el.setLeft(box.x);
36574 this.split.el.setTop(box.y+box.height);
36575 this.split.el.setWidth(box.width);
36577 if(this.collapsed){
36578 this.updateBody(box.width, null);
36580 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36588 Roo.bootstrap.layout.South = function(config){
36589 config.region = 'south';
36590 config.cursor = 's-resize';
36591 Roo.bootstrap.layout.Split.call(this, config);
36593 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36594 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36595 this.split.el.addClass("roo-layout-split-v");
36597 var size = config.initialSize || config.height;
36598 if(typeof size != "undefined"){
36599 this.el.setHeight(size);
36603 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36604 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36605 getBox : function(){
36606 if(this.collapsed){
36607 return this.collapsedEl.getBox();
36609 var box = this.el.getBox();
36611 var sh = this.split.el.getHeight();
36618 updateBox : function(box){
36619 if(this.split && !this.collapsed){
36620 var sh = this.split.el.getHeight();
36623 this.split.el.setLeft(box.x);
36624 this.split.el.setTop(box.y-sh);
36625 this.split.el.setWidth(box.width);
36627 if(this.collapsed){
36628 this.updateBody(box.width, null);
36630 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36634 Roo.bootstrap.layout.East = function(config){
36635 config.region = "east";
36636 config.cursor = "e-resize";
36637 Roo.bootstrap.layout.Split.call(this, config);
36639 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36640 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36641 this.split.el.addClass("roo-layout-split-h");
36643 var size = config.initialSize || config.width;
36644 if(typeof size != "undefined"){
36645 this.el.setWidth(size);
36648 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36649 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36650 getBox : function(){
36651 if(this.collapsed){
36652 return this.collapsedEl.getBox();
36654 var box = this.el.getBox();
36656 var sw = this.split.el.getWidth();
36663 updateBox : function(box){
36664 if(this.split && !this.collapsed){
36665 var sw = this.split.el.getWidth();
36667 this.split.el.setLeft(box.x);
36668 this.split.el.setTop(box.y);
36669 this.split.el.setHeight(box.height);
36672 if(this.collapsed){
36673 this.updateBody(null, box.height);
36675 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36679 Roo.bootstrap.layout.West = function(config){
36680 config.region = "west";
36681 config.cursor = "w-resize";
36683 Roo.bootstrap.layout.Split.call(this, config);
36685 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36686 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36687 this.split.el.addClass("roo-layout-split-h");
36691 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36692 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36694 onRender: function(ctr, pos)
36696 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36697 var size = this.config.initialSize || this.config.width;
36698 if(typeof size != "undefined"){
36699 this.el.setWidth(size);
36703 getBox : function(){
36704 if(this.collapsed){
36705 return this.collapsedEl.getBox();
36707 var box = this.el.getBox();
36709 box.width += this.split.el.getWidth();
36714 updateBox : function(box){
36715 if(this.split && !this.collapsed){
36716 var sw = this.split.el.getWidth();
36718 this.split.el.setLeft(box.x+box.width);
36719 this.split.el.setTop(box.y);
36720 this.split.el.setHeight(box.height);
36722 if(this.collapsed){
36723 this.updateBody(null, box.height);
36725 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36728 Roo.namespace("Roo.bootstrap.panel");/*
36730 * Ext JS Library 1.1.1
36731 * Copyright(c) 2006-2007, Ext JS, LLC.
36733 * Originally Released Under LGPL - original licence link has changed is not relivant.
36736 * <script type="text/javascript">
36739 * @class Roo.ContentPanel
36740 * @extends Roo.util.Observable
36741 * A basic ContentPanel element.
36742 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36743 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36744 * @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
36745 * @cfg {Boolean} closable True if the panel can be closed/removed
36746 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36747 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36748 * @cfg {Toolbar} toolbar A toolbar for this panel
36749 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36750 * @cfg {String} title The title for this panel
36751 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36752 * @cfg {String} url Calls {@link #setUrl} with this value
36753 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36754 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36755 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36756 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36757 * @cfg {Boolean} badges render the badges
36760 * Create a new ContentPanel.
36761 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36762 * @param {String/Object} config A string to set only the title or a config object
36763 * @param {String} content (optional) Set the HTML content for this panel
36764 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36766 Roo.bootstrap.panel.Content = function( config){
36768 this.tpl = config.tpl || false;
36770 var el = config.el;
36771 var content = config.content;
36773 if(config.autoCreate){ // xtype is available if this is called from factory
36776 this.el = Roo.get(el);
36777 if(!this.el && config && config.autoCreate){
36778 if(typeof config.autoCreate == "object"){
36779 if(!config.autoCreate.id){
36780 config.autoCreate.id = config.id||el;
36782 this.el = Roo.DomHelper.append(document.body,
36783 config.autoCreate, true);
36785 var elcfg = { tag: "div",
36786 cls: "roo-layout-inactive-content",
36790 elcfg.html = config.html;
36794 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36797 this.closable = false;
36798 this.loaded = false;
36799 this.active = false;
36802 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36804 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36806 this.wrapEl = this.el; //this.el.wrap();
36808 if (config.toolbar.items) {
36809 ti = config.toolbar.items ;
36810 delete config.toolbar.items ;
36814 this.toolbar.render(this.wrapEl, 'before');
36815 for(var i =0;i < ti.length;i++) {
36816 // Roo.log(['add child', items[i]]);
36817 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36819 this.toolbar.items = nitems;
36820 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36821 delete config.toolbar;
36825 // xtype created footer. - not sure if will work as we normally have to render first..
36826 if (this.footer && !this.footer.el && this.footer.xtype) {
36827 if (!this.wrapEl) {
36828 this.wrapEl = this.el.wrap();
36831 this.footer.container = this.wrapEl.createChild();
36833 this.footer = Roo.factory(this.footer, Roo);
36838 if(typeof config == "string"){
36839 this.title = config;
36841 Roo.apply(this, config);
36845 this.resizeEl = Roo.get(this.resizeEl, true);
36847 this.resizeEl = this.el;
36849 // handle view.xtype
36857 * Fires when this panel is activated.
36858 * @param {Roo.ContentPanel} this
36862 * @event deactivate
36863 * Fires when this panel is activated.
36864 * @param {Roo.ContentPanel} this
36866 "deactivate" : true,
36870 * Fires when this panel is resized if fitToFrame is true.
36871 * @param {Roo.ContentPanel} this
36872 * @param {Number} width The width after any component adjustments
36873 * @param {Number} height The height after any component adjustments
36879 * Fires when this tab is created
36880 * @param {Roo.ContentPanel} this
36891 if(this.autoScroll){
36892 this.resizeEl.setStyle("overflow", "auto");
36894 // fix randome scrolling
36895 //this.el.on('scroll', function() {
36896 // Roo.log('fix random scolling');
36897 // this.scrollTo('top',0);
36900 content = content || this.content;
36902 this.setContent(content);
36904 if(config && config.url){
36905 this.setUrl(this.url, this.params, this.loadOnce);
36910 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36912 if (this.view && typeof(this.view.xtype) != 'undefined') {
36913 this.view.el = this.el.appendChild(document.createElement("div"));
36914 this.view = Roo.factory(this.view);
36915 this.view.render && this.view.render(false, '');
36919 this.fireEvent('render', this);
36922 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36926 setRegion : function(region){
36927 this.region = region;
36928 this.setActiveClass(region && !this.background);
36932 setActiveClass: function(state)
36935 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36936 this.el.setStyle('position','relative');
36938 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36939 this.el.setStyle('position', 'absolute');
36944 * Returns the toolbar for this Panel if one was configured.
36945 * @return {Roo.Toolbar}
36947 getToolbar : function(){
36948 return this.toolbar;
36951 setActiveState : function(active)
36953 this.active = active;
36954 this.setActiveClass(active);
36956 if(this.fireEvent("deactivate", this) === false){
36961 this.fireEvent("activate", this);
36965 * Updates this panel's element
36966 * @param {String} content The new content
36967 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36969 setContent : function(content, loadScripts){
36970 this.el.update(content, loadScripts);
36973 ignoreResize : function(w, h){
36974 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36977 this.lastSize = {width: w, height: h};
36982 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36983 * @return {Roo.UpdateManager} The UpdateManager
36985 getUpdateManager : function(){
36986 return this.el.getUpdateManager();
36989 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36990 * @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:
36993 url: "your-url.php",
36994 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36995 callback: yourFunction,
36996 scope: yourObject, //(optional scope)
36999 text: "Loading...",
37004 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37005 * 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.
37006 * @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}
37007 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37008 * @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.
37009 * @return {Roo.ContentPanel} this
37012 var um = this.el.getUpdateManager();
37013 um.update.apply(um, arguments);
37019 * 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.
37020 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37021 * @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)
37022 * @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)
37023 * @return {Roo.UpdateManager} The UpdateManager
37025 setUrl : function(url, params, loadOnce){
37026 if(this.refreshDelegate){
37027 this.removeListener("activate", this.refreshDelegate);
37029 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37030 this.on("activate", this.refreshDelegate);
37031 return this.el.getUpdateManager();
37034 _handleRefresh : function(url, params, loadOnce){
37035 if(!loadOnce || !this.loaded){
37036 var updater = this.el.getUpdateManager();
37037 updater.update(url, params, this._setLoaded.createDelegate(this));
37041 _setLoaded : function(){
37042 this.loaded = true;
37046 * Returns this panel's id
37049 getId : function(){
37054 * Returns this panel's element - used by regiosn to add.
37055 * @return {Roo.Element}
37057 getEl : function(){
37058 return this.wrapEl || this.el;
37063 adjustForComponents : function(width, height)
37065 //Roo.log('adjustForComponents ');
37066 if(this.resizeEl != this.el){
37067 width -= this.el.getFrameWidth('lr');
37068 height -= this.el.getFrameWidth('tb');
37071 var te = this.toolbar.getEl();
37072 te.setWidth(width);
37073 height -= te.getHeight();
37076 var te = this.footer.getEl();
37077 te.setWidth(width);
37078 height -= te.getHeight();
37082 if(this.adjustments){
37083 width += this.adjustments[0];
37084 height += this.adjustments[1];
37086 return {"width": width, "height": height};
37089 setSize : function(width, height){
37090 if(this.fitToFrame && !this.ignoreResize(width, height)){
37091 if(this.fitContainer && this.resizeEl != this.el){
37092 this.el.setSize(width, height);
37094 var size = this.adjustForComponents(width, height);
37095 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37096 this.fireEvent('resize', this, size.width, size.height);
37101 * Returns this panel's title
37104 getTitle : function(){
37106 if (typeof(this.title) != 'object') {
37111 for (var k in this.title) {
37112 if (!this.title.hasOwnProperty(k)) {
37116 if (k.indexOf('-') >= 0) {
37117 var s = k.split('-');
37118 for (var i = 0; i<s.length; i++) {
37119 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37122 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37129 * Set this panel's title
37130 * @param {String} title
37132 setTitle : function(title){
37133 this.title = title;
37135 this.region.updatePanelTitle(this, title);
37140 * Returns true is this panel was configured to be closable
37141 * @return {Boolean}
37143 isClosable : function(){
37144 return this.closable;
37147 beforeSlide : function(){
37149 this.resizeEl.clip();
37152 afterSlide : function(){
37154 this.resizeEl.unclip();
37158 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37159 * Will fail silently if the {@link #setUrl} method has not been called.
37160 * This does not activate the panel, just updates its content.
37162 refresh : function(){
37163 if(this.refreshDelegate){
37164 this.loaded = false;
37165 this.refreshDelegate();
37170 * Destroys this panel
37172 destroy : function(){
37173 this.el.removeAllListeners();
37174 var tempEl = document.createElement("span");
37175 tempEl.appendChild(this.el.dom);
37176 tempEl.innerHTML = "";
37182 * form - if the content panel contains a form - this is a reference to it.
37183 * @type {Roo.form.Form}
37187 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37188 * This contains a reference to it.
37194 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37204 * @param {Object} cfg Xtype definition of item to add.
37208 getChildContainer: function () {
37209 return this.getEl();
37214 var ret = new Roo.factory(cfg);
37219 if (cfg.xtype.match(/^Form$/)) {
37222 //if (this.footer) {
37223 // el = this.footer.container.insertSibling(false, 'before');
37225 el = this.el.createChild();
37228 this.form = new Roo.form.Form(cfg);
37231 if ( this.form.allItems.length) {
37232 this.form.render(el.dom);
37236 // should only have one of theses..
37237 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37238 // views.. should not be just added - used named prop 'view''
37240 cfg.el = this.el.appendChild(document.createElement("div"));
37243 var ret = new Roo.factory(cfg);
37245 ret.render && ret.render(false, ''); // render blank..
37255 * @class Roo.bootstrap.panel.Grid
37256 * @extends Roo.bootstrap.panel.Content
37258 * Create a new GridPanel.
37259 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37260 * @param {Object} config A the config object
37266 Roo.bootstrap.panel.Grid = function(config)
37270 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37271 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37273 config.el = this.wrapper;
37274 //this.el = this.wrapper;
37276 if (config.container) {
37277 // ctor'ed from a Border/panel.grid
37280 this.wrapper.setStyle("overflow", "hidden");
37281 this.wrapper.addClass('roo-grid-container');
37286 if(config.toolbar){
37287 var tool_el = this.wrapper.createChild();
37288 this.toolbar = Roo.factory(config.toolbar);
37290 if (config.toolbar.items) {
37291 ti = config.toolbar.items ;
37292 delete config.toolbar.items ;
37296 this.toolbar.render(tool_el);
37297 for(var i =0;i < ti.length;i++) {
37298 // Roo.log(['add child', items[i]]);
37299 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37301 this.toolbar.items = nitems;
37303 delete config.toolbar;
37306 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37307 config.grid.scrollBody = true;;
37308 config.grid.monitorWindowResize = false; // turn off autosizing
37309 config.grid.autoHeight = false;
37310 config.grid.autoWidth = false;
37312 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37314 if (config.background) {
37315 // render grid on panel activation (if panel background)
37316 this.on('activate', function(gp) {
37317 if (!gp.grid.rendered) {
37318 gp.grid.render(this.wrapper);
37319 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37324 this.grid.render(this.wrapper);
37325 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37328 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37329 // ??? needed ??? config.el = this.wrapper;
37334 // xtype created footer. - not sure if will work as we normally have to render first..
37335 if (this.footer && !this.footer.el && this.footer.xtype) {
37337 var ctr = this.grid.getView().getFooterPanel(true);
37338 this.footer.dataSource = this.grid.dataSource;
37339 this.footer = Roo.factory(this.footer, Roo);
37340 this.footer.render(ctr);
37350 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37351 getId : function(){
37352 return this.grid.id;
37356 * Returns the grid for this panel
37357 * @return {Roo.bootstrap.Table}
37359 getGrid : function(){
37363 setSize : function(width, height){
37364 if(!this.ignoreResize(width, height)){
37365 var grid = this.grid;
37366 var size = this.adjustForComponents(width, height);
37367 var gridel = grid.getGridEl();
37368 gridel.setSize(size.width, size.height);
37370 var thd = grid.getGridEl().select('thead',true).first();
37371 var tbd = grid.getGridEl().select('tbody', true).first();
37373 tbd.setSize(width, height - thd.getHeight());
37382 beforeSlide : function(){
37383 this.grid.getView().scroller.clip();
37386 afterSlide : function(){
37387 this.grid.getView().scroller.unclip();
37390 destroy : function(){
37391 this.grid.destroy();
37393 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37398 * @class Roo.bootstrap.panel.Nest
37399 * @extends Roo.bootstrap.panel.Content
37401 * Create a new Panel, that can contain a layout.Border.
37404 * @param {Roo.BorderLayout} layout The layout for this panel
37405 * @param {String/Object} config A string to set only the title or a config object
37407 Roo.bootstrap.panel.Nest = function(config)
37409 // construct with only one argument..
37410 /* FIXME - implement nicer consturctors
37411 if (layout.layout) {
37413 layout = config.layout;
37414 delete config.layout;
37416 if (layout.xtype && !layout.getEl) {
37417 // then layout needs constructing..
37418 layout = Roo.factory(layout, Roo);
37422 config.el = config.layout.getEl();
37424 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37426 config.layout.monitorWindowResize = false; // turn off autosizing
37427 this.layout = config.layout;
37428 this.layout.getEl().addClass("roo-layout-nested-layout");
37435 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37437 setSize : function(width, height){
37438 if(!this.ignoreResize(width, height)){
37439 var size = this.adjustForComponents(width, height);
37440 var el = this.layout.getEl();
37441 if (size.height < 1) {
37442 el.setWidth(size.width);
37444 el.setSize(size.width, size.height);
37446 var touch = el.dom.offsetWidth;
37447 this.layout.layout();
37448 // ie requires a double layout on the first pass
37449 if(Roo.isIE && !this.initialized){
37450 this.initialized = true;
37451 this.layout.layout();
37456 // activate all subpanels if not currently active..
37458 setActiveState : function(active){
37459 this.active = active;
37460 this.setActiveClass(active);
37463 this.fireEvent("deactivate", this);
37467 this.fireEvent("activate", this);
37468 // not sure if this should happen before or after..
37469 if (!this.layout) {
37470 return; // should not happen..
37473 for (var r in this.layout.regions) {
37474 reg = this.layout.getRegion(r);
37475 if (reg.getActivePanel()) {
37476 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37477 reg.setActivePanel(reg.getActivePanel());
37480 if (!reg.panels.length) {
37483 reg.showPanel(reg.getPanel(0));
37492 * Returns the nested BorderLayout for this panel
37493 * @return {Roo.BorderLayout}
37495 getLayout : function(){
37496 return this.layout;
37500 * Adds a xtype elements to the layout of the nested panel
37504 xtype : 'ContentPanel',
37511 xtype : 'NestedLayoutPanel',
37517 items : [ ... list of content panels or nested layout panels.. ]
37521 * @param {Object} cfg Xtype definition of item to add.
37523 addxtype : function(cfg) {
37524 return this.layout.addxtype(cfg);
37529 * Ext JS Library 1.1.1
37530 * Copyright(c) 2006-2007, Ext JS, LLC.
37532 * Originally Released Under LGPL - original licence link has changed is not relivant.
37535 * <script type="text/javascript">
37538 * @class Roo.TabPanel
37539 * @extends Roo.util.Observable
37540 * A lightweight tab container.
37544 // basic tabs 1, built from existing content
37545 var tabs = new Roo.TabPanel("tabs1");
37546 tabs.addTab("script", "View Script");
37547 tabs.addTab("markup", "View Markup");
37548 tabs.activate("script");
37550 // more advanced tabs, built from javascript
37551 var jtabs = new Roo.TabPanel("jtabs");
37552 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37554 // set up the UpdateManager
37555 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37556 var updater = tab2.getUpdateManager();
37557 updater.setDefaultUrl("ajax1.htm");
37558 tab2.on('activate', updater.refresh, updater, true);
37560 // Use setUrl for Ajax loading
37561 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37562 tab3.setUrl("ajax2.htm", null, true);
37565 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37568 jtabs.activate("jtabs-1");
37571 * Create a new TabPanel.
37572 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37573 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37575 Roo.bootstrap.panel.Tabs = function(config){
37577 * The container element for this TabPanel.
37578 * @type Roo.Element
37580 this.el = Roo.get(config.el);
37583 if(typeof config == "boolean"){
37584 this.tabPosition = config ? "bottom" : "top";
37586 Roo.apply(this, config);
37590 if(this.tabPosition == "bottom"){
37591 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37592 this.el.addClass("roo-tabs-bottom");
37594 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37595 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37596 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37598 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37600 if(this.tabPosition != "bottom"){
37601 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37602 * @type Roo.Element
37604 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37605 this.el.addClass("roo-tabs-top");
37609 this.bodyEl.setStyle("position", "relative");
37611 this.active = null;
37612 this.activateDelegate = this.activate.createDelegate(this);
37617 * Fires when the active tab changes
37618 * @param {Roo.TabPanel} this
37619 * @param {Roo.TabPanelItem} activePanel The new active tab
37623 * @event beforetabchange
37624 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37625 * @param {Roo.TabPanel} this
37626 * @param {Object} e Set cancel to true on this object to cancel the tab change
37627 * @param {Roo.TabPanelItem} tab The tab being changed to
37629 "beforetabchange" : true
37632 Roo.EventManager.onWindowResize(this.onResize, this);
37633 this.cpad = this.el.getPadding("lr");
37634 this.hiddenCount = 0;
37637 // toolbar on the tabbar support...
37638 if (this.toolbar) {
37639 alert("no toolbar support yet");
37640 this.toolbar = false;
37642 var tcfg = this.toolbar;
37643 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37644 this.toolbar = new Roo.Toolbar(tcfg);
37645 if (Roo.isSafari) {
37646 var tbl = tcfg.container.child('table', true);
37647 tbl.setAttribute('width', '100%');
37655 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37658 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37660 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37662 tabPosition : "top",
37664 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37666 currentTabWidth : 0,
37668 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37672 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37676 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37678 preferredTabWidth : 175,
37680 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37682 resizeTabs : false,
37684 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37686 monitorResize : true,
37688 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37693 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37694 * @param {String} id The id of the div to use <b>or create</b>
37695 * @param {String} text The text for the tab
37696 * @param {String} content (optional) Content to put in the TabPanelItem body
37697 * @param {Boolean} closable (optional) True to create a close icon on the tab
37698 * @return {Roo.TabPanelItem} The created TabPanelItem
37700 addTab : function(id, text, content, closable, tpl)
37702 var item = new Roo.bootstrap.panel.TabItem({
37706 closable : closable,
37709 this.addTabItem(item);
37711 item.setContent(content);
37717 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37718 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37719 * @return {Roo.TabPanelItem}
37721 getTab : function(id){
37722 return this.items[id];
37726 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37727 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37729 hideTab : function(id){
37730 var t = this.items[id];
37733 this.hiddenCount++;
37734 this.autoSizeTabs();
37739 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37740 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37742 unhideTab : function(id){
37743 var t = this.items[id];
37745 t.setHidden(false);
37746 this.hiddenCount--;
37747 this.autoSizeTabs();
37752 * Adds an existing {@link Roo.TabPanelItem}.
37753 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37755 addTabItem : function(item){
37756 this.items[item.id] = item;
37757 this.items.push(item);
37758 // if(this.resizeTabs){
37759 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37760 // this.autoSizeTabs();
37762 // item.autoSize();
37767 * Removes a {@link Roo.TabPanelItem}.
37768 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37770 removeTab : function(id){
37771 var items = this.items;
37772 var tab = items[id];
37773 if(!tab) { return; }
37774 var index = items.indexOf(tab);
37775 if(this.active == tab && items.length > 1){
37776 var newTab = this.getNextAvailable(index);
37781 this.stripEl.dom.removeChild(tab.pnode.dom);
37782 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37783 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37785 items.splice(index, 1);
37786 delete this.items[tab.id];
37787 tab.fireEvent("close", tab);
37788 tab.purgeListeners();
37789 this.autoSizeTabs();
37792 getNextAvailable : function(start){
37793 var items = this.items;
37795 // look for a next tab that will slide over to
37796 // replace the one being removed
37797 while(index < items.length){
37798 var item = items[++index];
37799 if(item && !item.isHidden()){
37803 // if one isn't found select the previous tab (on the left)
37806 var item = items[--index];
37807 if(item && !item.isHidden()){
37815 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37816 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37818 disableTab : function(id){
37819 var tab = this.items[id];
37820 if(tab && this.active != tab){
37826 * Enables a {@link Roo.TabPanelItem} that is disabled.
37827 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37829 enableTab : function(id){
37830 var tab = this.items[id];
37835 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37836 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37837 * @return {Roo.TabPanelItem} The TabPanelItem.
37839 activate : function(id){
37840 var tab = this.items[id];
37844 if(tab == this.active || tab.disabled){
37848 this.fireEvent("beforetabchange", this, e, tab);
37849 if(e.cancel !== true && !tab.disabled){
37851 this.active.hide();
37853 this.active = this.items[id];
37854 this.active.show();
37855 this.fireEvent("tabchange", this, this.active);
37861 * Gets the active {@link Roo.TabPanelItem}.
37862 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37864 getActiveTab : function(){
37865 return this.active;
37869 * Updates the tab body element to fit the height of the container element
37870 * for overflow scrolling
37871 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37873 syncHeight : function(targetHeight){
37874 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37875 var bm = this.bodyEl.getMargins();
37876 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37877 this.bodyEl.setHeight(newHeight);
37881 onResize : function(){
37882 if(this.monitorResize){
37883 this.autoSizeTabs();
37888 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37890 beginUpdate : function(){
37891 this.updating = true;
37895 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37897 endUpdate : function(){
37898 this.updating = false;
37899 this.autoSizeTabs();
37903 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37905 autoSizeTabs : function(){
37906 var count = this.items.length;
37907 var vcount = count - this.hiddenCount;
37908 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37911 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37912 var availWidth = Math.floor(w / vcount);
37913 var b = this.stripBody;
37914 if(b.getWidth() > w){
37915 var tabs = this.items;
37916 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37917 if(availWidth < this.minTabWidth){
37918 /*if(!this.sleft){ // incomplete scrolling code
37919 this.createScrollButtons();
37922 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37925 if(this.currentTabWidth < this.preferredTabWidth){
37926 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37932 * Returns the number of tabs in this TabPanel.
37935 getCount : function(){
37936 return this.items.length;
37940 * Resizes all the tabs to the passed width
37941 * @param {Number} The new width
37943 setTabWidth : function(width){
37944 this.currentTabWidth = width;
37945 for(var i = 0, len = this.items.length; i < len; i++) {
37946 if(!this.items[i].isHidden()) {
37947 this.items[i].setWidth(width);
37953 * Destroys this TabPanel
37954 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37956 destroy : function(removeEl){
37957 Roo.EventManager.removeResizeListener(this.onResize, this);
37958 for(var i = 0, len = this.items.length; i < len; i++){
37959 this.items[i].purgeListeners();
37961 if(removeEl === true){
37962 this.el.update("");
37967 createStrip : function(container)
37969 var strip = document.createElement("nav");
37970 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37971 container.appendChild(strip);
37975 createStripList : function(strip)
37977 // div wrapper for retard IE
37978 // returns the "tr" element.
37979 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37980 //'<div class="x-tabs-strip-wrap">'+
37981 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37982 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37983 return strip.firstChild; //.firstChild.firstChild.firstChild;
37985 createBody : function(container)
37987 var body = document.createElement("div");
37988 Roo.id(body, "tab-body");
37989 //Roo.fly(body).addClass("x-tabs-body");
37990 Roo.fly(body).addClass("tab-content");
37991 container.appendChild(body);
37994 createItemBody :function(bodyEl, id){
37995 var body = Roo.getDom(id);
37997 body = document.createElement("div");
38000 //Roo.fly(body).addClass("x-tabs-item-body");
38001 Roo.fly(body).addClass("tab-pane");
38002 bodyEl.insertBefore(body, bodyEl.firstChild);
38006 createStripElements : function(stripEl, text, closable, tpl)
38008 var td = document.createElement("li"); // was td..
38011 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38014 stripEl.appendChild(td);
38016 td.className = "x-tabs-closable";
38017 if(!this.closeTpl){
38018 this.closeTpl = new Roo.Template(
38019 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38020 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38021 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38024 var el = this.closeTpl.overwrite(td, {"text": text});
38025 var close = el.getElementsByTagName("div")[0];
38026 var inner = el.getElementsByTagName("em")[0];
38027 return {"el": el, "close": close, "inner": inner};
38030 // not sure what this is..
38031 // if(!this.tabTpl){
38032 //this.tabTpl = new Roo.Template(
38033 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38034 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38036 // this.tabTpl = new Roo.Template(
38037 // '<a href="#">' +
38038 // '<span unselectable="on"' +
38039 // (this.disableTooltips ? '' : ' title="{text}"') +
38040 // ' >{text}</span></a>'
38046 var template = tpl || this.tabTpl || false;
38050 template = new Roo.Template(
38052 '<span unselectable="on"' +
38053 (this.disableTooltips ? '' : ' title="{text}"') +
38054 ' >{text}</span></a>'
38058 switch (typeof(template)) {
38062 template = new Roo.Template(template);
38068 var el = template.overwrite(td, {"text": text});
38070 var inner = el.getElementsByTagName("span")[0];
38072 return {"el": el, "inner": inner};
38080 * @class Roo.TabPanelItem
38081 * @extends Roo.util.Observable
38082 * Represents an individual item (tab plus body) in a TabPanel.
38083 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38084 * @param {String} id The id of this TabPanelItem
38085 * @param {String} text The text for the tab of this TabPanelItem
38086 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38088 Roo.bootstrap.panel.TabItem = function(config){
38090 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38091 * @type Roo.TabPanel
38093 this.tabPanel = config.panel;
38095 * The id for this TabPanelItem
38098 this.id = config.id;
38100 this.disabled = false;
38102 this.text = config.text;
38104 this.loaded = false;
38105 this.closable = config.closable;
38108 * The body element for this TabPanelItem.
38109 * @type Roo.Element
38111 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38112 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38113 this.bodyEl.setStyle("display", "block");
38114 this.bodyEl.setStyle("zoom", "1");
38115 //this.hideAction();
38117 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38119 this.el = Roo.get(els.el);
38120 this.inner = Roo.get(els.inner, true);
38121 this.textEl = Roo.get(this.el.dom.firstChild, true);
38122 this.pnode = Roo.get(els.el.parentNode, true);
38123 // this.el.on("mousedown", this.onTabMouseDown, this);
38124 this.el.on("click", this.onTabClick, this);
38126 if(config.closable){
38127 var c = Roo.get(els.close, true);
38128 c.dom.title = this.closeText;
38129 c.addClassOnOver("close-over");
38130 c.on("click", this.closeClick, this);
38136 * Fires when this tab becomes the active tab.
38137 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38138 * @param {Roo.TabPanelItem} this
38142 * @event beforeclose
38143 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38144 * @param {Roo.TabPanelItem} this
38145 * @param {Object} e Set cancel to true on this object to cancel the close.
38147 "beforeclose": true,
38150 * Fires when this tab is closed.
38151 * @param {Roo.TabPanelItem} this
38155 * @event deactivate
38156 * Fires when this tab is no longer the active tab.
38157 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38158 * @param {Roo.TabPanelItem} this
38160 "deactivate" : true
38162 this.hidden = false;
38164 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38167 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38169 purgeListeners : function(){
38170 Roo.util.Observable.prototype.purgeListeners.call(this);
38171 this.el.removeAllListeners();
38174 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38177 this.pnode.addClass("active");
38180 this.tabPanel.stripWrap.repaint();
38182 this.fireEvent("activate", this.tabPanel, this);
38186 * Returns true if this tab is the active tab.
38187 * @return {Boolean}
38189 isActive : function(){
38190 return this.tabPanel.getActiveTab() == this;
38194 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38197 this.pnode.removeClass("active");
38199 this.fireEvent("deactivate", this.tabPanel, this);
38202 hideAction : function(){
38203 this.bodyEl.hide();
38204 this.bodyEl.setStyle("position", "absolute");
38205 this.bodyEl.setLeft("-20000px");
38206 this.bodyEl.setTop("-20000px");
38209 showAction : function(){
38210 this.bodyEl.setStyle("position", "relative");
38211 this.bodyEl.setTop("");
38212 this.bodyEl.setLeft("");
38213 this.bodyEl.show();
38217 * Set the tooltip for the tab.
38218 * @param {String} tooltip The tab's tooltip
38220 setTooltip : function(text){
38221 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38222 this.textEl.dom.qtip = text;
38223 this.textEl.dom.removeAttribute('title');
38225 this.textEl.dom.title = text;
38229 onTabClick : function(e){
38230 e.preventDefault();
38231 this.tabPanel.activate(this.id);
38234 onTabMouseDown : function(e){
38235 e.preventDefault();
38236 this.tabPanel.activate(this.id);
38239 getWidth : function(){
38240 return this.inner.getWidth();
38243 setWidth : function(width){
38244 var iwidth = width - this.pnode.getPadding("lr");
38245 this.inner.setWidth(iwidth);
38246 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38247 this.pnode.setWidth(width);
38251 * Show or hide the tab
38252 * @param {Boolean} hidden True to hide or false to show.
38254 setHidden : function(hidden){
38255 this.hidden = hidden;
38256 this.pnode.setStyle("display", hidden ? "none" : "");
38260 * Returns true if this tab is "hidden"
38261 * @return {Boolean}
38263 isHidden : function(){
38264 return this.hidden;
38268 * Returns the text for this tab
38271 getText : function(){
38275 autoSize : function(){
38276 //this.el.beginMeasure();
38277 this.textEl.setWidth(1);
38279 * #2804 [new] Tabs in Roojs
38280 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38282 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38283 //this.el.endMeasure();
38287 * Sets the text for the tab (Note: this also sets the tooltip text)
38288 * @param {String} text The tab's text and tooltip
38290 setText : function(text){
38292 this.textEl.update(text);
38293 this.setTooltip(text);
38294 //if(!this.tabPanel.resizeTabs){
38295 // this.autoSize();
38299 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38301 activate : function(){
38302 this.tabPanel.activate(this.id);
38306 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38308 disable : function(){
38309 if(this.tabPanel.active != this){
38310 this.disabled = true;
38311 this.pnode.addClass("disabled");
38316 * Enables this TabPanelItem if it was previously disabled.
38318 enable : function(){
38319 this.disabled = false;
38320 this.pnode.removeClass("disabled");
38324 * Sets the content for this TabPanelItem.
38325 * @param {String} content The content
38326 * @param {Boolean} loadScripts true to look for and load scripts
38328 setContent : function(content, loadScripts){
38329 this.bodyEl.update(content, loadScripts);
38333 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38334 * @return {Roo.UpdateManager} The UpdateManager
38336 getUpdateManager : function(){
38337 return this.bodyEl.getUpdateManager();
38341 * Set a URL to be used to load the content for this TabPanelItem.
38342 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38343 * @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)
38344 * @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)
38345 * @return {Roo.UpdateManager} The UpdateManager
38347 setUrl : function(url, params, loadOnce){
38348 if(this.refreshDelegate){
38349 this.un('activate', this.refreshDelegate);
38351 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38352 this.on("activate", this.refreshDelegate);
38353 return this.bodyEl.getUpdateManager();
38357 _handleRefresh : function(url, params, loadOnce){
38358 if(!loadOnce || !this.loaded){
38359 var updater = this.bodyEl.getUpdateManager();
38360 updater.update(url, params, this._setLoaded.createDelegate(this));
38365 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38366 * Will fail silently if the setUrl method has not been called.
38367 * This does not activate the panel, just updates its content.
38369 refresh : function(){
38370 if(this.refreshDelegate){
38371 this.loaded = false;
38372 this.refreshDelegate();
38377 _setLoaded : function(){
38378 this.loaded = true;
38382 closeClick : function(e){
38385 this.fireEvent("beforeclose", this, o);
38386 if(o.cancel !== true){
38387 this.tabPanel.removeTab(this.id);
38391 * The text displayed in the tooltip for the close icon.
38394 closeText : "Close this tab"
38397 * This script refer to:
38398 * Title: International Telephone Input
38399 * Author: Jack O'Connor
38400 * Code version: v12.1.12
38401 * Availability: https://github.com/jackocnr/intl-tel-input.git
38404 Roo.bootstrap.PhoneInputData = function() {
38407 "Afghanistan (افغانستان)",
38412 "Albania (Shqipëri)",
38417 "Algeria (الجزائر)",
38442 "Antigua and Barbuda",
38452 "Armenia (Հայաստան)",
38468 "Austria (Österreich)",
38473 "Azerbaijan (Azərbaycan)",
38483 "Bahrain (البحرين)",
38488 "Bangladesh (বাংলাদেশ)",
38498 "Belarus (Беларусь)",
38503 "Belgium (België)",
38533 "Bosnia and Herzegovina (Босна и Херцеговина)",
38548 "British Indian Ocean Territory",
38553 "British Virgin Islands",
38563 "Bulgaria (България)",
38573 "Burundi (Uburundi)",
38578 "Cambodia (កម្ពុជា)",
38583 "Cameroon (Cameroun)",
38592 ["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"]
38595 "Cape Verde (Kabu Verdi)",
38600 "Caribbean Netherlands",
38611 "Central African Republic (République centrafricaine)",
38631 "Christmas Island",
38637 "Cocos (Keeling) Islands",
38648 "Comoros (جزر القمر)",
38653 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38658 "Congo (Republic) (Congo-Brazzaville)",
38678 "Croatia (Hrvatska)",
38699 "Czech Republic (Česká republika)",
38704 "Denmark (Danmark)",
38719 "Dominican Republic (República Dominicana)",
38723 ["809", "829", "849"]
38741 "Equatorial Guinea (Guinea Ecuatorial)",
38761 "Falkland Islands (Islas Malvinas)",
38766 "Faroe Islands (Føroyar)",
38787 "French Guiana (Guyane française)",
38792 "French Polynesia (Polynésie française)",
38807 "Georgia (საქართველო)",
38812 "Germany (Deutschland)",
38832 "Greenland (Kalaallit Nunaat)",
38869 "Guinea-Bissau (Guiné Bissau)",
38894 "Hungary (Magyarország)",
38899 "Iceland (Ísland)",
38919 "Iraq (العراق)",
38935 "Israel (ישראל)",
38962 "Jordan (الأردن)",
38967 "Kazakhstan (Казахстан)",
38988 "Kuwait (الكويت)",
38993 "Kyrgyzstan (Кыргызстан)",
39003 "Latvia (Latvija)",
39008 "Lebanon (لبنان)",
39023 "Libya (ليبيا)",
39033 "Lithuania (Lietuva)",
39048 "Macedonia (FYROM) (Македонија)",
39053 "Madagascar (Madagasikara)",
39083 "Marshall Islands",
39093 "Mauritania (موريتانيا)",
39098 "Mauritius (Moris)",
39119 "Moldova (Republica Moldova)",
39129 "Mongolia (Монгол)",
39134 "Montenegro (Crna Gora)",
39144 "Morocco (المغرب)",
39150 "Mozambique (Moçambique)",
39155 "Myanmar (Burma) (မြန်မာ)",
39160 "Namibia (Namibië)",
39175 "Netherlands (Nederland)",
39180 "New Caledonia (Nouvelle-Calédonie)",
39215 "North Korea (조선 민주주의 인민 공화국)",
39220 "Northern Mariana Islands",
39236 "Pakistan (پاکستان)",
39246 "Palestine (فلسطين)",
39256 "Papua New Guinea",
39298 "Réunion (La Réunion)",
39304 "Romania (România)",
39320 "Saint Barthélemy",
39331 "Saint Kitts and Nevis",
39341 "Saint Martin (Saint-Martin (partie française))",
39347 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39352 "Saint Vincent and the Grenadines",
39367 "São Tomé and Príncipe (São Tomé e Príncipe)",
39372 "Saudi Arabia (المملكة العربية السعودية)",
39377 "Senegal (Sénégal)",
39407 "Slovakia (Slovensko)",
39412 "Slovenia (Slovenija)",
39422 "Somalia (Soomaaliya)",
39432 "South Korea (대한민국)",
39437 "South Sudan (جنوب السودان)",
39447 "Sri Lanka (ශ්රී ලංකාව)",
39452 "Sudan (السودان)",
39462 "Svalbard and Jan Mayen",
39473 "Sweden (Sverige)",
39478 "Switzerland (Schweiz)",
39483 "Syria (سوريا)",
39528 "Trinidad and Tobago",
39533 "Tunisia (تونس)",
39538 "Turkey (Türkiye)",
39548 "Turks and Caicos Islands",
39558 "U.S. Virgin Islands",
39568 "Ukraine (Україна)",
39573 "United Arab Emirates (الإمارات العربية المتحدة)",
39595 "Uzbekistan (Oʻzbekiston)",
39605 "Vatican City (Città del Vaticano)",
39616 "Vietnam (Việt Nam)",
39621 "Wallis and Futuna (Wallis-et-Futuna)",
39626 "Western Sahara (الصحراء الغربية)",
39632 "Yemen (اليمن)",
39656 * This script refer to:
39657 * Title: International Telephone Input
39658 * Author: Jack O'Connor
39659 * Code version: v12.1.12
39660 * Availability: https://github.com/jackocnr/intl-tel-input.git
39664 * @class Roo.bootstrap.PhoneInput
39665 * @extends Roo.bootstrap.TriggerField
39666 * An input with International dial-code selection
39668 * @cfg {String} defaultDialCode default '+852'
39669 * @cfg {Array} preferedCountries default []
39672 * Create a new PhoneInput.
39673 * @param {Object} config Configuration options
39676 Roo.bootstrap.PhoneInput = function(config) {
39677 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39680 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39682 listWidth: undefined,
39684 selectedClass: 'active',
39686 invalidClass : "has-warning",
39688 validClass: 'has-success',
39690 allowed: '0123456789',
39693 * @cfg {String} defaultDialCode The default dial code when initializing the input
39695 defaultDialCode: '+852',
39698 * @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
39700 preferedCountries: false,
39702 getAutoCreate : function()
39704 var data = Roo.bootstrap.PhoneInputData();
39705 var align = this.labelAlign || this.parentLabelAlign();
39708 this.allCountries = [];
39709 this.dialCodeMapping = [];
39711 for (var i = 0; i < data.length; i++) {
39713 this.allCountries[i] = {
39717 priority: c[3] || 0,
39718 areaCodes: c[4] || null
39720 this.dialCodeMapping[c[2]] = {
39723 priority: c[3] || 0,
39724 areaCodes: c[4] || null
39736 cls : 'form-control tel-input',
39737 autocomplete: 'new-password'
39740 var hiddenInput = {
39743 cls: 'hidden-tel-input'
39747 hiddenInput.name = this.name;
39750 if (this.disabled) {
39751 input.disabled = true;
39754 var flag_container = {
39771 cls: this.hasFeedback ? 'has-feedback' : '',
39777 cls: 'dial-code-holder',
39784 cls: 'roo-select2-container input-group',
39791 if (this.fieldLabel.length) {
39794 tooltip: 'This field is required'
39800 cls: 'control-label',
39806 html: this.fieldLabel
39809 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39815 if(this.indicatorpos == 'right') {
39816 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39823 if(align == 'left') {
39831 if(this.labelWidth > 12){
39832 label.style = "width: " + this.labelWidth + 'px';
39834 if(this.labelWidth < 13 && this.labelmd == 0){
39835 this.labelmd = this.labelWidth;
39837 if(this.labellg > 0){
39838 label.cls += ' col-lg-' + this.labellg;
39839 input.cls += ' col-lg-' + (12 - this.labellg);
39841 if(this.labelmd > 0){
39842 label.cls += ' col-md-' + this.labelmd;
39843 container.cls += ' col-md-' + (12 - this.labelmd);
39845 if(this.labelsm > 0){
39846 label.cls += ' col-sm-' + this.labelsm;
39847 container.cls += ' col-sm-' + (12 - this.labelsm);
39849 if(this.labelxs > 0){
39850 label.cls += ' col-xs-' + this.labelxs;
39851 container.cls += ' col-xs-' + (12 - this.labelxs);
39861 var settings = this;
39863 ['xs','sm','md','lg'].map(function(size){
39864 if (settings[size]) {
39865 cfg.cls += ' col-' + size + '-' + settings[size];
39869 this.store = new Roo.data.Store({
39870 proxy : new Roo.data.MemoryProxy({}),
39871 reader : new Roo.data.JsonReader({
39882 'name' : 'dialCode',
39886 'name' : 'priority',
39890 'name' : 'areaCodes',
39897 if(!this.preferedCountries) {
39898 this.preferedCountries = [
39905 var p = this.preferedCountries.reverse();
39908 for (var i = 0; i < p.length; i++) {
39909 for (var j = 0; j < this.allCountries.length; j++) {
39910 if(this.allCountries[j].iso2 == p[i]) {
39911 var t = this.allCountries[j];
39912 this.allCountries.splice(j,1);
39913 this.allCountries.unshift(t);
39919 this.store.proxy.data = {
39921 data: this.allCountries
39927 initEvents : function()
39930 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39932 this.indicator = this.indicatorEl();
39933 this.flag = this.flagEl();
39934 this.dialCodeHolder = this.dialCodeHolderEl();
39936 this.trigger = this.el.select('div.flag-box',true).first();
39937 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39942 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39943 _this.list.setWidth(lw);
39946 this.list.on('mouseover', this.onViewOver, this);
39947 this.list.on('mousemove', this.onViewMove, this);
39948 this.inputEl().on("keyup", this.onKeyUp, this);
39950 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39952 this.view = new Roo.View(this.list, this.tpl, {
39953 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39956 this.view.on('click', this.onViewClick, this);
39957 this.setValue(this.defaultDialCode);
39960 onTriggerClick : function(e)
39962 Roo.log('trigger click');
39967 if(this.isExpanded()){
39969 this.hasFocus = false;
39971 this.store.load({});
39972 this.hasFocus = true;
39977 isExpanded : function()
39979 return this.list.isVisible();
39982 collapse : function()
39984 if(!this.isExpanded()){
39988 Roo.get(document).un('mousedown', this.collapseIf, this);
39989 Roo.get(document).un('mousewheel', this.collapseIf, this);
39990 this.fireEvent('collapse', this);
39994 expand : function()
39998 if(this.isExpanded() || !this.hasFocus){
40002 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40003 this.list.setWidth(lw);
40006 this.restrictHeight();
40008 Roo.get(document).on('mousedown', this.collapseIf, this);
40009 Roo.get(document).on('mousewheel', this.collapseIf, this);
40011 this.fireEvent('expand', this);
40014 restrictHeight : function()
40016 this.list.alignTo(this.inputEl(), this.listAlign);
40017 this.list.alignTo(this.inputEl(), this.listAlign);
40020 onViewOver : function(e, t)
40022 if(this.inKeyMode){
40025 var item = this.view.findItemFromChild(t);
40028 var index = this.view.indexOf(item);
40029 this.select(index, false);
40034 onViewClick : function(view, doFocus, el, e)
40036 var index = this.view.getSelectedIndexes()[0];
40038 var r = this.store.getAt(index);
40041 this.onSelect(r, index);
40043 if(doFocus !== false && !this.blockFocus){
40044 this.inputEl().focus();
40048 onViewMove : function(e, t)
40050 this.inKeyMode = false;
40053 select : function(index, scrollIntoView)
40055 this.selectedIndex = index;
40056 this.view.select(index);
40057 if(scrollIntoView !== false){
40058 var el = this.view.getNode(index);
40060 this.list.scrollChildIntoView(el, false);
40065 createList : function()
40067 this.list = Roo.get(document.body).createChild({
40069 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40070 style: 'display:none'
40073 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40076 collapseIf : function(e)
40078 var in_combo = e.within(this.el);
40079 var in_list = e.within(this.list);
40080 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40082 if (in_combo || in_list || is_list) {
40088 onSelect : function(record, index)
40090 if(this.fireEvent('beforeselect', this, record, index) !== false){
40092 this.setFlagClass(record.data.iso2);
40093 this.setDialCode(record.data.dialCode);
40094 this.hasFocus = false;
40096 this.fireEvent('select', this, record, index);
40100 flagEl : function()
40102 var flag = this.el.select('div.flag',true).first();
40109 dialCodeHolderEl : function()
40111 var d = this.el.select('input.dial-code-holder',true).first();
40118 setDialCode : function(v)
40120 this.dialCodeHolder.dom.value = '+'+v;
40123 setFlagClass : function(n)
40125 this.flag.dom.className = 'flag '+n;
40128 getValue : function()
40130 var v = this.inputEl().getValue();
40131 if(this.dialCodeHolder) {
40132 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40137 setValue : function(v)
40139 var d = this.getDialCode(v);
40141 //invalid dial code
40142 if(v.length == 0 || !d || d.length == 0) {
40144 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40145 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40151 this.setFlagClass(this.dialCodeMapping[d].iso2);
40152 this.setDialCode(d);
40153 this.inputEl().dom.value = v.replace('+'+d,'');
40154 this.hiddenEl().dom.value = this.getValue();
40159 getDialCode : function(v)
40163 if (v.length == 0) {
40164 return this.dialCodeHolder.dom.value;
40168 if (v.charAt(0) != "+") {
40171 var numericChars = "";
40172 for (var i = 1; i < v.length; i++) {
40173 var c = v.charAt(i);
40176 if (this.dialCodeMapping[numericChars]) {
40177 dialCode = v.substr(1, i);
40179 if (numericChars.length == 4) {
40189 this.setValue(this.defaultDialCode);
40193 hiddenEl : function()
40195 return this.el.select('input.hidden-tel-input',true).first();
40198 onKeyUp : function(e){
40200 var k = e.getKey();
40201 var c = e.getCharCode();
40204 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40205 this.allowed.indexOf(String.fromCharCode(c)) === -1
40210 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40213 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40217 this.setValue(this.getValue());
40222 * @class Roo.bootstrap.MoneyField
40223 * @extends Roo.bootstrap.ComboBox
40224 * Bootstrap MoneyField class
40227 * Create a new MoneyField.
40228 * @param {Object} config Configuration options
40231 Roo.bootstrap.MoneyField = function(config) {
40233 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40237 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40240 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40242 allowDecimals : true,
40244 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40246 decimalSeparator : ".",
40248 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40250 decimalPrecision : 0,
40252 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40254 allowNegative : true,
40256 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40260 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40262 minValue : Number.NEGATIVE_INFINITY,
40264 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40266 maxValue : Number.MAX_VALUE,
40268 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40270 minText : "The minimum value for this field is {0}",
40272 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40274 maxText : "The maximum value for this field is {0}",
40276 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40277 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40279 nanText : "{0} is not a valid number",
40281 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40285 * @cfg {String} defaults currency of the MoneyField
40286 * value should be in lkey
40288 defaultCurrency : false,
40290 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40292 thousandsDelimiter : false,
40302 getAutoCreate : function()
40304 var align = this.labelAlign || this.parentLabelAlign();
40316 cls : 'form-control roo-money-amount-input',
40317 autocomplete: 'new-password'
40320 var hiddenInput = {
40324 cls: 'hidden-number-input'
40328 hiddenInput.name = this.name;
40331 if (this.disabled) {
40332 input.disabled = true;
40335 var clg = 12 - this.inputlg;
40336 var cmd = 12 - this.inputmd;
40337 var csm = 12 - this.inputsm;
40338 var cxs = 12 - this.inputxs;
40342 cls : 'row roo-money-field',
40346 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40350 cls: 'roo-select2-container input-group',
40354 cls : 'form-control roo-money-currency-input',
40355 autocomplete: 'new-password',
40357 name : this.currencyName
40361 cls : 'input-group-addon',
40375 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40379 cls: this.hasFeedback ? 'has-feedback' : '',
40390 if (this.fieldLabel.length) {
40393 tooltip: 'This field is required'
40399 cls: 'control-label',
40405 html: this.fieldLabel
40408 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40414 if(this.indicatorpos == 'right') {
40415 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40422 if(align == 'left') {
40430 if(this.labelWidth > 12){
40431 label.style = "width: " + this.labelWidth + 'px';
40433 if(this.labelWidth < 13 && this.labelmd == 0){
40434 this.labelmd = this.labelWidth;
40436 if(this.labellg > 0){
40437 label.cls += ' col-lg-' + this.labellg;
40438 input.cls += ' col-lg-' + (12 - this.labellg);
40440 if(this.labelmd > 0){
40441 label.cls += ' col-md-' + this.labelmd;
40442 container.cls += ' col-md-' + (12 - this.labelmd);
40444 if(this.labelsm > 0){
40445 label.cls += ' col-sm-' + this.labelsm;
40446 container.cls += ' col-sm-' + (12 - this.labelsm);
40448 if(this.labelxs > 0){
40449 label.cls += ' col-xs-' + this.labelxs;
40450 container.cls += ' col-xs-' + (12 - this.labelxs);
40461 var settings = this;
40463 ['xs','sm','md','lg'].map(function(size){
40464 if (settings[size]) {
40465 cfg.cls += ' col-' + size + '-' + settings[size];
40472 initEvents : function()
40474 this.indicator = this.indicatorEl();
40476 this.initCurrencyEvent();
40478 this.initNumberEvent();
40481 initCurrencyEvent : function()
40484 throw "can not find store for combo";
40487 this.store = Roo.factory(this.store, Roo.data);
40488 this.store.parent = this;
40492 this.triggerEl = this.el.select('.input-group-addon', true).first();
40494 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40499 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40500 _this.list.setWidth(lw);
40503 this.list.on('mouseover', this.onViewOver, this);
40504 this.list.on('mousemove', this.onViewMove, this);
40505 this.list.on('scroll', this.onViewScroll, this);
40508 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40511 this.view = new Roo.View(this.list, this.tpl, {
40512 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40515 this.view.on('click', this.onViewClick, this);
40517 this.store.on('beforeload', this.onBeforeLoad, this);
40518 this.store.on('load', this.onLoad, this);
40519 this.store.on('loadexception', this.onLoadException, this);
40521 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40522 "up" : function(e){
40523 this.inKeyMode = true;
40527 "down" : function(e){
40528 if(!this.isExpanded()){
40529 this.onTriggerClick();
40531 this.inKeyMode = true;
40536 "enter" : function(e){
40539 if(this.fireEvent("specialkey", this, e)){
40540 this.onViewClick(false);
40546 "esc" : function(e){
40550 "tab" : function(e){
40553 if(this.fireEvent("specialkey", this, e)){
40554 this.onViewClick(false);
40562 doRelay : function(foo, bar, hname){
40563 if(hname == 'down' || this.scope.isExpanded()){
40564 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40572 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40576 initNumberEvent : function(e)
40578 this.inputEl().on("keydown" , this.fireKey, this);
40579 this.inputEl().on("focus", this.onFocus, this);
40580 this.inputEl().on("blur", this.onBlur, this);
40582 this.inputEl().relayEvent('keyup', this);
40584 if(this.indicator){
40585 this.indicator.addClass('invisible');
40588 this.originalValue = this.getValue();
40590 if(this.validationEvent == 'keyup'){
40591 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40592 this.inputEl().on('keyup', this.filterValidation, this);
40594 else if(this.validationEvent !== false){
40595 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40598 if(this.selectOnFocus){
40599 this.on("focus", this.preFocus, this);
40602 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40603 this.inputEl().on("keypress", this.filterKeys, this);
40605 this.inputEl().relayEvent('keypress', this);
40608 var allowed = "0123456789";
40610 if(this.allowDecimals){
40611 allowed += this.decimalSeparator;
40614 if(this.allowNegative){
40618 if(this.thousandsDelimiter) {
40622 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40624 var keyPress = function(e){
40626 var k = e.getKey();
40628 var c = e.getCharCode();
40631 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40632 allowed.indexOf(String.fromCharCode(c)) === -1
40638 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40642 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40647 this.inputEl().on("keypress", keyPress, this);
40651 onTriggerClick : function(e)
40658 this.loadNext = false;
40660 if(this.isExpanded()){
40665 this.hasFocus = true;
40667 if(this.triggerAction == 'all') {
40668 this.doQuery(this.allQuery, true);
40672 this.doQuery(this.getRawValue());
40675 getCurrency : function()
40677 var v = this.currencyEl().getValue();
40682 restrictHeight : function()
40684 this.list.alignTo(this.currencyEl(), this.listAlign);
40685 this.list.alignTo(this.currencyEl(), this.listAlign);
40688 onViewClick : function(view, doFocus, el, e)
40690 var index = this.view.getSelectedIndexes()[0];
40692 var r = this.store.getAt(index);
40695 this.onSelect(r, index);
40699 onSelect : function(record, index){
40701 if(this.fireEvent('beforeselect', this, record, index) !== false){
40703 this.setFromCurrencyData(index > -1 ? record.data : false);
40707 this.fireEvent('select', this, record, index);
40711 setFromCurrencyData : function(o)
40715 this.lastCurrency = o;
40717 if (this.currencyField) {
40718 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40720 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40723 this.lastSelectionText = currency;
40725 //setting default currency
40726 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40727 this.setCurrency(this.defaultCurrency);
40731 this.setCurrency(currency);
40734 setFromData : function(o)
40738 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40740 this.setFromCurrencyData(c);
40745 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40747 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40750 this.setValue(value);
40754 setCurrency : function(v)
40756 this.currencyValue = v;
40759 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40764 setValue : function(v)
40766 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40772 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40774 this.inputEl().dom.value = (v == '') ? '' :
40775 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40777 if(!this.allowZero && v === '0') {
40778 this.hiddenEl().dom.value = '';
40779 this.inputEl().dom.value = '';
40786 getRawValue : function()
40788 var v = this.inputEl().getValue();
40793 getValue : function()
40795 return this.fixPrecision(this.parseValue(this.getRawValue()));
40798 parseValue : function(value)
40800 if(this.thousandsDelimiter) {
40802 r = new RegExp(",", "g");
40803 value = value.replace(r, "");
40806 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40807 return isNaN(value) ? '' : value;
40811 fixPrecision : function(value)
40813 if(this.thousandsDelimiter) {
40815 r = new RegExp(",", "g");
40816 value = value.replace(r, "");
40819 var nan = isNaN(value);
40821 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40822 return nan ? '' : value;
40824 return parseFloat(value).toFixed(this.decimalPrecision);
40827 decimalPrecisionFcn : function(v)
40829 return Math.floor(v);
40832 validateValue : function(value)
40834 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40838 var num = this.parseValue(value);
40841 this.markInvalid(String.format(this.nanText, value));
40845 if(num < this.minValue){
40846 this.markInvalid(String.format(this.minText, this.minValue));
40850 if(num > this.maxValue){
40851 this.markInvalid(String.format(this.maxText, this.maxValue));
40858 validate : function()
40860 if(this.disabled || this.allowBlank){
40865 var currency = this.getCurrency();
40867 if(this.validateValue(this.getRawValue()) && currency.length){
40872 this.markInvalid();
40876 getName: function()
40881 beforeBlur : function()
40887 var v = this.parseValue(this.getRawValue());
40894 onBlur : function()
40898 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40899 //this.el.removeClass(this.focusClass);
40902 this.hasFocus = false;
40904 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40908 var v = this.getValue();
40910 if(String(v) !== String(this.startValue)){
40911 this.fireEvent('change', this, v, this.startValue);
40914 this.fireEvent("blur", this);
40917 inputEl : function()
40919 return this.el.select('.roo-money-amount-input', true).first();
40922 currencyEl : function()
40924 return this.el.select('.roo-money-currency-input', true).first();
40927 hiddenEl : function()
40929 return this.el.select('input.hidden-number-input',true).first();