4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
407 * Hide a component - adds 'hidden' class
411 if(!this.getVisibilityEl()){
415 this.getVisibilityEl().addClass('hidden');
428 * @class Roo.bootstrap.Body
429 * @extends Roo.bootstrap.Component
430 * Bootstrap Body class
434 * @param {Object} config The config object
437 Roo.bootstrap.Body = function(config){
439 config = config || {};
441 Roo.bootstrap.Body.superclass.constructor.call(this, config);
442 this.el = Roo.get(config.el ? config.el : document.body );
443 if (this.cls && this.cls.length) {
444 Roo.get(document.body).addClass(this.cls);
448 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
450 is_body : true,// just to make sure it's constructed?
455 onRender : function(ct, position)
457 /* Roo.log("Roo.bootstrap.Body - onRender");
458 if (this.cls && this.cls.length) {
459 Roo.get(document.body).addClass(this.cls);
478 * @class Roo.bootstrap.ButtonGroup
479 * @extends Roo.bootstrap.Component
480 * Bootstrap ButtonGroup class
481 * @cfg {String} size lg | sm | xs (default empty normal)
482 * @cfg {String} align vertical | justified (default none)
483 * @cfg {String} direction up | down (default down)
484 * @cfg {Boolean} toolbar false | true
485 * @cfg {Boolean} btn true | false
490 * @param {Object} config The config object
493 Roo.bootstrap.ButtonGroup = function(config){
494 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
497 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
505 getAutoCreate : function(){
511 cfg.html = this.html || cfg.html;
522 if (['vertical','justified'].indexOf(this.align)!==-1) {
523 cfg.cls = 'btn-group-' + this.align;
525 if (this.align == 'justified') {
526 console.log(this.items);
530 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
531 cfg.cls += ' btn-group-' + this.size;
534 if (this.direction == 'up') {
535 cfg.cls += ' dropup' ;
551 * @class Roo.bootstrap.Button
552 * @extends Roo.bootstrap.Component
553 * Bootstrap Button class
554 * @cfg {String} html The button content
555 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
556 * @cfg {String} size ( lg | sm | xs)
557 * @cfg {String} tag ( a | input | submit)
558 * @cfg {String} href empty or href
559 * @cfg {Boolean} disabled default false;
560 * @cfg {Boolean} isClose default false;
561 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
562 * @cfg {String} badge text for badge
563 * @cfg {String} theme (default|glow)
564 * @cfg {Boolean} inverse dark themed version
565 * @cfg {Boolean} toggle is it a slidy toggle button
566 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
567 * @cfg {String} ontext text for on slidy toggle state
568 * @cfg {String} offtext text for off slidy toggle state
569 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
570 * @cfg {Boolean} removeClass remove the standard class..
571 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
574 * Create a new button
575 * @param {Object} config The config object
579 Roo.bootstrap.Button = function(config){
580 Roo.bootstrap.Button.superclass.constructor.call(this, config);
581 this.weightClass = ["btn-default",
593 * When a butotn is pressed
594 * @param {Roo.bootstrap.Button} btn
595 * @param {Roo.EventObject} e
600 * After the button has been toggles
601 * @param {Roo.bootstrap.Button} btn
602 * @param {Roo.EventObject} e
603 * @param {boolean} pressed (also available as button.pressed)
609 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
627 preventDefault: true,
635 getAutoCreate : function(){
643 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
644 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
651 if (this.toggle == true) {
654 cls: 'slider-frame roo-button',
659 'data-off-text':'OFF',
660 cls: 'slider-button',
666 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667 cfg.cls += ' '+this.weight;
676 cfg["aria-hidden"] = true;
678 cfg.html = "×";
684 if (this.theme==='default') {
685 cfg.cls = 'btn roo-button';
687 //if (this.parentType != 'Navbar') {
688 this.weight = this.weight.length ? this.weight : 'default';
690 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
692 cfg.cls += ' btn-' + this.weight;
694 } else if (this.theme==='glow') {
697 cfg.cls = 'btn-glow roo-button';
699 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701 cfg.cls += ' ' + this.weight;
707 this.cls += ' inverse';
711 if (this.active || this.pressed === true) {
712 cfg.cls += ' active';
716 cfg.disabled = 'disabled';
720 Roo.log('changing to ul' );
722 this.glyphicon = 'caret';
725 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
727 //gsRoo.log(this.parentType);
728 if (this.parentType === 'Navbar' && !this.parent().bar) {
729 Roo.log('changing to li?');
738 href : this.href || '#'
741 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
742 cfg.cls += ' dropdown';
749 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
751 if (this.glyphicon) {
752 cfg.html = ' ' + cfg.html;
757 cls: 'glyphicon glyphicon-' + this.glyphicon
767 // cfg.cls='btn roo-button';
771 var value = cfg.html;
776 cls: 'glyphicon glyphicon-' + this.glyphicon,
795 cfg.cls += ' dropdown';
796 cfg.html = typeof(cfg.html) != 'undefined' ?
797 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
800 if (cfg.tag !== 'a' && this.href !== '') {
801 throw "Tag must be a to set href.";
802 } else if (this.href.length > 0) {
803 cfg.href = this.href;
806 if(this.removeClass){
811 cfg.target = this.target;
816 initEvents: function() {
817 // Roo.log('init events?');
818 // Roo.log(this.el.dom);
821 if (typeof (this.menu) != 'undefined') {
822 this.menu.parentType = this.xtype;
823 this.menu.triggerEl = this.el;
824 this.addxtype(Roo.apply({}, this.menu));
828 if (this.el.hasClass('roo-button')) {
829 this.el.on('click', this.onClick, this);
831 this.el.select('.roo-button').on('click', this.onClick, this);
834 if(this.removeClass){
835 this.el.on('click', this.onClick, this);
838 this.el.enableDisplayMode();
841 onClick : function(e)
847 Roo.log('button on click ');
848 if(this.preventDefault){
852 if (this.pressed === true || this.pressed === false) {
853 this.toggleActive(e);
857 this.fireEvent('click', this, e);
861 * Enables this button
865 this.disabled = false;
866 this.el.removeClass('disabled');
870 * Disable this button
874 this.disabled = true;
875 this.el.addClass('disabled');
878 * sets the active state on/off,
879 * @param {Boolean} state (optional) Force a particular state
881 setActive : function(v) {
883 this.el[v ? 'addClass' : 'removeClass']('active');
887 * toggles the current active state
889 toggleActive : function(e)
891 this.setActive(!this.pressed);
892 this.fireEvent('toggle', this, e, !this.pressed);
895 * get the current active state
896 * @return {boolean} true if it's active
898 isActive : function()
900 return this.el.hasClass('active');
903 * set the text of the first selected button
905 setText : function(str)
907 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
910 * get the text of the first selected button
914 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
925 setWeight : function(str)
927 this.el.removeClass(this.weightClass);
928 this.el.addClass('btn-' + str);
942 * @class Roo.bootstrap.Column
943 * @extends Roo.bootstrap.Component
944 * Bootstrap Column class
945 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
946 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
947 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
948 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
949 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
955 * @cfg {Boolean} hidden (true|false) hide the element
956 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
957 * @cfg {String} fa (ban|check|...) font awesome icon
958 * @cfg {Number} fasize (1|2|....) font awsome size
960 * @cfg {String} icon (info-sign|check|...) glyphicon name
962 * @cfg {String} html content of column.
965 * Create a new Column
966 * @param {Object} config The config object
969 Roo.bootstrap.Column = function(config){
970 Roo.bootstrap.Column.superclass.constructor.call(this, config);
973 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
991 getAutoCreate : function(){
992 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1000 ['xs','sm','md','lg'].map(function(size){
1001 //Roo.log( size + ':' + settings[size]);
1003 if (settings[size+'off'] !== false) {
1004 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1007 if (settings[size] === false) {
1011 if (!settings[size]) { // 0 = hidden
1012 cfg.cls += ' hidden-' + size;
1015 cfg.cls += ' col-' + size + '-' + settings[size];
1020 cfg.cls += ' hidden';
1023 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1024 cfg.cls +=' alert alert-' + this.alert;
1028 if (this.html.length) {
1029 cfg.html = this.html;
1033 if (this.fasize > 1) {
1034 fasize = ' fa-' + this.fasize + 'x';
1036 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1041 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1060 * @class Roo.bootstrap.Container
1061 * @extends Roo.bootstrap.Component
1062 * Bootstrap Container class
1063 * @cfg {Boolean} jumbotron is it a jumbotron element
1064 * @cfg {String} html content of element
1065 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1066 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1067 * @cfg {String} header content of header (for panel)
1068 * @cfg {String} footer content of footer (for panel)
1069 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1070 * @cfg {String} tag (header|aside|section) type of HTML tag.
1071 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1072 * @cfg {String} fa font awesome icon
1073 * @cfg {String} icon (info-sign|check|...) glyphicon name
1074 * @cfg {Boolean} hidden (true|false) hide the element
1075 * @cfg {Boolean} expandable (true|false) default false
1076 * @cfg {Boolean} expanded (true|false) default true
1077 * @cfg {String} rheader contet on the right of header
1078 * @cfg {Boolean} clickable (true|false) default false
1082 * Create a new Container
1083 * @param {Object} config The config object
1086 Roo.bootstrap.Container = function(config){
1087 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1093 * After the panel has been expand
1095 * @param {Roo.bootstrap.Container} this
1100 * After the panel has been collapsed
1102 * @param {Roo.bootstrap.Container} this
1107 * When a element is chick
1108 * @param {Roo.bootstrap.Container} this
1109 * @param {Roo.EventObject} e
1115 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1133 getChildContainer : function() {
1139 if (this.panel.length) {
1140 return this.el.select('.panel-body',true).first();
1147 getAutoCreate : function(){
1150 tag : this.tag || 'div',
1154 if (this.jumbotron) {
1155 cfg.cls = 'jumbotron';
1160 // - this is applied by the parent..
1162 // cfg.cls = this.cls + '';
1165 if (this.sticky.length) {
1167 var bd = Roo.get(document.body);
1168 if (!bd.hasClass('bootstrap-sticky')) {
1169 bd.addClass('bootstrap-sticky');
1170 Roo.select('html',true).setStyle('height', '100%');
1173 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1177 if (this.well.length) {
1178 switch (this.well) {
1181 cfg.cls +=' well well-' +this.well;
1190 cfg.cls += ' hidden';
1194 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1195 cfg.cls +=' alert alert-' + this.alert;
1200 if (this.panel.length) {
1201 cfg.cls += ' panel panel-' + this.panel;
1203 if (this.header.length) {
1207 if(this.expandable){
1209 cfg.cls = cfg.cls + ' expandable';
1213 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1221 cls : 'panel-title',
1222 html : (this.expandable ? ' ' : '') + this.header
1226 cls: 'panel-header-right',
1232 cls : 'panel-heading',
1233 style : this.expandable ? 'cursor: pointer' : '',
1241 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1246 if (this.footer.length) {
1248 cls : 'panel-footer',
1257 body.html = this.html || cfg.html;
1258 // prefix with the icons..
1260 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1263 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1268 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1269 cfg.cls = 'container';
1275 initEvents: function()
1277 if(this.expandable){
1278 var headerEl = this.headerEl();
1281 headerEl.on('click', this.onToggleClick, this);
1286 this.el.on('click', this.onClick, this);
1291 onToggleClick : function()
1293 var headerEl = this.headerEl();
1309 if(this.fireEvent('expand', this)) {
1311 this.expanded = true;
1313 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1315 this.el.select('.panel-body',true).first().removeClass('hide');
1317 var toggleEl = this.toggleEl();
1323 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1328 collapse : function()
1330 if(this.fireEvent('collapse', this)) {
1332 this.expanded = false;
1334 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1335 this.el.select('.panel-body',true).first().addClass('hide');
1337 var toggleEl = this.toggleEl();
1343 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1347 toggleEl : function()
1349 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1353 return this.el.select('.panel-heading .fa',true).first();
1356 headerEl : function()
1358 if(!this.el || !this.panel.length || !this.header.length){
1362 return this.el.select('.panel-heading',true).first()
1367 if(!this.el || !this.panel.length){
1371 return this.el.select('.panel-body',true).first()
1374 titleEl : function()
1376 if(!this.el || !this.panel.length || !this.header.length){
1380 return this.el.select('.panel-title',true).first();
1383 setTitle : function(v)
1385 var titleEl = this.titleEl();
1391 titleEl.dom.innerHTML = v;
1394 getTitle : function()
1397 var titleEl = this.titleEl();
1403 return titleEl.dom.innerHTML;
1406 setRightTitle : function(v)
1408 var t = this.el.select('.panel-header-right',true).first();
1414 t.dom.innerHTML = v;
1417 onClick : function(e)
1421 this.fireEvent('click', this, e);
1434 * @class Roo.bootstrap.Img
1435 * @extends Roo.bootstrap.Component
1436 * Bootstrap Img class
1437 * @cfg {Boolean} imgResponsive false | true
1438 * @cfg {String} border rounded | circle | thumbnail
1439 * @cfg {String} src image source
1440 * @cfg {String} alt image alternative text
1441 * @cfg {String} href a tag href
1442 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1443 * @cfg {String} xsUrl xs image source
1444 * @cfg {String} smUrl sm image source
1445 * @cfg {String} mdUrl md image source
1446 * @cfg {String} lgUrl lg image source
1449 * Create a new Input
1450 * @param {Object} config The config object
1453 Roo.bootstrap.Img = function(config){
1454 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1460 * The img click event for the img.
1461 * @param {Roo.EventObject} e
1467 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1469 imgResponsive: true,
1479 getAutoCreate : function()
1481 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1482 return this.createSingleImg();
1487 cls: 'roo-image-responsive-group',
1492 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1494 if(!_this[size + 'Url']){
1500 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1501 html: _this.html || cfg.html,
1502 src: _this[size + 'Url']
1505 img.cls += ' roo-image-responsive-' + size;
1507 var s = ['xs', 'sm', 'md', 'lg'];
1509 s.splice(s.indexOf(size), 1);
1511 Roo.each(s, function(ss){
1512 img.cls += ' hidden-' + ss;
1515 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1516 cfg.cls += ' img-' + _this.border;
1520 cfg.alt = _this.alt;
1533 a.target = _this.target;
1537 cfg.cn.push((_this.href) ? a : img);
1544 createSingleImg : function()
1548 cls: (this.imgResponsive) ? 'img-responsive' : '',
1550 src : 'about:blank' // just incase src get's set to undefined?!?
1553 cfg.html = this.html || cfg.html;
1555 cfg.src = this.src || cfg.src;
1557 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1558 cfg.cls += ' img-' + this.border;
1575 a.target = this.target;
1580 return (this.href) ? a : cfg;
1583 initEvents: function()
1586 this.el.on('click', this.onClick, this);
1591 onClick : function(e)
1593 Roo.log('img onclick');
1594 this.fireEvent('click', this, e);
1597 * Sets the url of the image - used to update it
1598 * @param {String} url the url of the image
1601 setSrc : function(url)
1605 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1606 this.el.dom.src = url;
1610 this.el.select('img', true).first().dom.src = url;
1626 * @class Roo.bootstrap.Link
1627 * @extends Roo.bootstrap.Component
1628 * Bootstrap Link Class
1629 * @cfg {String} alt image alternative text
1630 * @cfg {String} href a tag href
1631 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1632 * @cfg {String} html the content of the link.
1633 * @cfg {String} anchor name for the anchor link
1634 * @cfg {String} fa - favicon
1636 * @cfg {Boolean} preventDefault (true | false) default false
1640 * Create a new Input
1641 * @param {Object} config The config object
1644 Roo.bootstrap.Link = function(config){
1645 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1651 * The img click event for the img.
1652 * @param {Roo.EventObject} e
1658 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1662 preventDefault: false,
1668 getAutoCreate : function()
1670 var html = this.html || '';
1672 if (this.fa !== false) {
1673 html = '<i class="fa fa-' + this.fa + '"></i>';
1678 // anchor's do not require html/href...
1679 if (this.anchor === false) {
1681 cfg.href = this.href || '#';
1683 cfg.name = this.anchor;
1684 if (this.html !== false || this.fa !== false) {
1687 if (this.href !== false) {
1688 cfg.href = this.href;
1692 if(this.alt !== false){
1697 if(this.target !== false) {
1698 cfg.target = this.target;
1704 initEvents: function() {
1706 if(!this.href || this.preventDefault){
1707 this.el.on('click', this.onClick, this);
1711 onClick : function(e)
1713 if(this.preventDefault){
1716 //Roo.log('img onclick');
1717 this.fireEvent('click', this, e);
1730 * @class Roo.bootstrap.Header
1731 * @extends Roo.bootstrap.Component
1732 * Bootstrap Header class
1733 * @cfg {String} html content of header
1734 * @cfg {Number} level (1|2|3|4|5|6) default 1
1737 * Create a new Header
1738 * @param {Object} config The config object
1742 Roo.bootstrap.Header = function(config){
1743 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1746 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1754 getAutoCreate : function(){
1759 tag: 'h' + (1 *this.level),
1760 html: this.html || ''
1772 * Ext JS Library 1.1.1
1773 * Copyright(c) 2006-2007, Ext JS, LLC.
1775 * Originally Released Under LGPL - original licence link has changed is not relivant.
1778 * <script type="text/javascript">
1782 * @class Roo.bootstrap.MenuMgr
1783 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1786 Roo.bootstrap.MenuMgr = function(){
1787 var menus, active, groups = {}, attached = false, lastShow = new Date();
1789 // private - called when first menu is created
1792 active = new Roo.util.MixedCollection();
1793 Roo.get(document).addKeyListener(27, function(){
1794 if(active.length > 0){
1802 if(active && active.length > 0){
1803 var c = active.clone();
1813 if(active.length < 1){
1814 Roo.get(document).un("mouseup", onMouseDown);
1822 var last = active.last();
1823 lastShow = new Date();
1826 Roo.get(document).on("mouseup", onMouseDown);
1831 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1832 m.parentMenu.activeChild = m;
1833 }else if(last && last.isVisible()){
1834 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1839 function onBeforeHide(m){
1841 m.activeChild.hide();
1843 if(m.autoHideTimer){
1844 clearTimeout(m.autoHideTimer);
1845 delete m.autoHideTimer;
1850 function onBeforeShow(m){
1851 var pm = m.parentMenu;
1852 if(!pm && !m.allowOtherMenus){
1854 }else if(pm && pm.activeChild && active != m){
1855 pm.activeChild.hide();
1859 // private this should really trigger on mouseup..
1860 function onMouseDown(e){
1861 Roo.log("on Mouse Up");
1863 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1864 Roo.log("MenuManager hideAll");
1873 function onBeforeCheck(mi, state){
1875 var g = groups[mi.group];
1876 for(var i = 0, l = g.length; i < l; i++){
1878 g[i].setChecked(false);
1887 * Hides all menus that are currently visible
1889 hideAll : function(){
1894 register : function(menu){
1898 menus[menu.id] = menu;
1899 menu.on("beforehide", onBeforeHide);
1900 menu.on("hide", onHide);
1901 menu.on("beforeshow", onBeforeShow);
1902 menu.on("show", onShow);
1904 if(g && menu.events["checkchange"]){
1908 groups[g].push(menu);
1909 menu.on("checkchange", onCheck);
1914 * Returns a {@link Roo.menu.Menu} object
1915 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1916 * be used to generate and return a new Menu instance.
1918 get : function(menu){
1919 if(typeof menu == "string"){ // menu id
1921 }else if(menu.events){ // menu instance
1924 /*else if(typeof menu.length == 'number'){ // array of menu items?
1925 return new Roo.bootstrap.Menu({items:menu});
1926 }else{ // otherwise, must be a config
1927 return new Roo.bootstrap.Menu(menu);
1934 unregister : function(menu){
1935 delete menus[menu.id];
1936 menu.un("beforehide", onBeforeHide);
1937 menu.un("hide", onHide);
1938 menu.un("beforeshow", onBeforeShow);
1939 menu.un("show", onShow);
1941 if(g && menu.events["checkchange"]){
1942 groups[g].remove(menu);
1943 menu.un("checkchange", onCheck);
1948 registerCheckable : function(menuItem){
1949 var g = menuItem.group;
1954 groups[g].push(menuItem);
1955 menuItem.on("beforecheckchange", onBeforeCheck);
1960 unregisterCheckable : function(menuItem){
1961 var g = menuItem.group;
1963 groups[g].remove(menuItem);
1964 menuItem.un("beforecheckchange", onBeforeCheck);
1976 * @class Roo.bootstrap.Menu
1977 * @extends Roo.bootstrap.Component
1978 * Bootstrap Menu class - container for MenuItems
1979 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1980 * @cfg {bool} hidden if the menu should be hidden when rendered.
1981 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1982 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1986 * @param {Object} config The config object
1990 Roo.bootstrap.Menu = function(config){
1991 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1992 if (this.registerMenu && this.type != 'treeview') {
1993 Roo.bootstrap.MenuMgr.register(this);
1998 * Fires before this menu is displayed
1999 * @param {Roo.menu.Menu} this
2004 * Fires before this menu is hidden
2005 * @param {Roo.menu.Menu} this
2010 * Fires after this menu is displayed
2011 * @param {Roo.menu.Menu} this
2016 * Fires after this menu is hidden
2017 * @param {Roo.menu.Menu} this
2022 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2023 * @param {Roo.menu.Menu} this
2024 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2025 * @param {Roo.EventObject} e
2030 * Fires when the mouse is hovering over this menu
2031 * @param {Roo.menu.Menu} this
2032 * @param {Roo.EventObject} e
2033 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038 * Fires when the mouse exits this menu
2039 * @param {Roo.menu.Menu} this
2040 * @param {Roo.EventObject} e
2041 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2046 * Fires when a menu item contained in this menu is clicked
2047 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2048 * @param {Roo.EventObject} e
2052 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2055 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2059 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2062 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2064 registerMenu : true,
2066 menuItems :false, // stores the menu items..
2076 getChildContainer : function() {
2080 getAutoCreate : function(){
2082 //if (['right'].indexOf(this.align)!==-1) {
2083 // cfg.cn[1].cls += ' pull-right'
2089 cls : 'dropdown-menu' ,
2090 style : 'z-index:1000'
2094 if (this.type === 'submenu') {
2095 cfg.cls = 'submenu active';
2097 if (this.type === 'treeview') {
2098 cfg.cls = 'treeview-menu';
2103 initEvents : function() {
2105 // Roo.log("ADD event");
2106 // Roo.log(this.triggerEl.dom);
2108 this.triggerEl.on('click', this.onTriggerClick, this);
2110 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2112 this.triggerEl.addClass('dropdown-toggle');
2115 this.el.on('touchstart' , this.onTouch, this);
2117 this.el.on('click' , this.onClick, this);
2119 this.el.on("mouseover", this.onMouseOver, this);
2120 this.el.on("mouseout", this.onMouseOut, this);
2124 findTargetItem : function(e)
2126 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2130 //Roo.log(t); Roo.log(t.id);
2132 //Roo.log(this.menuitems);
2133 return this.menuitems.get(t.id);
2135 //return this.items.get(t.menuItemId);
2141 onTouch : function(e)
2143 Roo.log("menu.onTouch");
2144 //e.stopEvent(); this make the user popdown broken
2148 onClick : function(e)
2150 Roo.log("menu.onClick");
2152 var t = this.findTargetItem(e);
2153 if(!t || t.isContainer){
2158 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2159 if(t == this.activeItem && t.shouldDeactivate(e)){
2160 this.activeItem.deactivate();
2161 delete this.activeItem;
2165 this.setActiveItem(t, true);
2173 Roo.log('pass click event');
2177 this.fireEvent("click", this, t, e);
2181 if(!t.href.length || t.href == '#'){
2182 (function() { _this.hide(); }).defer(100);
2187 onMouseOver : function(e){
2188 var t = this.findTargetItem(e);
2191 // if(t.canActivate && !t.disabled){
2192 // this.setActiveItem(t, true);
2196 this.fireEvent("mouseover", this, e, t);
2198 isVisible : function(){
2199 return !this.hidden;
2201 onMouseOut : function(e){
2202 var t = this.findTargetItem(e);
2205 // if(t == this.activeItem && t.shouldDeactivate(e)){
2206 // this.activeItem.deactivate();
2207 // delete this.activeItem;
2210 this.fireEvent("mouseout", this, e, t);
2215 * Displays this menu relative to another element
2216 * @param {String/HTMLElement/Roo.Element} element The element to align to
2217 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2218 * the element (defaults to this.defaultAlign)
2219 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2221 show : function(el, pos, parentMenu){
2222 this.parentMenu = parentMenu;
2226 this.fireEvent("beforeshow", this);
2227 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2230 * Displays this menu at a specific xy position
2231 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2232 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2234 showAt : function(xy, parentMenu, /* private: */_e){
2235 this.parentMenu = parentMenu;
2240 this.fireEvent("beforeshow", this);
2241 //xy = this.el.adjustForConstraints(xy);
2245 this.hideMenuItems();
2246 this.hidden = false;
2247 this.triggerEl.addClass('open');
2249 // reassign x when hitting right
2250 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2251 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2254 // reassign y when hitting bottom
2255 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2256 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2259 // but the list may align on trigger left or trigger top... should it be a properity?
2261 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2266 this.fireEvent("show", this);
2272 this.doFocus.defer(50, this);
2276 doFocus : function(){
2278 this.focusEl.focus();
2283 * Hides this menu and optionally all parent menus
2284 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2286 hide : function(deep)
2289 this.hideMenuItems();
2290 if(this.el && this.isVisible()){
2291 this.fireEvent("beforehide", this);
2292 if(this.activeItem){
2293 this.activeItem.deactivate();
2294 this.activeItem = null;
2296 this.triggerEl.removeClass('open');;
2298 this.fireEvent("hide", this);
2300 if(deep === true && this.parentMenu){
2301 this.parentMenu.hide(true);
2305 onTriggerClick : function(e)
2307 Roo.log('trigger click');
2309 var target = e.getTarget();
2311 Roo.log(target.nodeName.toLowerCase());
2313 if(target.nodeName.toLowerCase() === 'i'){
2319 onTriggerPress : function(e)
2321 Roo.log('trigger press');
2322 //Roo.log(e.getTarget());
2323 // Roo.log(this.triggerEl.dom);
2325 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2326 var pel = Roo.get(e.getTarget());
2327 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2328 Roo.log('is treeview or dropdown?');
2332 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2336 if (this.isVisible()) {
2341 this.show(this.triggerEl, false, false);
2344 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2351 hideMenuItems : function()
2353 Roo.log("hide Menu Items");
2357 //$(backdrop).remove()
2358 this.el.select('.open',true).each(function(aa) {
2360 aa.removeClass('open');
2361 //var parent = getParent($(this))
2362 //var relatedTarget = { relatedTarget: this }
2364 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2365 //if (e.isDefaultPrevented()) return
2366 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2369 addxtypeChild : function (tree, cntr) {
2370 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2372 this.menuitems.add(comp);
2384 this.getEl().dom.innerHTML = '';
2385 this.menuitems.clear();
2399 * @class Roo.bootstrap.MenuItem
2400 * @extends Roo.bootstrap.Component
2401 * Bootstrap MenuItem class
2402 * @cfg {String} html the menu label
2403 * @cfg {String} href the link
2404 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2405 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2406 * @cfg {Boolean} active used on sidebars to highlight active itesm
2407 * @cfg {String} fa favicon to show on left of menu item.
2408 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2412 * Create a new MenuItem
2413 * @param {Object} config The config object
2417 Roo.bootstrap.MenuItem = function(config){
2418 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2423 * The raw click event for the entire grid.
2424 * @param {Roo.bootstrap.MenuItem} this
2425 * @param {Roo.EventObject} e
2431 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2435 preventDefault: false,
2436 isContainer : false,
2440 getAutoCreate : function(){
2442 if(this.isContainer){
2445 cls: 'dropdown-menu-item'
2459 if (this.fa !== false) {
2462 cls : 'fa fa-' + this.fa
2471 cls: 'dropdown-menu-item',
2474 if (this.parent().type == 'treeview') {
2475 cfg.cls = 'treeview-menu';
2478 cfg.cls += ' active';
2483 anc.href = this.href || cfg.cn[0].href ;
2484 ctag.html = this.html || cfg.cn[0].html ;
2488 initEvents: function()
2490 if (this.parent().type == 'treeview') {
2491 this.el.select('a').on('click', this.onClick, this);
2495 this.menu.parentType = this.xtype;
2496 this.menu.triggerEl = this.el;
2497 this.menu = this.addxtype(Roo.apply({}, this.menu));
2501 onClick : function(e)
2503 Roo.log('item on click ');
2505 if(this.preventDefault){
2508 //this.parent().hideMenuItems();
2510 this.fireEvent('click', this, e);
2529 * @class Roo.bootstrap.MenuSeparator
2530 * @extends Roo.bootstrap.Component
2531 * Bootstrap MenuSeparator class
2534 * Create a new MenuItem
2535 * @param {Object} config The config object
2539 Roo.bootstrap.MenuSeparator = function(config){
2540 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2543 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2545 getAutoCreate : function(){
2564 * @class Roo.bootstrap.Modal
2565 * @extends Roo.bootstrap.Component
2566 * Bootstrap Modal class
2567 * @cfg {String} title Title of dialog
2568 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2569 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2570 * @cfg {Boolean} specificTitle default false
2571 * @cfg {Array} buttons Array of buttons or standard button set..
2572 * @cfg {String} buttonPosition (left|right|center) default right
2573 * @cfg {Boolean} animate default true
2574 * @cfg {Boolean} allow_close default true
2575 * @cfg {Boolean} fitwindow default false
2576 * @cfg {String} size (sm|lg) default empty
2580 * Create a new Modal Dialog
2581 * @param {Object} config The config object
2584 Roo.bootstrap.Modal = function(config){
2585 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2590 * The raw btnclick event for the button
2591 * @param {Roo.EventObject} e
2596 * Fire when dialog resize
2597 * @param {Roo.bootstrap.Modal} this
2598 * @param {Roo.EventObject} e
2602 this.buttons = this.buttons || [];
2605 this.tmpl = Roo.factory(this.tmpl);
2610 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2612 title : 'test dialog',
2622 specificTitle: false,
2624 buttonPosition: 'right',
2643 onRender : function(ct, position)
2645 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2648 var cfg = Roo.apply({}, this.getAutoCreate());
2651 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653 //if (!cfg.name.length) {
2657 cfg.cls += ' ' + this.cls;
2660 cfg.style = this.style;
2662 this.el = Roo.get(document.body).createChild(cfg, position);
2664 //var type = this.el.dom.type;
2667 if(this.tabIndex !== undefined){
2668 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2671 this.dialogEl = this.el.select('.modal-dialog',true).first();
2672 this.bodyEl = this.el.select('.modal-body',true).first();
2673 this.closeEl = this.el.select('.modal-header .close', true).first();
2674 this.headerEl = this.el.select('.modal-header',true).first();
2675 this.titleEl = this.el.select('.modal-title',true).first();
2676 this.footerEl = this.el.select('.modal-footer',true).first();
2678 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680 //this.el.addClass("x-dlg-modal");
2682 if (this.buttons.length) {
2683 Roo.each(this.buttons, function(bb) {
2684 var b = Roo.apply({}, bb);
2685 b.xns = b.xns || Roo.bootstrap;
2686 b.xtype = b.xtype || 'Button';
2687 if (typeof(b.listeners) == 'undefined') {
2688 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2691 var btn = Roo.factory(b);
2693 btn.render(this.el.select('.modal-footer div').first());
2697 // render the children.
2700 if(typeof(this.items) != 'undefined'){
2701 var items = this.items;
2704 for(var i =0;i < items.length;i++) {
2705 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2709 this.items = nitems;
2711 // where are these used - they used to be body/close/footer
2715 //this.el.addClass([this.fieldClass, this.cls]);
2719 getAutoCreate : function(){
2724 html : this.html || ''
2729 cls : 'modal-title',
2733 if(this.specificTitle){
2739 if (this.allow_close) {
2751 if(this.size.length){
2752 size = 'modal-' + this.size;
2759 cls: "modal-dialog " + size,
2762 cls : "modal-content",
2765 cls : 'modal-header',
2770 cls : 'modal-footer',
2774 cls: 'btn-' + this.buttonPosition
2791 modal.cls += ' fade';
2797 getChildContainer : function() {
2802 getButtonContainer : function() {
2803 return this.el.select('.modal-footer div',true).first();
2806 initEvents : function()
2808 if (this.allow_close) {
2809 this.closeEl.on('click', this.hide, this);
2811 Roo.EventManager.onWindowResize(this.resize, this, true);
2818 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2819 if (this.fitwindow) {
2820 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2821 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2826 setSize : function(w,h)
2836 if (!this.rendered) {
2840 //this.el.setStyle('display', 'block');
2841 this.el.removeClass('hideing');
2842 this.el.addClass('show');
2844 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2847 this.el.addClass('in');
2850 this.el.addClass('in');
2854 // not sure how we can show data in here..
2856 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2859 Roo.get(document.body).addClass("x-body-masked");
2861 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2862 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2863 this.maskEl.addClass('show');
2867 this.fireEvent('show', this);
2869 // set zindex here - otherwise it appears to be ignored...
2870 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2873 this.items.forEach( function(e) {
2874 e.layout ? e.layout() : false;
2882 if(this.fireEvent("beforehide", this) !== false){
2883 this.maskEl.removeClass('show');
2884 Roo.get(document.body).removeClass("x-body-masked");
2885 this.el.removeClass('in');
2886 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2888 if(this.animate){ // why
2889 this.el.addClass('hideing');
2891 if (!this.el.hasClass('hideing')) {
2892 return; // it's been shown again...
2894 this.el.removeClass('show');
2895 this.el.removeClass('hideing');
2899 this.el.removeClass('show');
2901 this.fireEvent('hide', this);
2904 isVisible : function()
2907 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2911 addButton : function(str, cb)
2915 var b = Roo.apply({}, { html : str } );
2916 b.xns = b.xns || Roo.bootstrap;
2917 b.xtype = b.xtype || 'Button';
2918 if (typeof(b.listeners) == 'undefined') {
2919 b.listeners = { click : cb.createDelegate(this) };
2922 var btn = Roo.factory(b);
2924 btn.render(this.el.select('.modal-footer div').first());
2930 setDefaultButton : function(btn)
2932 //this.el.select('.modal-footer').()
2936 resizeTo: function(w,h)
2940 this.dialogEl.setWidth(w);
2941 if (this.diff === false) {
2942 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2945 this.bodyEl.setHeight(h-this.diff);
2947 this.fireEvent('resize', this);
2950 setContentSize : function(w, h)
2954 onButtonClick: function(btn,e)
2957 this.fireEvent('btnclick', btn.name, e);
2960 * Set the title of the Dialog
2961 * @param {String} str new Title
2963 setTitle: function(str) {
2964 this.titleEl.dom.innerHTML = str;
2967 * Set the body of the Dialog
2968 * @param {String} str new Title
2970 setBody: function(str) {
2971 this.bodyEl.dom.innerHTML = str;
2974 * Set the body of the Dialog using the template
2975 * @param {Obj} data - apply this data to the template and replace the body contents.
2977 applyBody: function(obj)
2980 Roo.log("Error - using apply Body without a template");
2983 this.tmpl.overwrite(this.bodyEl, obj);
2989 Roo.apply(Roo.bootstrap.Modal, {
2991 * Button config that displays a single OK button
3000 * Button config that displays Yes and No buttons
3016 * Button config that displays OK and Cancel buttons
3031 * Button config that displays Yes, No and Cancel buttons
3055 * messagebox - can be used as a replace
3059 * @class Roo.MessageBox
3060 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3064 Roo.Msg.alert('Status', 'Changes saved successfully.');
3066 // Prompt for user data:
3067 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3069 // process text value...
3073 // Show a dialog using config options:
3075 title:'Save Changes?',
3076 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3077 buttons: Roo.Msg.YESNOCANCEL,
3084 Roo.bootstrap.MessageBox = function(){
3085 var dlg, opt, mask, waitTimer;
3086 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3087 var buttons, activeTextEl, bwidth;
3091 var handleButton = function(button){
3093 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3097 var handleHide = function(){
3099 dlg.el.removeClass(opt.cls);
3102 // Roo.TaskMgr.stop(waitTimer);
3103 // waitTimer = null;
3108 var updateButtons = function(b){
3111 buttons["ok"].hide();
3112 buttons["cancel"].hide();
3113 buttons["yes"].hide();
3114 buttons["no"].hide();
3115 //dlg.footer.dom.style.display = 'none';
3118 dlg.footerEl.dom.style.display = '';
3119 for(var k in buttons){
3120 if(typeof buttons[k] != "function"){
3123 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3124 width += buttons[k].el.getWidth()+15;
3134 var handleEsc = function(d, k, e){
3135 if(opt && opt.closable !== false){
3145 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3146 * @return {Roo.BasicDialog} The BasicDialog element
3148 getDialog : function(){
3150 dlg = new Roo.bootstrap.Modal( {
3153 //constraintoviewport:false,
3155 //collapsible : false,
3160 //buttonAlign:"center",
3161 closeClick : function(){
3162 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3165 handleButton("cancel");
3170 dlg.on("hide", handleHide);
3172 //dlg.addKeyListener(27, handleEsc);
3174 this.buttons = buttons;
3175 var bt = this.buttonText;
3176 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3177 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3178 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3179 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3181 bodyEl = dlg.bodyEl.createChild({
3183 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3184 '<textarea class="roo-mb-textarea"></textarea>' +
3185 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3187 msgEl = bodyEl.dom.firstChild;
3188 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3189 textboxEl.enableDisplayMode();
3190 textboxEl.addKeyListener([10,13], function(){
3191 if(dlg.isVisible() && opt && opt.buttons){
3194 }else if(opt.buttons.yes){
3195 handleButton("yes");
3199 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3200 textareaEl.enableDisplayMode();
3201 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3202 progressEl.enableDisplayMode();
3204 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3205 var pf = progressEl.dom.firstChild;
3207 pp = Roo.get(pf.firstChild);
3208 pp.setHeight(pf.offsetHeight);
3216 * Updates the message box body text
3217 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3218 * the XHTML-compliant non-breaking space character '&#160;')
3219 * @return {Roo.MessageBox} This message box
3221 updateText : function(text)
3223 if(!dlg.isVisible() && !opt.width){
3224 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3225 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3227 msgEl.innerHTML = text || ' ';
3229 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3230 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3232 Math.min(opt.width || cw , this.maxWidth),
3233 Math.max(opt.minWidth || this.minWidth, bwidth)
3236 activeTextEl.setWidth(w);
3238 if(dlg.isVisible()){
3239 dlg.fixedcenter = false;
3241 // to big, make it scroll. = But as usual stupid IE does not support
3244 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3245 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3246 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3248 bodyEl.dom.style.height = '';
3249 bodyEl.dom.style.overflowY = '';
3252 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3254 bodyEl.dom.style.overflowX = '';
3257 dlg.setContentSize(w, bodyEl.getHeight());
3258 if(dlg.isVisible()){
3259 dlg.fixedcenter = true;
3265 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3266 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3267 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3268 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3269 * @return {Roo.MessageBox} This message box
3271 updateProgress : function(value, text){
3273 this.updateText(text);
3276 if (pp) { // weird bug on my firefox - for some reason this is not defined
3277 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3278 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3284 * Returns true if the message box is currently displayed
3285 * @return {Boolean} True if the message box is visible, else false
3287 isVisible : function(){
3288 return dlg && dlg.isVisible();
3292 * Hides the message box if it is displayed
3295 if(this.isVisible()){
3301 * Displays a new message box, or reinitializes an existing message box, based on the config options
3302 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3303 * The following config object properties are supported:
3305 Property Type Description
3306 ---------- --------------- ------------------------------------------------------------------------------------
3307 animEl String/Element An id or Element from which the message box should animate as it opens and
3308 closes (defaults to undefined)
3309 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3310 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3311 closable Boolean False to hide the top-right close button (defaults to true). Note that
3312 progress and wait dialogs will ignore this property and always hide the
3313 close button as they can only be closed programmatically.
3314 cls String A custom CSS class to apply to the message box element
3315 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3316 displayed (defaults to 75)
3317 fn Function A callback function to execute after closing the dialog. The arguments to the
3318 function will be btn (the name of the button that was clicked, if applicable,
3319 e.g. "ok"), and text (the value of the active text field, if applicable).
3320 Progress and wait dialogs will ignore this option since they do not respond to
3321 user actions and can only be closed programmatically, so any required function
3322 should be called by the same code after it closes the dialog.
3323 icon String A CSS class that provides a background image to be used as an icon for
3324 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3325 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3326 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3327 modal Boolean False to allow user interaction with the page while the message box is
3328 displayed (defaults to true)
3329 msg String A string that will replace the existing message box body text (defaults
3330 to the XHTML-compliant non-breaking space character ' ')
3331 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3332 progress Boolean True to display a progress bar (defaults to false)
3333 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3334 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3335 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3336 title String The title text
3337 value String The string value to set into the active textbox element if displayed
3338 wait Boolean True to display a progress bar (defaults to false)
3339 width Number The width of the dialog in pixels
3346 msg: 'Please enter your address:',
3348 buttons: Roo.MessageBox.OKCANCEL,
3351 animEl: 'addAddressBtn'
3354 * @param {Object} config Configuration options
3355 * @return {Roo.MessageBox} This message box
3357 show : function(options)
3360 // this causes nightmares if you show one dialog after another
3361 // especially on callbacks..
3363 if(this.isVisible()){
3366 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3367 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3368 Roo.log("New Dialog Message:" + options.msg )
3369 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3370 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3373 var d = this.getDialog();
3375 d.setTitle(opt.title || " ");
3376 d.closeEl.setDisplayed(opt.closable !== false);
3377 activeTextEl = textboxEl;
3378 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3383 textareaEl.setHeight(typeof opt.multiline == "number" ?
3384 opt.multiline : this.defaultTextHeight);
3385 activeTextEl = textareaEl;
3394 progressEl.setDisplayed(opt.progress === true);
3395 this.updateProgress(0);
3396 activeTextEl.dom.value = opt.value || "";
3398 dlg.setDefaultButton(activeTextEl);
3400 var bs = opt.buttons;
3404 }else if(bs && bs.yes){
3405 db = buttons["yes"];
3407 dlg.setDefaultButton(db);
3409 bwidth = updateButtons(opt.buttons);
3410 this.updateText(opt.msg);
3412 d.el.addClass(opt.cls);
3414 d.proxyDrag = opt.proxyDrag === true;
3415 d.modal = opt.modal !== false;
3416 d.mask = opt.modal !== false ? mask : false;
3418 // force it to the end of the z-index stack so it gets a cursor in FF
3419 document.body.appendChild(dlg.el.dom);
3420 d.animateTarget = null;
3421 d.show(options.animEl);
3427 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3428 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3429 * and closing the message box when the process is complete.
3430 * @param {String} title The title bar text
3431 * @param {String} msg The message box body text
3432 * @return {Roo.MessageBox} This message box
3434 progress : function(title, msg){
3441 minWidth: this.minProgressWidth,
3448 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3449 * If a callback function is passed it will be called after the user clicks the button, and the
3450 * id of the button that was clicked will be passed as the only parameter to the callback
3451 * (could also be the top-right close button).
3452 * @param {String} title The title bar text
3453 * @param {String} msg The message box body text
3454 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3455 * @param {Object} scope (optional) The scope of the callback function
3456 * @return {Roo.MessageBox} This message box
3458 alert : function(title, msg, fn, scope)
3473 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3474 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3475 * You are responsible for closing the message box when the process is complete.
3476 * @param {String} msg The message box body text
3477 * @param {String} title (optional) The title bar text
3478 * @return {Roo.MessageBox} This message box
3480 wait : function(msg, title){
3491 waitTimer = Roo.TaskMgr.start({
3493 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3501 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3502 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3503 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3504 * @param {String} title The title bar text
3505 * @param {String} msg The message box body text
3506 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3507 * @param {Object} scope (optional) The scope of the callback function
3508 * @return {Roo.MessageBox} This message box
3510 confirm : function(title, msg, fn, scope){
3514 buttons: this.YESNO,
3523 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3524 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3525 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3526 * (could also be the top-right close button) and the text that was entered will be passed as the two
3527 * parameters to the callback.
3528 * @param {String} title The title bar text
3529 * @param {String} msg The message box body text
3530 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3531 * @param {Object} scope (optional) The scope of the callback function
3532 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3533 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3534 * @return {Roo.MessageBox} This message box
3536 prompt : function(title, msg, fn, scope, multiline){
3540 buttons: this.OKCANCEL,
3545 multiline: multiline,
3552 * Button config that displays a single OK button
3557 * Button config that displays Yes and No buttons
3560 YESNO : {yes:true, no:true},
3562 * Button config that displays OK and Cancel buttons
3565 OKCANCEL : {ok:true, cancel:true},
3567 * Button config that displays Yes, No and Cancel buttons
3570 YESNOCANCEL : {yes:true, no:true, cancel:true},
3573 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3576 defaultTextHeight : 75,
3578 * The maximum width in pixels of the message box (defaults to 600)
3583 * The minimum width in pixels of the message box (defaults to 100)
3588 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3589 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3592 minProgressWidth : 250,
3594 * An object containing the default button text strings that can be overriden for localized language support.
3595 * Supported properties are: ok, cancel, yes and no.
3596 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3609 * Shorthand for {@link Roo.MessageBox}
3611 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3612 Roo.Msg = Roo.Msg || Roo.MessageBox;
3621 * @class Roo.bootstrap.Navbar
3622 * @extends Roo.bootstrap.Component
3623 * Bootstrap Navbar class
3626 * Create a new Navbar
3627 * @param {Object} config The config object
3631 Roo.bootstrap.Navbar = function(config){
3632 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3636 * @event beforetoggle
3637 * Fire before toggle the menu
3638 * @param {Roo.EventObject} e
3640 "beforetoggle" : true
3644 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3653 getAutoCreate : function(){
3656 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3660 initEvents :function ()
3662 //Roo.log(this.el.select('.navbar-toggle',true));
3663 this.el.select('.navbar-toggle',true).on('click', function() {
3664 if(this.fireEvent('beforetoggle', this) !== false){
3665 this.el.select('.navbar-collapse',true).toggleClass('in');
3675 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3677 var size = this.el.getSize();
3678 this.maskEl.setSize(size.width, size.height);
3679 this.maskEl.enableDisplayMode("block");
3688 getChildContainer : function()
3690 if (this.el.select('.collapse').getCount()) {
3691 return this.el.select('.collapse',true).first();
3724 * @class Roo.bootstrap.NavSimplebar
3725 * @extends Roo.bootstrap.Navbar
3726 * Bootstrap Sidebar class
3728 * @cfg {Boolean} inverse is inverted color
3730 * @cfg {String} type (nav | pills | tabs)
3731 * @cfg {Boolean} arrangement stacked | justified
3732 * @cfg {String} align (left | right) alignment
3734 * @cfg {Boolean} main (true|false) main nav bar? default false
3735 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3737 * @cfg {String} tag (header|footer|nav|div) default is nav
3743 * Create a new Sidebar
3744 * @param {Object} config The config object
3748 Roo.bootstrap.NavSimplebar = function(config){
3749 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3752 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3768 getAutoCreate : function(){
3772 tag : this.tag || 'div',
3785 this.type = this.type || 'nav';
3786 if (['tabs','pills'].indexOf(this.type)!==-1) {
3787 cfg.cn[0].cls += ' nav-' + this.type
3791 if (this.type!=='nav') {
3792 Roo.log('nav type must be nav/tabs/pills')
3794 cfg.cn[0].cls += ' navbar-nav'
3800 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3801 cfg.cn[0].cls += ' nav-' + this.arrangement;
3805 if (this.align === 'right') {
3806 cfg.cn[0].cls += ' navbar-right';
3810 cfg.cls += ' navbar-inverse';
3837 * @class Roo.bootstrap.NavHeaderbar
3838 * @extends Roo.bootstrap.NavSimplebar
3839 * Bootstrap Sidebar class
3841 * @cfg {String} brand what is brand
3842 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3843 * @cfg {String} brand_href href of the brand
3844 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3845 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3846 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3847 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3850 * Create a new Sidebar
3851 * @param {Object} config The config object
3855 Roo.bootstrap.NavHeaderbar = function(config){
3856 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3860 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3867 desktopCenter : false,
3870 getAutoCreate : function(){
3873 tag: this.nav || 'nav',
3880 if (this.desktopCenter) {
3881 cn.push({cls : 'container', cn : []});
3888 cls: 'navbar-header',
3893 cls: 'navbar-toggle',
3894 'data-toggle': 'collapse',
3899 html: 'Toggle navigation'
3921 cls: 'collapse navbar-collapse',
3925 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3927 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3928 cfg.cls += ' navbar-' + this.position;
3930 // tag can override this..
3932 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3935 if (this.brand !== '') {
3938 href: this.brand_href ? this.brand_href : '#',
3939 cls: 'navbar-brand',
3947 cfg.cls += ' main-nav';
3955 getHeaderChildContainer : function()
3957 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3958 return this.el.select('.navbar-header',true).first();
3961 return this.getChildContainer();
3965 initEvents : function()
3967 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3969 if (this.autohide) {
3974 Roo.get(document).on('scroll',function(e) {
3975 var ns = Roo.get(document).getScroll().top;
3976 var os = prevScroll;
3980 ft.removeClass('slideDown');
3981 ft.addClass('slideUp');
3984 ft.removeClass('slideUp');
3985 ft.addClass('slideDown');
4006 * @class Roo.bootstrap.NavSidebar
4007 * @extends Roo.bootstrap.Navbar
4008 * Bootstrap Sidebar class
4011 * Create a new Sidebar
4012 * @param {Object} config The config object
4016 Roo.bootstrap.NavSidebar = function(config){
4017 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4020 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4022 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4024 getAutoCreate : function(){
4029 cls: 'sidebar sidebar-nav'
4051 * @class Roo.bootstrap.NavGroup
4052 * @extends Roo.bootstrap.Component
4053 * Bootstrap NavGroup class
4054 * @cfg {String} align (left|right)
4055 * @cfg {Boolean} inverse
4056 * @cfg {String} type (nav|pills|tab) default nav
4057 * @cfg {String} navId - reference Id for navbar.
4061 * Create a new nav group
4062 * @param {Object} config The config object
4065 Roo.bootstrap.NavGroup = function(config){
4066 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4069 Roo.bootstrap.NavGroup.register(this);
4073 * Fires when the active item changes
4074 * @param {Roo.bootstrap.NavGroup} this
4075 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4076 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4083 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4094 getAutoCreate : function()
4096 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4103 if (['tabs','pills'].indexOf(this.type)!==-1) {
4104 cfg.cls += ' nav-' + this.type
4106 if (this.type!=='nav') {
4107 Roo.log('nav type must be nav/tabs/pills')
4109 cfg.cls += ' navbar-nav'
4112 if (this.parent() && this.parent().sidebar) {
4115 cls: 'dashboard-menu sidebar-menu'
4121 if (this.form === true) {
4127 if (this.align === 'right') {
4128 cfg.cls += ' navbar-right';
4130 cfg.cls += ' navbar-left';
4134 if (this.align === 'right') {
4135 cfg.cls += ' navbar-right';
4139 cfg.cls += ' navbar-inverse';
4147 * sets the active Navigation item
4148 * @param {Roo.bootstrap.NavItem} the new current navitem
4150 setActiveItem : function(item)
4153 Roo.each(this.navItems, function(v){
4158 v.setActive(false, true);
4165 item.setActive(true, true);
4166 this.fireEvent('changed', this, item, prev);
4171 * gets the active Navigation item
4172 * @return {Roo.bootstrap.NavItem} the current navitem
4174 getActive : function()
4178 Roo.each(this.navItems, function(v){
4189 indexOfNav : function()
4193 Roo.each(this.navItems, function(v,i){
4204 * adds a Navigation item
4205 * @param {Roo.bootstrap.NavItem} the navitem to add
4207 addItem : function(cfg)
4209 var cn = new Roo.bootstrap.NavItem(cfg);
4211 cn.parentId = this.id;
4212 cn.onRender(this.el, null);
4216 * register a Navigation item
4217 * @param {Roo.bootstrap.NavItem} the navitem to add
4219 register : function(item)
4221 this.navItems.push( item);
4222 item.navId = this.navId;
4227 * clear all the Navigation item
4230 clearAll : function()
4233 this.el.dom.innerHTML = '';
4236 getNavItem: function(tabId)
4239 Roo.each(this.navItems, function(e) {
4240 if (e.tabId == tabId) {
4250 setActiveNext : function()
4252 var i = this.indexOfNav(this.getActive());
4253 if (i > this.navItems.length) {
4256 this.setActiveItem(this.navItems[i+1]);
4258 setActivePrev : function()
4260 var i = this.indexOfNav(this.getActive());
4264 this.setActiveItem(this.navItems[i-1]);
4266 clearWasActive : function(except) {
4267 Roo.each(this.navItems, function(e) {
4268 if (e.tabId != except.tabId && e.was_active) {
4269 e.was_active = false;
4276 getWasActive : function ()
4279 Roo.each(this.navItems, function(e) {
4294 Roo.apply(Roo.bootstrap.NavGroup, {
4298 * register a Navigation Group
4299 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4301 register : function(navgrp)
4303 this.groups[navgrp.navId] = navgrp;
4307 * fetch a Navigation Group based on the navigation ID
4308 * @param {string} the navgroup to add
4309 * @returns {Roo.bootstrap.NavGroup} the navgroup
4311 get: function(navId) {
4312 if (typeof(this.groups[navId]) == 'undefined') {
4314 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4316 return this.groups[navId] ;
4331 * @class Roo.bootstrap.NavItem
4332 * @extends Roo.bootstrap.Component
4333 * Bootstrap Navbar.NavItem class
4334 * @cfg {String} href link to
4335 * @cfg {String} html content of button
4336 * @cfg {String} badge text inside badge
4337 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4338 * @cfg {String} glyphicon name of glyphicon
4339 * @cfg {String} icon name of font awesome icon
4340 * @cfg {Boolean} active Is item active
4341 * @cfg {Boolean} disabled Is item disabled
4343 * @cfg {Boolean} preventDefault (true | false) default false
4344 * @cfg {String} tabId the tab that this item activates.
4345 * @cfg {String} tagtype (a|span) render as a href or span?
4346 * @cfg {Boolean} animateRef (true|false) link to element default false
4349 * Create a new Navbar Item
4350 * @param {Object} config The config object
4352 Roo.bootstrap.NavItem = function(config){
4353 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4358 * The raw click event for the entire grid.
4359 * @param {Roo.EventObject} e
4364 * Fires when the active item active state changes
4365 * @param {Roo.bootstrap.NavItem} this
4366 * @param {boolean} state the new state
4372 * Fires when scroll to element
4373 * @param {Roo.bootstrap.NavItem} this
4374 * @param {Object} options
4375 * @param {Roo.EventObject} e
4383 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4391 preventDefault : false,
4398 getAutoCreate : function(){
4407 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4409 if (this.disabled) {
4410 cfg.cls += ' disabled';
4413 if (this.href || this.html || this.glyphicon || this.icon) {
4417 href : this.href || "#",
4418 html: this.html || ''
4423 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4426 if(this.glyphicon) {
4427 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4432 cfg.cn[0].html += " <span class='caret'></span>";
4436 if (this.badge !== '') {
4438 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4446 initEvents: function()
4448 if (typeof (this.menu) != 'undefined') {
4449 this.menu.parentType = this.xtype;
4450 this.menu.triggerEl = this.el;
4451 this.menu = this.addxtype(Roo.apply({}, this.menu));
4454 this.el.select('a',true).on('click', this.onClick, this);
4456 if(this.tagtype == 'span'){
4457 this.el.select('span',true).on('click', this.onClick, this);
4460 // at this point parent should be available..
4461 this.parent().register(this);
4464 onClick : function(e)
4466 if (e.getTarget('.dropdown-menu-item')) {
4467 // did you click on a menu itemm.... - then don't trigger onclick..
4472 this.preventDefault ||
4475 Roo.log("NavItem - prevent Default?");
4479 if (this.disabled) {
4483 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4484 if (tg && tg.transition) {
4485 Roo.log("waiting for the transitionend");
4491 //Roo.log("fire event clicked");
4492 if(this.fireEvent('click', this, e) === false){
4496 if(this.tagtype == 'span'){
4500 //Roo.log(this.href);
4501 var ael = this.el.select('a',true).first();
4504 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4505 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4506 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4507 return; // ignore... - it's a 'hash' to another page.
4509 Roo.log("NavItem - prevent Default?");
4511 this.scrollToElement(e);
4515 var p = this.parent();
4517 if (['tabs','pills'].indexOf(p.type)!==-1) {
4518 if (typeof(p.setActiveItem) !== 'undefined') {
4519 p.setActiveItem(this);
4523 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4524 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4525 // remove the collapsed menu expand...
4526 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4530 isActive: function () {
4533 setActive : function(state, fire, is_was_active)
4535 if (this.active && !state && this.navId) {
4536 this.was_active = true;
4537 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4539 nv.clearWasActive(this);
4543 this.active = state;
4546 this.el.removeClass('active');
4547 } else if (!this.el.hasClass('active')) {
4548 this.el.addClass('active');
4551 this.fireEvent('changed', this, state);
4554 // show a panel if it's registered and related..
4556 if (!this.navId || !this.tabId || !state || is_was_active) {
4560 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4564 var pan = tg.getPanelByName(this.tabId);
4568 // if we can not flip to new panel - go back to old nav highlight..
4569 if (false == tg.showPanel(pan)) {
4570 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4572 var onav = nv.getWasActive();
4574 onav.setActive(true, false, true);
4583 // this should not be here...
4584 setDisabled : function(state)
4586 this.disabled = state;
4588 this.el.removeClass('disabled');
4589 } else if (!this.el.hasClass('disabled')) {
4590 this.el.addClass('disabled');
4596 * Fetch the element to display the tooltip on.
4597 * @return {Roo.Element} defaults to this.el
4599 tooltipEl : function()
4601 return this.el.select('' + this.tagtype + '', true).first();
4604 scrollToElement : function(e)
4606 var c = document.body;
4609 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4611 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4612 c = document.documentElement;
4615 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4621 var o = target.calcOffsetsTo(c);
4628 this.fireEvent('scrollto', this, options, e);
4630 Roo.get(c).scrollTo('top', options.value, true);
4643 * <span> icon </span>
4644 * <span> text </span>
4645 * <span>badge </span>
4649 * @class Roo.bootstrap.NavSidebarItem
4650 * @extends Roo.bootstrap.NavItem
4651 * Bootstrap Navbar.NavSidebarItem class
4652 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4653 * {Boolean} open is the menu open
4654 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4655 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4656 * {String} buttonSize (sm|md|lg)the extra classes for the button
4657 * {Boolean} showArrow show arrow next to the text (default true)
4659 * Create a new Navbar Button
4660 * @param {Object} config The config object
4662 Roo.bootstrap.NavSidebarItem = function(config){
4663 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4668 * The raw click event for the entire grid.
4669 * @param {Roo.EventObject} e
4674 * Fires when the active item active state changes
4675 * @param {Roo.bootstrap.NavSidebarItem} this
4676 * @param {boolean} state the new state
4684 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4686 badgeWeight : 'default',
4692 buttonWeight : 'default',
4698 getAutoCreate : function(){
4703 href : this.href || '#',
4709 if(this.buttonView){
4712 href : this.href || '#',
4713 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4726 cfg.cls += ' active';
4729 if (this.disabled) {
4730 cfg.cls += ' disabled';
4733 cfg.cls += ' open x-open';
4736 if (this.glyphicon || this.icon) {
4737 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4738 a.cn.push({ tag : 'i', cls : c }) ;
4741 if(!this.buttonView){
4744 html : this.html || ''
4751 if (this.badge !== '') {
4752 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4758 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4761 a.cls += ' dropdown-toggle treeview' ;
4767 initEvents : function()
4769 if (typeof (this.menu) != 'undefined') {
4770 this.menu.parentType = this.xtype;
4771 this.menu.triggerEl = this.el;
4772 this.menu = this.addxtype(Roo.apply({}, this.menu));
4775 this.el.on('click', this.onClick, this);
4777 if(this.badge !== ''){
4778 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4783 onClick : function(e)
4790 if(this.preventDefault){
4794 this.fireEvent('click', this);
4797 disable : function()
4799 this.setDisabled(true);
4804 this.setDisabled(false);
4807 setDisabled : function(state)
4809 if(this.disabled == state){
4813 this.disabled = state;
4816 this.el.addClass('disabled');
4820 this.el.removeClass('disabled');
4825 setActive : function(state)
4827 if(this.active == state){
4831 this.active = state;
4834 this.el.addClass('active');
4838 this.el.removeClass('active');
4843 isActive: function ()
4848 setBadge : function(str)
4854 this.badgeEl.dom.innerHTML = str;
4871 * @class Roo.bootstrap.Row
4872 * @extends Roo.bootstrap.Component
4873 * Bootstrap Row class (contains columns...)
4877 * @param {Object} config The config object
4880 Roo.bootstrap.Row = function(config){
4881 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4884 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4886 getAutoCreate : function(){
4905 * @class Roo.bootstrap.Element
4906 * @extends Roo.bootstrap.Component
4907 * Bootstrap Element class
4908 * @cfg {String} html contents of the element
4909 * @cfg {String} tag tag of the element
4910 * @cfg {String} cls class of the element
4911 * @cfg {Boolean} preventDefault (true|false) default false
4912 * @cfg {Boolean} clickable (true|false) default false
4915 * Create a new Element
4916 * @param {Object} config The config object
4919 Roo.bootstrap.Element = function(config){
4920 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4926 * When a element is chick
4927 * @param {Roo.bootstrap.Element} this
4928 * @param {Roo.EventObject} e
4934 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4939 preventDefault: false,
4942 getAutoCreate : function(){
4946 // cls: this.cls, double assign in parent class Component.js :: onRender
4953 initEvents: function()
4955 Roo.bootstrap.Element.superclass.initEvents.call(this);
4958 this.el.on('click', this.onClick, this);
4963 onClick : function(e)
4965 if(this.preventDefault){
4969 this.fireEvent('click', this, e);
4972 getValue : function()
4974 return this.el.dom.innerHTML;
4977 setValue : function(value)
4979 this.el.dom.innerHTML = value;
4994 * @class Roo.bootstrap.Pagination
4995 * @extends Roo.bootstrap.Component
4996 * Bootstrap Pagination class
4997 * @cfg {String} size xs | sm | md | lg
4998 * @cfg {Boolean} inverse false | true
5001 * Create a new Pagination
5002 * @param {Object} config The config object
5005 Roo.bootstrap.Pagination = function(config){
5006 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5009 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5015 getAutoCreate : function(){
5021 cfg.cls += ' inverse';
5027 cfg.cls += " " + this.cls;
5045 * @class Roo.bootstrap.PaginationItem
5046 * @extends Roo.bootstrap.Component
5047 * Bootstrap PaginationItem class
5048 * @cfg {String} html text
5049 * @cfg {String} href the link
5050 * @cfg {Boolean} preventDefault (true | false) default true
5051 * @cfg {Boolean} active (true | false) default false
5052 * @cfg {Boolean} disabled default false
5056 * Create a new PaginationItem
5057 * @param {Object} config The config object
5061 Roo.bootstrap.PaginationItem = function(config){
5062 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5067 * The raw click event for the entire grid.
5068 * @param {Roo.EventObject} e
5074 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5078 preventDefault: true,
5083 getAutoCreate : function(){
5089 href : this.href ? this.href : '#',
5090 html : this.html ? this.html : ''
5100 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5104 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5110 initEvents: function() {
5112 this.el.on('click', this.onClick, this);
5115 onClick : function(e)
5117 Roo.log('PaginationItem on click ');
5118 if(this.preventDefault){
5126 this.fireEvent('click', this, e);
5142 * @class Roo.bootstrap.Slider
5143 * @extends Roo.bootstrap.Component
5144 * Bootstrap Slider class
5147 * Create a new Slider
5148 * @param {Object} config The config object
5151 Roo.bootstrap.Slider = function(config){
5152 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5155 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5157 getAutoCreate : function(){
5161 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5165 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5177 * Ext JS Library 1.1.1
5178 * Copyright(c) 2006-2007, Ext JS, LLC.
5180 * Originally Released Under LGPL - original licence link has changed is not relivant.
5183 * <script type="text/javascript">
5188 * @class Roo.grid.ColumnModel
5189 * @extends Roo.util.Observable
5190 * This is the default implementation of a ColumnModel used by the Grid. It defines
5191 * the columns in the grid.
5194 var colModel = new Roo.grid.ColumnModel([
5195 {header: "Ticker", width: 60, sortable: true, locked: true},
5196 {header: "Company Name", width: 150, sortable: true},
5197 {header: "Market Cap.", width: 100, sortable: true},
5198 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5199 {header: "Employees", width: 100, sortable: true, resizable: false}
5204 * The config options listed for this class are options which may appear in each
5205 * individual column definition.
5206 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5208 * @param {Object} config An Array of column config objects. See this class's
5209 * config objects for details.
5211 Roo.grid.ColumnModel = function(config){
5213 * The config passed into the constructor
5215 this.config = config;
5218 // if no id, create one
5219 // if the column does not have a dataIndex mapping,
5220 // map it to the order it is in the config
5221 for(var i = 0, len = config.length; i < len; i++){
5223 if(typeof c.dataIndex == "undefined"){
5226 if(typeof c.renderer == "string"){
5227 c.renderer = Roo.util.Format[c.renderer];
5229 if(typeof c.id == "undefined"){
5232 if(c.editor && c.editor.xtype){
5233 c.editor = Roo.factory(c.editor, Roo.grid);
5235 if(c.editor && c.editor.isFormField){
5236 c.editor = new Roo.grid.GridEditor(c.editor);
5238 this.lookup[c.id] = c;
5242 * The width of columns which have no width specified (defaults to 100)
5245 this.defaultWidth = 100;
5248 * Default sortable of columns which have no sortable specified (defaults to false)
5251 this.defaultSortable = false;
5255 * @event widthchange
5256 * Fires when the width of a column changes.
5257 * @param {ColumnModel} this
5258 * @param {Number} columnIndex The column index
5259 * @param {Number} newWidth The new width
5261 "widthchange": true,
5263 * @event headerchange
5264 * Fires when the text of a header changes.
5265 * @param {ColumnModel} this
5266 * @param {Number} columnIndex The column index
5267 * @param {Number} newText The new header text
5269 "headerchange": true,
5271 * @event hiddenchange
5272 * Fires when a column is hidden or "unhidden".
5273 * @param {ColumnModel} this
5274 * @param {Number} columnIndex The column index
5275 * @param {Boolean} hidden true if hidden, false otherwise
5277 "hiddenchange": true,
5279 * @event columnmoved
5280 * Fires when a column is moved.
5281 * @param {ColumnModel} this
5282 * @param {Number} oldIndex
5283 * @param {Number} newIndex
5285 "columnmoved" : true,
5287 * @event columlockchange
5288 * Fires when a column's locked state is changed
5289 * @param {ColumnModel} this
5290 * @param {Number} colIndex
5291 * @param {Boolean} locked true if locked
5293 "columnlockchange" : true
5295 Roo.grid.ColumnModel.superclass.constructor.call(this);
5297 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5299 * @cfg {String} header The header text to display in the Grid view.
5302 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5303 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5304 * specified, the column's index is used as an index into the Record's data Array.
5307 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5308 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5311 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5312 * Defaults to the value of the {@link #defaultSortable} property.
5313 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5316 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5319 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5322 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5325 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5328 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5329 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5330 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5331 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5334 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5337 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5340 * @cfg {String} cursor (Optional)
5343 * @cfg {String} tooltip (Optional)
5346 * @cfg {Number} xs (Optional)
5349 * @cfg {Number} sm (Optional)
5352 * @cfg {Number} md (Optional)
5355 * @cfg {Number} lg (Optional)
5358 * Returns the id of the column at the specified index.
5359 * @param {Number} index The column index
5360 * @return {String} the id
5362 getColumnId : function(index){
5363 return this.config[index].id;
5367 * Returns the column for a specified id.
5368 * @param {String} id The column id
5369 * @return {Object} the column
5371 getColumnById : function(id){
5372 return this.lookup[id];
5377 * Returns the column for a specified dataIndex.
5378 * @param {String} dataIndex The column dataIndex
5379 * @return {Object|Boolean} the column or false if not found
5381 getColumnByDataIndex: function(dataIndex){
5382 var index = this.findColumnIndex(dataIndex);
5383 return index > -1 ? this.config[index] : false;
5387 * Returns the index for a specified column id.
5388 * @param {String} id The column id
5389 * @return {Number} the index, or -1 if not found
5391 getIndexById : function(id){
5392 for(var i = 0, len = this.config.length; i < len; i++){
5393 if(this.config[i].id == id){
5401 * Returns the index for a specified column dataIndex.
5402 * @param {String} dataIndex The column dataIndex
5403 * @return {Number} the index, or -1 if not found
5406 findColumnIndex : function(dataIndex){
5407 for(var i = 0, len = this.config.length; i < len; i++){
5408 if(this.config[i].dataIndex == dataIndex){
5416 moveColumn : function(oldIndex, newIndex){
5417 var c = this.config[oldIndex];
5418 this.config.splice(oldIndex, 1);
5419 this.config.splice(newIndex, 0, c);
5420 this.dataMap = null;
5421 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5424 isLocked : function(colIndex){
5425 return this.config[colIndex].locked === true;
5428 setLocked : function(colIndex, value, suppressEvent){
5429 if(this.isLocked(colIndex) == value){
5432 this.config[colIndex].locked = value;
5434 this.fireEvent("columnlockchange", this, colIndex, value);
5438 getTotalLockedWidth : function(){
5440 for(var i = 0; i < this.config.length; i++){
5441 if(this.isLocked(i) && !this.isHidden(i)){
5442 this.totalWidth += this.getColumnWidth(i);
5448 getLockedCount : function(){
5449 for(var i = 0, len = this.config.length; i < len; i++){
5450 if(!this.isLocked(i)){
5455 return this.config.length;
5459 * Returns the number of columns.
5462 getColumnCount : function(visibleOnly){
5463 if(visibleOnly === true){
5465 for(var i = 0, len = this.config.length; i < len; i++){
5466 if(!this.isHidden(i)){
5472 return this.config.length;
5476 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5477 * @param {Function} fn
5478 * @param {Object} scope (optional)
5479 * @return {Array} result
5481 getColumnsBy : function(fn, scope){
5483 for(var i = 0, len = this.config.length; i < len; i++){
5484 var c = this.config[i];
5485 if(fn.call(scope||this, c, i) === true){
5493 * Returns true if the specified column is sortable.
5494 * @param {Number} col The column index
5497 isSortable : function(col){
5498 if(typeof this.config[col].sortable == "undefined"){
5499 return this.defaultSortable;
5501 return this.config[col].sortable;
5505 * Returns the rendering (formatting) function defined for the column.
5506 * @param {Number} col The column index.
5507 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5509 getRenderer : function(col){
5510 if(!this.config[col].renderer){
5511 return Roo.grid.ColumnModel.defaultRenderer;
5513 return this.config[col].renderer;
5517 * Sets the rendering (formatting) function for a column.
5518 * @param {Number} col The column index
5519 * @param {Function} fn The function to use to process the cell's raw data
5520 * to return HTML markup for the grid view. The render function is called with
5521 * the following parameters:<ul>
5522 * <li>Data value.</li>
5523 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5524 * <li>css A CSS style string to apply to the table cell.</li>
5525 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5526 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5527 * <li>Row index</li>
5528 * <li>Column index</li>
5529 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5531 setRenderer : function(col, fn){
5532 this.config[col].renderer = fn;
5536 * Returns the width for the specified column.
5537 * @param {Number} col The column index
5540 getColumnWidth : function(col){
5541 return this.config[col].width * 1 || this.defaultWidth;
5545 * Sets the width for a column.
5546 * @param {Number} col The column index
5547 * @param {Number} width The new width
5549 setColumnWidth : function(col, width, suppressEvent){
5550 this.config[col].width = width;
5551 this.totalWidth = null;
5553 this.fireEvent("widthchange", this, col, width);
5558 * Returns the total width of all columns.
5559 * @param {Boolean} includeHidden True to include hidden column widths
5562 getTotalWidth : function(includeHidden){
5563 if(!this.totalWidth){
5564 this.totalWidth = 0;
5565 for(var i = 0, len = this.config.length; i < len; i++){
5566 if(includeHidden || !this.isHidden(i)){
5567 this.totalWidth += this.getColumnWidth(i);
5571 return this.totalWidth;
5575 * Returns the header for the specified column.
5576 * @param {Number} col The column index
5579 getColumnHeader : function(col){
5580 return this.config[col].header;
5584 * Sets the header for a column.
5585 * @param {Number} col The column index
5586 * @param {String} header The new header
5588 setColumnHeader : function(col, header){
5589 this.config[col].header = header;
5590 this.fireEvent("headerchange", this, col, header);
5594 * Returns the tooltip for the specified column.
5595 * @param {Number} col The column index
5598 getColumnTooltip : function(col){
5599 return this.config[col].tooltip;
5602 * Sets the tooltip for a column.
5603 * @param {Number} col The column index
5604 * @param {String} tooltip The new tooltip
5606 setColumnTooltip : function(col, tooltip){
5607 this.config[col].tooltip = tooltip;
5611 * Returns the dataIndex for the specified column.
5612 * @param {Number} col The column index
5615 getDataIndex : function(col){
5616 return this.config[col].dataIndex;
5620 * Sets the dataIndex for a column.
5621 * @param {Number} col The column index
5622 * @param {Number} dataIndex The new dataIndex
5624 setDataIndex : function(col, dataIndex){
5625 this.config[col].dataIndex = dataIndex;
5631 * Returns true if the cell is editable.
5632 * @param {Number} colIndex The column index
5633 * @param {Number} rowIndex The row index - this is nto actually used..?
5636 isCellEditable : function(colIndex, rowIndex){
5637 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5641 * Returns the editor defined for the cell/column.
5642 * return false or null to disable editing.
5643 * @param {Number} colIndex The column index
5644 * @param {Number} rowIndex The row index
5647 getCellEditor : function(colIndex, rowIndex){
5648 return this.config[colIndex].editor;
5652 * Sets if a column is editable.
5653 * @param {Number} col The column index
5654 * @param {Boolean} editable True if the column is editable
5656 setEditable : function(col, editable){
5657 this.config[col].editable = editable;
5662 * Returns true if the column is hidden.
5663 * @param {Number} colIndex The column index
5666 isHidden : function(colIndex){
5667 return this.config[colIndex].hidden;
5672 * Returns true if the column width cannot be changed
5674 isFixed : function(colIndex){
5675 return this.config[colIndex].fixed;
5679 * Returns true if the column can be resized
5682 isResizable : function(colIndex){
5683 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5686 * Sets if a column is hidden.
5687 * @param {Number} colIndex The column index
5688 * @param {Boolean} hidden True if the column is hidden
5690 setHidden : function(colIndex, hidden){
5691 this.config[colIndex].hidden = hidden;
5692 this.totalWidth = null;
5693 this.fireEvent("hiddenchange", this, colIndex, hidden);
5697 * Sets the editor for a column.
5698 * @param {Number} col The column index
5699 * @param {Object} editor The editor object
5701 setEditor : function(col, editor){
5702 this.config[col].editor = editor;
5706 Roo.grid.ColumnModel.defaultRenderer = function(value)
5708 if(typeof value == "object") {
5711 if(typeof value == "string" && value.length < 1){
5715 return String.format("{0}", value);
5718 // Alias for backwards compatibility
5719 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5722 * Ext JS Library 1.1.1
5723 * Copyright(c) 2006-2007, Ext JS, LLC.
5725 * Originally Released Under LGPL - original licence link has changed is not relivant.
5728 * <script type="text/javascript">
5732 * @class Roo.LoadMask
5733 * A simple utility class for generically masking elements while loading data. If the element being masked has
5734 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5735 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5736 * element's UpdateManager load indicator and will be destroyed after the initial load.
5738 * Create a new LoadMask
5739 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5740 * @param {Object} config The config object
5742 Roo.LoadMask = function(el, config){
5743 this.el = Roo.get(el);
5744 Roo.apply(this, config);
5746 this.store.on('beforeload', this.onBeforeLoad, this);
5747 this.store.on('load', this.onLoad, this);
5748 this.store.on('loadexception', this.onLoadException, this);
5749 this.removeMask = false;
5751 var um = this.el.getUpdateManager();
5752 um.showLoadIndicator = false; // disable the default indicator
5753 um.on('beforeupdate', this.onBeforeLoad, this);
5754 um.on('update', this.onLoad, this);
5755 um.on('failure', this.onLoad, this);
5756 this.removeMask = true;
5760 Roo.LoadMask.prototype = {
5762 * @cfg {Boolean} removeMask
5763 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5764 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5768 * The text to display in a centered loading message box (defaults to 'Loading...')
5772 * @cfg {String} msgCls
5773 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5775 msgCls : 'x-mask-loading',
5778 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5784 * Disables the mask to prevent it from being displayed
5786 disable : function(){
5787 this.disabled = true;
5791 * Enables the mask so that it can be displayed
5793 enable : function(){
5794 this.disabled = false;
5797 onLoadException : function()
5801 if (typeof(arguments[3]) != 'undefined') {
5802 Roo.MessageBox.alert("Error loading",arguments[3]);
5806 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5807 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5814 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5819 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5823 onBeforeLoad : function(){
5825 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5830 destroy : function(){
5832 this.store.un('beforeload', this.onBeforeLoad, this);
5833 this.store.un('load', this.onLoad, this);
5834 this.store.un('loadexception', this.onLoadException, this);
5836 var um = this.el.getUpdateManager();
5837 um.un('beforeupdate', this.onBeforeLoad, this);
5838 um.un('update', this.onLoad, this);
5839 um.un('failure', this.onLoad, this);
5850 * @class Roo.bootstrap.Table
5851 * @extends Roo.bootstrap.Component
5852 * Bootstrap Table class
5853 * @cfg {String} cls table class
5854 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5855 * @cfg {String} bgcolor Specifies the background color for a table
5856 * @cfg {Number} border Specifies whether the table cells should have borders or not
5857 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5858 * @cfg {Number} cellspacing Specifies the space between cells
5859 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5860 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5861 * @cfg {String} sortable Specifies that the table should be sortable
5862 * @cfg {String} summary Specifies a summary of the content of a table
5863 * @cfg {Number} width Specifies the width of a table
5864 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5866 * @cfg {boolean} striped Should the rows be alternative striped
5867 * @cfg {boolean} bordered Add borders to the table
5868 * @cfg {boolean} hover Add hover highlighting
5869 * @cfg {boolean} condensed Format condensed
5870 * @cfg {boolean} responsive Format condensed
5871 * @cfg {Boolean} loadMask (true|false) default false
5872 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5873 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5874 * @cfg {Boolean} rowSelection (true|false) default false
5875 * @cfg {Boolean} cellSelection (true|false) default false
5876 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5877 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5878 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5882 * Create a new Table
5883 * @param {Object} config The config object
5886 Roo.bootstrap.Table = function(config){
5887 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5892 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5893 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5894 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5895 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5897 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5899 this.sm.grid = this;
5900 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5901 this.sm = this.selModel;
5902 this.sm.xmodule = this.xmodule || false;
5905 if (this.cm && typeof(this.cm.config) == 'undefined') {
5906 this.colModel = new Roo.grid.ColumnModel(this.cm);
5907 this.cm = this.colModel;
5908 this.cm.xmodule = this.xmodule || false;
5911 this.store= Roo.factory(this.store, Roo.data);
5912 this.ds = this.store;
5913 this.ds.xmodule = this.xmodule || false;
5916 if (this.footer && this.store) {
5917 this.footer.dataSource = this.ds;
5918 this.footer = Roo.factory(this.footer);
5925 * Fires when a cell is clicked
5926 * @param {Roo.bootstrap.Table} this
5927 * @param {Roo.Element} el
5928 * @param {Number} rowIndex
5929 * @param {Number} columnIndex
5930 * @param {Roo.EventObject} e
5934 * @event celldblclick
5935 * Fires when a cell is double clicked
5936 * @param {Roo.bootstrap.Table} this
5937 * @param {Roo.Element} el
5938 * @param {Number} rowIndex
5939 * @param {Number} columnIndex
5940 * @param {Roo.EventObject} e
5942 "celldblclick" : true,
5945 * Fires when a row is clicked
5946 * @param {Roo.bootstrap.Table} this
5947 * @param {Roo.Element} el
5948 * @param {Number} rowIndex
5949 * @param {Roo.EventObject} e
5953 * @event rowdblclick
5954 * Fires when a row is double clicked
5955 * @param {Roo.bootstrap.Table} this
5956 * @param {Roo.Element} el
5957 * @param {Number} rowIndex
5958 * @param {Roo.EventObject} e
5960 "rowdblclick" : true,
5963 * Fires when a mouseover occur
5964 * @param {Roo.bootstrap.Table} this
5965 * @param {Roo.Element} el
5966 * @param {Number} rowIndex
5967 * @param {Number} columnIndex
5968 * @param {Roo.EventObject} e
5973 * Fires when a mouseout occur
5974 * @param {Roo.bootstrap.Table} this
5975 * @param {Roo.Element} el
5976 * @param {Number} rowIndex
5977 * @param {Number} columnIndex
5978 * @param {Roo.EventObject} e
5983 * Fires when a row is rendered, so you can change add a style to it.
5984 * @param {Roo.bootstrap.Table} this
5985 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5989 * @event rowsrendered
5990 * Fires when all the rows have been rendered
5991 * @param {Roo.bootstrap.Table} this
5993 'rowsrendered' : true,
5995 * @event contextmenu
5996 * The raw contextmenu event for the entire grid.
5997 * @param {Roo.EventObject} e
5999 "contextmenu" : true,
6001 * @event rowcontextmenu
6002 * Fires when a row is right clicked
6003 * @param {Roo.bootstrap.Table} this
6004 * @param {Number} rowIndex
6005 * @param {Roo.EventObject} e
6007 "rowcontextmenu" : true,
6009 * @event cellcontextmenu
6010 * Fires when a cell is right clicked
6011 * @param {Roo.bootstrap.Table} this
6012 * @param {Number} rowIndex
6013 * @param {Number} cellIndex
6014 * @param {Roo.EventObject} e
6016 "cellcontextmenu" : true,
6018 * @event headercontextmenu
6019 * Fires when a header is right clicked
6020 * @param {Roo.bootstrap.Table} this
6021 * @param {Number} columnIndex
6022 * @param {Roo.EventObject} e
6024 "headercontextmenu" : true
6028 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6054 rowSelection : false,
6055 cellSelection : false,
6058 // Roo.Element - the tbody
6060 // Roo.Element - thead element
6063 container: false, // used by gridpanel...
6069 getAutoCreate : function()
6071 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6078 if (this.scrollBody) {
6079 cfg.cls += ' table-body-fixed';
6082 cfg.cls += ' table-striped';
6086 cfg.cls += ' table-hover';
6088 if (this.bordered) {
6089 cfg.cls += ' table-bordered';
6091 if (this.condensed) {
6092 cfg.cls += ' table-condensed';
6094 if (this.responsive) {
6095 cfg.cls += ' table-responsive';
6099 cfg.cls+= ' ' +this.cls;
6102 // this lot should be simplifed...
6105 cfg.align=this.align;
6108 cfg.bgcolor=this.bgcolor;
6111 cfg.border=this.border;
6113 if (this.cellpadding) {
6114 cfg.cellpadding=this.cellpadding;
6116 if (this.cellspacing) {
6117 cfg.cellspacing=this.cellspacing;
6120 cfg.frame=this.frame;
6123 cfg.rules=this.rules;
6125 if (this.sortable) {
6126 cfg.sortable=this.sortable;
6129 cfg.summary=this.summary;
6132 cfg.width=this.width;
6135 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6138 if(this.store || this.cm){
6139 if(this.headerShow){
6140 cfg.cn.push(this.renderHeader());
6143 cfg.cn.push(this.renderBody());
6145 if(this.footerShow){
6146 cfg.cn.push(this.renderFooter());
6148 // where does this come from?
6149 //cfg.cls+= ' TableGrid';
6152 return { cn : [ cfg ] };
6155 initEvents : function()
6157 if(!this.store || !this.cm){
6160 if (this.selModel) {
6161 this.selModel.initEvents();
6165 //Roo.log('initEvents with ds!!!!');
6167 this.mainBody = this.el.select('tbody', true).first();
6168 this.mainHead = this.el.select('thead', true).first();
6175 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6176 e.on('click', _this.sort, _this);
6179 this.mainBody.on("click", this.onClick, this);
6180 this.mainBody.on("dblclick", this.onDblClick, this);
6182 // why is this done????? = it breaks dialogs??
6183 //this.parent().el.setStyle('position', 'relative');
6187 this.footer.parentId = this.id;
6188 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6191 this.el.select('tfoot tr td').first().addClass('hide');
6196 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6199 this.store.on('load', this.onLoad, this);
6200 this.store.on('beforeload', this.onBeforeLoad, this);
6201 this.store.on('update', this.onUpdate, this);
6202 this.store.on('add', this.onAdd, this);
6203 this.store.on("clear", this.clear, this);
6205 this.el.on("contextmenu", this.onContextMenu, this);
6207 this.mainBody.on('scroll', this.onBodyScroll, this);
6209 this.cm.on("headerchange", this.onHeaderChange, this);
6211 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6215 onContextMenu : function(e, t)
6217 this.processEvent("contextmenu", e);
6220 processEvent : function(name, e)
6222 if (name != 'touchstart' ) {
6223 this.fireEvent(name, e);
6226 var t = e.getTarget();
6228 var cell = Roo.get(t);
6234 if(cell.findParent('tfoot', false, true)){
6238 if(cell.findParent('thead', false, true)){
6240 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6241 cell = Roo.get(t).findParent('th', false, true);
6243 Roo.log("failed to find th in thead?");
6244 Roo.log(e.getTarget());
6249 var cellIndex = cell.dom.cellIndex;
6251 var ename = name == 'touchstart' ? 'click' : name;
6252 this.fireEvent("header" + ename, this, cellIndex, e);
6257 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6258 cell = Roo.get(t).findParent('td', false, true);
6260 Roo.log("failed to find th in tbody?");
6261 Roo.log(e.getTarget());
6266 var row = cell.findParent('tr', false, true);
6267 var cellIndex = cell.dom.cellIndex;
6268 var rowIndex = row.dom.rowIndex - 1;
6272 this.fireEvent("row" + name, this, rowIndex, e);
6276 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6282 onMouseover : function(e, el)
6284 var cell = Roo.get(el);
6290 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6291 cell = cell.findParent('td', false, true);
6294 var row = cell.findParent('tr', false, true);
6295 var cellIndex = cell.dom.cellIndex;
6296 var rowIndex = row.dom.rowIndex - 1; // start from 0
6298 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6302 onMouseout : function(e, el)
6304 var cell = Roo.get(el);
6310 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6311 cell = cell.findParent('td', false, true);
6314 var row = cell.findParent('tr', false, true);
6315 var cellIndex = cell.dom.cellIndex;
6316 var rowIndex = row.dom.rowIndex - 1; // start from 0
6318 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6322 onClick : function(e, el)
6324 var cell = Roo.get(el);
6326 if(!cell || (!this.cellSelection && !this.rowSelection)){
6330 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6331 cell = cell.findParent('td', false, true);
6334 if(!cell || typeof(cell) == 'undefined'){
6338 var row = cell.findParent('tr', false, true);
6340 if(!row || typeof(row) == 'undefined'){
6344 var cellIndex = cell.dom.cellIndex;
6345 var rowIndex = this.getRowIndex(row);
6347 // why??? - should these not be based on SelectionModel?
6348 if(this.cellSelection){
6349 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6352 if(this.rowSelection){
6353 this.fireEvent('rowclick', this, row, rowIndex, e);
6359 onDblClick : function(e,el)
6361 var cell = Roo.get(el);
6363 if(!cell || (!this.cellSelection && !this.rowSelection)){
6367 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6368 cell = cell.findParent('td', false, true);
6371 if(!cell || typeof(cell) == 'undefined'){
6375 var row = cell.findParent('tr', false, true);
6377 if(!row || typeof(row) == 'undefined'){
6381 var cellIndex = cell.dom.cellIndex;
6382 var rowIndex = this.getRowIndex(row);
6384 if(this.cellSelection){
6385 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6388 if(this.rowSelection){
6389 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6393 sort : function(e,el)
6395 var col = Roo.get(el);
6397 if(!col.hasClass('sortable')){
6401 var sort = col.attr('sort');
6404 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6408 this.store.sortInfo = {field : sort, direction : dir};
6411 Roo.log("calling footer first");
6412 this.footer.onClick('first');
6415 this.store.load({ params : { start : 0 } });
6419 renderHeader : function()
6427 this.totalWidth = 0;
6429 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6431 var config = cm.config[i];
6435 cls : 'x-hcol-' + i,
6437 html: cm.getColumnHeader(i)
6442 if(typeof(config.sortable) != 'undefined' && config.sortable){
6444 c.html = '<i class="glyphicon"></i>' + c.html;
6447 if(typeof(config.lgHeader) != 'undefined'){
6448 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6451 if(typeof(config.mdHeader) != 'undefined'){
6452 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6455 if(typeof(config.smHeader) != 'undefined'){
6456 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6459 if(typeof(config.xsHeader) != 'undefined'){
6460 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6467 if(typeof(config.tooltip) != 'undefined'){
6468 c.tooltip = config.tooltip;
6471 if(typeof(config.colspan) != 'undefined'){
6472 c.colspan = config.colspan;
6475 if(typeof(config.hidden) != 'undefined' && config.hidden){
6476 c.style += ' display:none;';
6479 if(typeof(config.dataIndex) != 'undefined'){
6480 c.sort = config.dataIndex;
6485 if(typeof(config.align) != 'undefined' && config.align.length){
6486 c.style += ' text-align:' + config.align + ';';
6489 if(typeof(config.width) != 'undefined'){
6490 c.style += ' width:' + config.width + 'px;';
6491 this.totalWidth += config.width;
6493 this.totalWidth += 100; // assume minimum of 100 per column?
6496 if(typeof(config.cls) != 'undefined'){
6497 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6500 ['xs','sm','md','lg'].map(function(size){
6502 if(typeof(config[size]) == 'undefined'){
6506 if (!config[size]) { // 0 = hidden
6507 c.cls += ' hidden-' + size;
6511 c.cls += ' col-' + size + '-' + config[size];
6521 renderBody : function()
6531 colspan : this.cm.getColumnCount()
6541 renderFooter : function()
6551 colspan : this.cm.getColumnCount()
6565 // Roo.log('ds onload');
6570 var ds = this.store;
6572 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6573 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6574 if (_this.store.sortInfo) {
6576 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6577 e.select('i', true).addClass(['glyphicon-arrow-up']);
6580 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6581 e.select('i', true).addClass(['glyphicon-arrow-down']);
6586 var tbody = this.mainBody;
6588 if(ds.getCount() > 0){
6589 ds.data.each(function(d,rowIndex){
6590 var row = this.renderRow(cm, ds, rowIndex);
6592 tbody.createChild(row);
6596 if(row.cellObjects.length){
6597 Roo.each(row.cellObjects, function(r){
6598 _this.renderCellObject(r);
6605 Roo.each(this.el.select('tbody td', true).elements, function(e){
6606 e.on('mouseover', _this.onMouseover, _this);
6609 Roo.each(this.el.select('tbody td', true).elements, function(e){
6610 e.on('mouseout', _this.onMouseout, _this);
6612 this.fireEvent('rowsrendered', this);
6618 onUpdate : function(ds,record)
6620 this.refreshRow(record);
6624 onRemove : function(ds, record, index, isUpdate){
6625 if(isUpdate !== true){
6626 this.fireEvent("beforerowremoved", this, index, record);
6628 var bt = this.mainBody.dom;
6630 var rows = this.el.select('tbody > tr', true).elements;
6632 if(typeof(rows[index]) != 'undefined'){
6633 bt.removeChild(rows[index].dom);
6636 // if(bt.rows[index]){
6637 // bt.removeChild(bt.rows[index]);
6640 if(isUpdate !== true){
6641 //this.stripeRows(index);
6642 //this.syncRowHeights(index, index);
6644 this.fireEvent("rowremoved", this, index, record);
6648 onAdd : function(ds, records, rowIndex)
6650 //Roo.log('on Add called');
6651 // - note this does not handle multiple adding very well..
6652 var bt = this.mainBody.dom;
6653 for (var i =0 ; i < records.length;i++) {
6654 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6655 //Roo.log(records[i]);
6656 //Roo.log(this.store.getAt(rowIndex+i));
6657 this.insertRow(this.store, rowIndex + i, false);
6664 refreshRow : function(record){
6665 var ds = this.store, index;
6666 if(typeof record == 'number'){
6668 record = ds.getAt(index);
6670 index = ds.indexOf(record);
6672 this.insertRow(ds, index, true);
6674 this.onRemove(ds, record, index+1, true);
6676 //this.syncRowHeights(index, index);
6678 this.fireEvent("rowupdated", this, index, record);
6681 insertRow : function(dm, rowIndex, isUpdate){
6684 this.fireEvent("beforerowsinserted", this, rowIndex);
6686 //var s = this.getScrollState();
6687 var row = this.renderRow(this.cm, this.store, rowIndex);
6688 // insert before rowIndex..
6689 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6693 if(row.cellObjects.length){
6694 Roo.each(row.cellObjects, function(r){
6695 _this.renderCellObject(r);
6700 this.fireEvent("rowsinserted", this, rowIndex);
6701 //this.syncRowHeights(firstRow, lastRow);
6702 //this.stripeRows(firstRow);
6709 getRowDom : function(rowIndex)
6711 var rows = this.el.select('tbody > tr', true).elements;
6713 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6716 // returns the object tree for a tr..
6719 renderRow : function(cm, ds, rowIndex)
6721 var d = ds.getAt(rowIndex);
6725 cls : 'x-row-' + rowIndex,
6729 var cellObjects = [];
6731 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6732 var config = cm.config[i];
6734 var renderer = cm.getRenderer(i);
6738 if(typeof(renderer) !== 'undefined'){
6739 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6741 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6742 // and are rendered into the cells after the row is rendered - using the id for the element.
6744 if(typeof(value) === 'object'){
6754 rowIndex : rowIndex,
6759 this.fireEvent('rowclass', this, rowcfg);
6763 cls : rowcfg.rowClass + ' x-col-' + i,
6765 html: (typeof(value) === 'object') ? '' : value
6772 if(typeof(config.colspan) != 'undefined'){
6773 td.colspan = config.colspan;
6776 if(typeof(config.hidden) != 'undefined' && config.hidden){
6777 td.style += ' display:none;';
6780 if(typeof(config.align) != 'undefined' && config.align.length){
6781 td.style += ' text-align:' + config.align + ';';
6784 if(typeof(config.width) != 'undefined'){
6785 td.style += ' width:' + config.width + 'px;';
6788 if(typeof(config.cursor) != 'undefined'){
6789 td.style += ' cursor:' + config.cursor + ';';
6792 if(typeof(config.cls) != 'undefined'){
6793 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6796 ['xs','sm','md','lg'].map(function(size){
6798 if(typeof(config[size]) == 'undefined'){
6802 if (!config[size]) { // 0 = hidden
6803 td.cls += ' hidden-' + size;
6807 td.cls += ' col-' + size + '-' + config[size];
6815 row.cellObjects = cellObjects;
6823 onBeforeLoad : function()
6832 this.el.select('tbody', true).first().dom.innerHTML = '';
6835 * Show or hide a row.
6836 * @param {Number} rowIndex to show or hide
6837 * @param {Boolean} state hide
6839 setRowVisibility : function(rowIndex, state)
6841 var bt = this.mainBody.dom;
6843 var rows = this.el.select('tbody > tr', true).elements;
6845 if(typeof(rows[rowIndex]) == 'undefined'){
6848 rows[rowIndex].dom.style.display = state ? '' : 'none';
6852 getSelectionModel : function(){
6854 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6856 return this.selModel;
6859 * Render the Roo.bootstrap object from renderder
6861 renderCellObject : function(r)
6865 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6867 var t = r.cfg.render(r.container);
6870 Roo.each(r.cfg.cn, function(c){
6872 container: t.getChildContainer(),
6875 _this.renderCellObject(child);
6880 getRowIndex : function(row)
6884 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6895 * Returns the grid's underlying element = used by panel.Grid
6896 * @return {Element} The element
6898 getGridEl : function(){
6902 * Forces a resize - used by panel.Grid
6903 * @return {Element} The element
6905 autoSize : function()
6907 //var ctr = Roo.get(this.container.dom.parentElement);
6908 var ctr = Roo.get(this.el.dom);
6910 var thd = this.getGridEl().select('thead',true).first();
6911 var tbd = this.getGridEl().select('tbody', true).first();
6912 var tfd = this.getGridEl().select('tfoot', true).first();
6914 var cw = ctr.getWidth();
6918 tbd.setSize(ctr.getWidth(),
6919 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6921 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6924 cw = Math.max(cw, this.totalWidth);
6925 this.getGridEl().select('tr',true).setWidth(cw);
6926 // resize 'expandable coloumn?
6928 return; // we doe not have a view in this design..
6931 onBodyScroll: function()
6933 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6935 this.mainHead.setStyle({
6936 'position' : 'relative',
6937 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6943 var scrollHeight = this.mainBody.dom.scrollHeight;
6945 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6947 var height = this.mainBody.getHeight();
6949 if(scrollHeight - height == scrollTop) {
6951 var total = this.ds.getTotalCount();
6953 if(this.footer.cursor + this.footer.pageSize < total){
6955 this.footer.ds.load({
6957 start : this.footer.cursor + this.footer.pageSize,
6958 limit : this.footer.pageSize
6968 onHeaderChange : function()
6970 var header = this.renderHeader();
6971 var table = this.el.select('table', true).first();
6973 this.mainHead.remove();
6974 this.mainHead = table.createChild(header, this.mainBody, false);
6977 onHiddenChange : function(colModel, colIndex, hidden)
6979 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6980 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6982 this.CSS.updateRule(thSelector, "display", "");
6983 this.CSS.updateRule(tdSelector, "display", "");
6986 this.CSS.updateRule(thSelector, "display", "none");
6987 this.CSS.updateRule(tdSelector, "display", "none");
6990 this.onHeaderChange();
7007 * @class Roo.bootstrap.TableCell
7008 * @extends Roo.bootstrap.Component
7009 * Bootstrap TableCell class
7010 * @cfg {String} html cell contain text
7011 * @cfg {String} cls cell class
7012 * @cfg {String} tag cell tag (td|th) default td
7013 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7014 * @cfg {String} align Aligns the content in a cell
7015 * @cfg {String} axis Categorizes cells
7016 * @cfg {String} bgcolor Specifies the background color of a cell
7017 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7018 * @cfg {Number} colspan Specifies the number of columns a cell should span
7019 * @cfg {String} headers Specifies one or more header cells a cell is related to
7020 * @cfg {Number} height Sets the height of a cell
7021 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7022 * @cfg {Number} rowspan Sets the number of rows a cell should span
7023 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7024 * @cfg {String} valign Vertical aligns the content in a cell
7025 * @cfg {Number} width Specifies the width of a cell
7028 * Create a new TableCell
7029 * @param {Object} config The config object
7032 Roo.bootstrap.TableCell = function(config){
7033 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7036 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7056 getAutoCreate : function(){
7057 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7077 cfg.align=this.align
7083 cfg.bgcolor=this.bgcolor
7086 cfg.charoff=this.charoff
7089 cfg.colspan=this.colspan
7092 cfg.headers=this.headers
7095 cfg.height=this.height
7098 cfg.nowrap=this.nowrap
7101 cfg.rowspan=this.rowspan
7104 cfg.scope=this.scope
7107 cfg.valign=this.valign
7110 cfg.width=this.width
7129 * @class Roo.bootstrap.TableRow
7130 * @extends Roo.bootstrap.Component
7131 * Bootstrap TableRow class
7132 * @cfg {String} cls row class
7133 * @cfg {String} align Aligns the content in a table row
7134 * @cfg {String} bgcolor Specifies a background color for a table row
7135 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7136 * @cfg {String} valign Vertical aligns the content in a table row
7139 * Create a new TableRow
7140 * @param {Object} config The config object
7143 Roo.bootstrap.TableRow = function(config){
7144 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7147 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7155 getAutoCreate : function(){
7156 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7166 cfg.align = this.align;
7169 cfg.bgcolor = this.bgcolor;
7172 cfg.charoff = this.charoff;
7175 cfg.valign = this.valign;
7193 * @class Roo.bootstrap.TableBody
7194 * @extends Roo.bootstrap.Component
7195 * Bootstrap TableBody class
7196 * @cfg {String} cls element class
7197 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7198 * @cfg {String} align Aligns the content inside the element
7199 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7200 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7203 * Create a new TableBody
7204 * @param {Object} config The config object
7207 Roo.bootstrap.TableBody = function(config){
7208 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7211 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7219 getAutoCreate : function(){
7220 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7234 cfg.align = this.align;
7237 cfg.charoff = this.charoff;
7240 cfg.valign = this.valign;
7247 // initEvents : function()
7254 // this.store = Roo.factory(this.store, Roo.data);
7255 // this.store.on('load', this.onLoad, this);
7257 // this.store.load();
7261 // onLoad: function ()
7263 // this.fireEvent('load', this);
7273 * Ext JS Library 1.1.1
7274 * Copyright(c) 2006-2007, Ext JS, LLC.
7276 * Originally Released Under LGPL - original licence link has changed is not relivant.
7279 * <script type="text/javascript">
7282 // as we use this in bootstrap.
7283 Roo.namespace('Roo.form');
7285 * @class Roo.form.Action
7286 * Internal Class used to handle form actions
7288 * @param {Roo.form.BasicForm} el The form element or its id
7289 * @param {Object} config Configuration options
7294 // define the action interface
7295 Roo.form.Action = function(form, options){
7297 this.options = options || {};
7300 * Client Validation Failed
7303 Roo.form.Action.CLIENT_INVALID = 'client';
7305 * Server Validation Failed
7308 Roo.form.Action.SERVER_INVALID = 'server';
7310 * Connect to Server Failed
7313 Roo.form.Action.CONNECT_FAILURE = 'connect';
7315 * Reading Data from Server Failed
7318 Roo.form.Action.LOAD_FAILURE = 'load';
7320 Roo.form.Action.prototype = {
7322 failureType : undefined,
7323 response : undefined,
7327 run : function(options){
7332 success : function(response){
7337 handleResponse : function(response){
7341 // default connection failure
7342 failure : function(response){
7344 this.response = response;
7345 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7346 this.form.afterAction(this, false);
7349 processResponse : function(response){
7350 this.response = response;
7351 if(!response.responseText){
7354 this.result = this.handleResponse(response);
7358 // utility functions used internally
7359 getUrl : function(appendParams){
7360 var url = this.options.url || this.form.url || this.form.el.dom.action;
7362 var p = this.getParams();
7364 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7370 getMethod : function(){
7371 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7374 getParams : function(){
7375 var bp = this.form.baseParams;
7376 var p = this.options.params;
7378 if(typeof p == "object"){
7379 p = Roo.urlEncode(Roo.applyIf(p, bp));
7380 }else if(typeof p == 'string' && bp){
7381 p += '&' + Roo.urlEncode(bp);
7384 p = Roo.urlEncode(bp);
7389 createCallback : function(){
7391 success: this.success,
7392 failure: this.failure,
7394 timeout: (this.form.timeout*1000),
7395 upload: this.form.fileUpload ? this.success : undefined
7400 Roo.form.Action.Submit = function(form, options){
7401 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7404 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7407 haveProgress : false,
7408 uploadComplete : false,
7410 // uploadProgress indicator.
7411 uploadProgress : function()
7413 if (!this.form.progressUrl) {
7417 if (!this.haveProgress) {
7418 Roo.MessageBox.progress("Uploading", "Uploading");
7420 if (this.uploadComplete) {
7421 Roo.MessageBox.hide();
7425 this.haveProgress = true;
7427 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7429 var c = new Roo.data.Connection();
7431 url : this.form.progressUrl,
7436 success : function(req){
7437 //console.log(data);
7441 rdata = Roo.decode(req.responseText)
7443 Roo.log("Invalid data from server..");
7447 if (!rdata || !rdata.success) {
7449 Roo.MessageBox.alert(Roo.encode(rdata));
7452 var data = rdata.data;
7454 if (this.uploadComplete) {
7455 Roo.MessageBox.hide();
7460 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7461 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7464 this.uploadProgress.defer(2000,this);
7467 failure: function(data) {
7468 Roo.log('progress url failed ');
7479 // run get Values on the form, so it syncs any secondary forms.
7480 this.form.getValues();
7482 var o = this.options;
7483 var method = this.getMethod();
7484 var isPost = method == 'POST';
7485 if(o.clientValidation === false || this.form.isValid()){
7487 if (this.form.progressUrl) {
7488 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7489 (new Date() * 1) + '' + Math.random());
7494 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7495 form:this.form.el.dom,
7496 url:this.getUrl(!isPost),
7498 params:isPost ? this.getParams() : null,
7499 isUpload: this.form.fileUpload
7502 this.uploadProgress();
7504 }else if (o.clientValidation !== false){ // client validation failed
7505 this.failureType = Roo.form.Action.CLIENT_INVALID;
7506 this.form.afterAction(this, false);
7510 success : function(response)
7512 this.uploadComplete= true;
7513 if (this.haveProgress) {
7514 Roo.MessageBox.hide();
7518 var result = this.processResponse(response);
7519 if(result === true || result.success){
7520 this.form.afterAction(this, true);
7524 this.form.markInvalid(result.errors);
7525 this.failureType = Roo.form.Action.SERVER_INVALID;
7527 this.form.afterAction(this, false);
7529 failure : function(response)
7531 this.uploadComplete= true;
7532 if (this.haveProgress) {
7533 Roo.MessageBox.hide();
7536 this.response = response;
7537 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7538 this.form.afterAction(this, false);
7541 handleResponse : function(response){
7542 if(this.form.errorReader){
7543 var rs = this.form.errorReader.read(response);
7546 for(var i = 0, len = rs.records.length; i < len; i++) {
7547 var r = rs.records[i];
7551 if(errors.length < 1){
7555 success : rs.success,
7561 ret = Roo.decode(response.responseText);
7565 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7575 Roo.form.Action.Load = function(form, options){
7576 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7577 this.reader = this.form.reader;
7580 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7585 Roo.Ajax.request(Roo.apply(
7586 this.createCallback(), {
7587 method:this.getMethod(),
7588 url:this.getUrl(false),
7589 params:this.getParams()
7593 success : function(response){
7595 var result = this.processResponse(response);
7596 if(result === true || !result.success || !result.data){
7597 this.failureType = Roo.form.Action.LOAD_FAILURE;
7598 this.form.afterAction(this, false);
7601 this.form.clearInvalid();
7602 this.form.setValues(result.data);
7603 this.form.afterAction(this, true);
7606 handleResponse : function(response){
7607 if(this.form.reader){
7608 var rs = this.form.reader.read(response);
7609 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7611 success : rs.success,
7615 return Roo.decode(response.responseText);
7619 Roo.form.Action.ACTION_TYPES = {
7620 'load' : Roo.form.Action.Load,
7621 'submit' : Roo.form.Action.Submit
7630 * @class Roo.bootstrap.Form
7631 * @extends Roo.bootstrap.Component
7632 * Bootstrap Form class
7633 * @cfg {String} method GET | POST (default POST)
7634 * @cfg {String} labelAlign top | left (default top)
7635 * @cfg {String} align left | right - for navbars
7636 * @cfg {Boolean} loadMask load mask when submit (default true)
7641 * @param {Object} config The config object
7645 Roo.bootstrap.Form = function(config){
7647 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7649 Roo.bootstrap.Form.popover.apply();
7653 * @event clientvalidation
7654 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7655 * @param {Form} this
7656 * @param {Boolean} valid true if the form has passed client-side validation
7658 clientvalidation: true,
7660 * @event beforeaction
7661 * Fires before any action is performed. Return false to cancel the action.
7662 * @param {Form} this
7663 * @param {Action} action The action to be performed
7667 * @event actionfailed
7668 * Fires when an action fails.
7669 * @param {Form} this
7670 * @param {Action} action The action that failed
7672 actionfailed : true,
7674 * @event actioncomplete
7675 * Fires when an action is completed.
7676 * @param {Form} this
7677 * @param {Action} action The action that completed
7679 actioncomplete : true
7683 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7686 * @cfg {String} method
7687 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7692 * The URL to use for form actions if one isn't supplied in the action options.
7695 * @cfg {Boolean} fileUpload
7696 * Set to true if this form is a file upload.
7700 * @cfg {Object} baseParams
7701 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7705 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7709 * @cfg {Sting} align (left|right) for navbar forms
7714 activeAction : null,
7717 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7718 * element by passing it or its id or mask the form itself by passing in true.
7721 waitMsgTarget : false,
7726 * @cfg {Boolean} errorMask (true|false) default false
7731 * @cfg {Number} maskOffset Default 100
7736 * @cfg {Boolean} maskBody
7740 getAutoCreate : function(){
7744 method : this.method || 'POST',
7745 id : this.id || Roo.id(),
7748 if (this.parent().xtype.match(/^Nav/)) {
7749 cfg.cls = 'navbar-form navbar-' + this.align;
7753 if (this.labelAlign == 'left' ) {
7754 cfg.cls += ' form-horizontal';
7760 initEvents : function()
7762 this.el.on('submit', this.onSubmit, this);
7763 // this was added as random key presses on the form where triggering form submit.
7764 this.el.on('keypress', function(e) {
7765 if (e.getCharCode() != 13) {
7768 // we might need to allow it for textareas.. and some other items.
7769 // check e.getTarget().
7771 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7775 Roo.log("keypress blocked");
7783 onSubmit : function(e){
7788 * Returns true if client-side validation on the form is successful.
7791 isValid : function(){
7792 var items = this.getItems();
7796 items.each(function(f){
7803 if(!target && f.el.isVisible(true)){
7809 if(this.errorMask && !valid){
7810 Roo.bootstrap.Form.popover.mask(this, target);
7817 * Returns true if any fields in this form have changed since their original load.
7820 isDirty : function(){
7822 var items = this.getItems();
7823 items.each(function(f){
7833 * Performs a predefined action (submit or load) or custom actions you define on this form.
7834 * @param {String} actionName The name of the action type
7835 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7836 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7837 * accept other config options):
7839 Property Type Description
7840 ---------------- --------------- ----------------------------------------------------------------------------------
7841 url String The url for the action (defaults to the form's url)
7842 method String The form method to use (defaults to the form's method, or POST if not defined)
7843 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7844 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7845 validate the form on the client (defaults to false)
7847 * @return {BasicForm} this
7849 doAction : function(action, options){
7850 if(typeof action == 'string'){
7851 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7853 if(this.fireEvent('beforeaction', this, action) !== false){
7854 this.beforeAction(action);
7855 action.run.defer(100, action);
7861 beforeAction : function(action){
7862 var o = action.options;
7867 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7869 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7872 // not really supported yet.. ??
7874 //if(this.waitMsgTarget === true){
7875 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7876 //}else if(this.waitMsgTarget){
7877 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7878 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7880 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7886 afterAction : function(action, success){
7887 this.activeAction = null;
7888 var o = action.options;
7893 Roo.get(document.body).unmask();
7899 //if(this.waitMsgTarget === true){
7900 // this.el.unmask();
7901 //}else if(this.waitMsgTarget){
7902 // this.waitMsgTarget.unmask();
7904 // Roo.MessageBox.updateProgress(1);
7905 // Roo.MessageBox.hide();
7912 Roo.callback(o.success, o.scope, [this, action]);
7913 this.fireEvent('actioncomplete', this, action);
7917 // failure condition..
7918 // we have a scenario where updates need confirming.
7919 // eg. if a locking scenario exists..
7920 // we look for { errors : { needs_confirm : true }} in the response.
7922 (typeof(action.result) != 'undefined') &&
7923 (typeof(action.result.errors) != 'undefined') &&
7924 (typeof(action.result.errors.needs_confirm) != 'undefined')
7927 Roo.log("not supported yet");
7930 Roo.MessageBox.confirm(
7931 "Change requires confirmation",
7932 action.result.errorMsg,
7937 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7947 Roo.callback(o.failure, o.scope, [this, action]);
7948 // show an error message if no failed handler is set..
7949 if (!this.hasListener('actionfailed')) {
7950 Roo.log("need to add dialog support");
7952 Roo.MessageBox.alert("Error",
7953 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7954 action.result.errorMsg :
7955 "Saving Failed, please check your entries or try again"
7960 this.fireEvent('actionfailed', this, action);
7965 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7966 * @param {String} id The value to search for
7969 findField : function(id){
7970 var items = this.getItems();
7971 var field = items.get(id);
7973 items.each(function(f){
7974 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7981 return field || null;
7984 * Mark fields in this form invalid in bulk.
7985 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7986 * @return {BasicForm} this
7988 markInvalid : function(errors){
7989 if(errors instanceof Array){
7990 for(var i = 0, len = errors.length; i < len; i++){
7991 var fieldError = errors[i];
7992 var f = this.findField(fieldError.id);
7994 f.markInvalid(fieldError.msg);
8000 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8001 field.markInvalid(errors[id]);
8005 //Roo.each(this.childForms || [], function (f) {
8006 // f.markInvalid(errors);
8013 * Set values for fields in this form in bulk.
8014 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8015 * @return {BasicForm} this
8017 setValues : function(values){
8018 if(values instanceof Array){ // array of objects
8019 for(var i = 0, len = values.length; i < len; i++){
8021 var f = this.findField(v.id);
8023 f.setValue(v.value);
8024 if(this.trackResetOnLoad){
8025 f.originalValue = f.getValue();
8029 }else{ // object hash
8032 if(typeof values[id] != 'function' && (field = this.findField(id))){
8034 if (field.setFromData &&
8036 field.displayField &&
8037 // combos' with local stores can
8038 // be queried via setValue()
8039 // to set their value..
8040 (field.store && !field.store.isLocal)
8044 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8045 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8046 field.setFromData(sd);
8048 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8050 field.setFromData(values);
8053 field.setValue(values[id]);
8057 if(this.trackResetOnLoad){
8058 field.originalValue = field.getValue();
8064 //Roo.each(this.childForms || [], function (f) {
8065 // f.setValues(values);
8072 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8073 * they are returned as an array.
8074 * @param {Boolean} asString
8077 getValues : function(asString){
8078 //if (this.childForms) {
8079 // copy values from the child forms
8080 // Roo.each(this.childForms, function (f) {
8081 // this.setValues(f.getValues());
8087 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8088 if(asString === true){
8091 return Roo.urlDecode(fs);
8095 * Returns the fields in this form as an object with key/value pairs.
8096 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8099 getFieldValues : function(with_hidden)
8101 var items = this.getItems();
8103 items.each(function(f){
8109 var v = f.getValue();
8111 if (f.inputType =='radio') {
8112 if (typeof(ret[f.getName()]) == 'undefined') {
8113 ret[f.getName()] = ''; // empty..
8116 if (!f.el.dom.checked) {
8124 if(f.xtype == 'MoneyField'){
8125 ret[f.currencyName] = f.getCurrency();
8128 // not sure if this supported any more..
8129 if ((typeof(v) == 'object') && f.getRawValue) {
8130 v = f.getRawValue() ; // dates..
8132 // combo boxes where name != hiddenName...
8133 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8134 ret[f.name] = f.getRawValue();
8136 ret[f.getName()] = v;
8143 * Clears all invalid messages in this form.
8144 * @return {BasicForm} this
8146 clearInvalid : function(){
8147 var items = this.getItems();
8149 items.each(function(f){
8158 * @return {BasicForm} this
8161 var items = this.getItems();
8162 items.each(function(f){
8166 Roo.each(this.childForms || [], function (f) {
8174 getItems : function()
8176 var r=new Roo.util.MixedCollection(false, function(o){
8177 return o.id || (o.id = Roo.id());
8179 var iter = function(el) {
8186 Roo.each(el.items,function(e) {
8195 hideFields : function(items)
8197 Roo.each(items, function(i){
8199 var f = this.findField(i);
8205 if(f.xtype == 'DateField'){
8206 f.setVisible(false);
8215 showFields : function(items)
8217 Roo.each(items, function(i){
8219 var f = this.findField(i);
8225 if(f.xtype == 'DateField'){
8237 Roo.apply(Roo.bootstrap.Form, {
8264 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8265 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8266 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8267 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8270 this.maskEl.top.enableDisplayMode("block");
8271 this.maskEl.left.enableDisplayMode("block");
8272 this.maskEl.bottom.enableDisplayMode("block");
8273 this.maskEl.right.enableDisplayMode("block");
8275 this.toolTip = new Roo.bootstrap.Tooltip({
8276 cls : 'roo-form-error-popover',
8278 'left' : ['r-l', [-2,0], 'right'],
8279 'right' : ['l-r', [2,0], 'left'],
8280 'bottom' : ['tl-bl', [0,2], 'top'],
8281 'top' : [ 'bl-tl', [0,-2], 'bottom']
8285 this.toolTip.render(Roo.get(document.body));
8287 this.toolTip.el.enableDisplayMode("block");
8289 Roo.get(document.body).on('click', function(){
8293 Roo.get(document.body).on('touchstart', function(){
8297 this.isApplied = true
8300 mask : function(form, target)
8304 this.target = target;
8306 if(!this.form.errorMask || !target.el){
8310 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8312 Roo.log(scrollable);
8314 var ot = this.target.el.calcOffsetsTo(scrollable);
8316 var scrollTo = ot[1] - this.form.maskOffset;
8318 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8320 scrollable.scrollTo('top', scrollTo);
8322 var box = this.target.el.getBox();
8324 var zIndex = Roo.bootstrap.Modal.zIndex++;
8327 this.maskEl.top.setStyle('position', 'absolute');
8328 this.maskEl.top.setStyle('z-index', zIndex);
8329 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8330 this.maskEl.top.setLeft(0);
8331 this.maskEl.top.setTop(0);
8332 this.maskEl.top.show();
8334 this.maskEl.left.setStyle('position', 'absolute');
8335 this.maskEl.left.setStyle('z-index', zIndex);
8336 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8337 this.maskEl.left.setLeft(0);
8338 this.maskEl.left.setTop(box.y - this.padding);
8339 this.maskEl.left.show();
8341 this.maskEl.bottom.setStyle('position', 'absolute');
8342 this.maskEl.bottom.setStyle('z-index', zIndex);
8343 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8344 this.maskEl.bottom.setLeft(0);
8345 this.maskEl.bottom.setTop(box.bottom + this.padding);
8346 this.maskEl.bottom.show();
8348 this.maskEl.right.setStyle('position', 'absolute');
8349 this.maskEl.right.setStyle('z-index', zIndex);
8350 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8351 this.maskEl.right.setLeft(box.right + this.padding);
8352 this.maskEl.right.setTop(box.y - this.padding);
8353 this.maskEl.right.show();
8355 this.toolTip.bindEl = this.target.el;
8357 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8359 var tip = this.target.blankText;
8361 if(this.target.getValue() !== '' ) {
8363 if (this.target.invalidText.length) {
8364 tip = this.target.invalidText;
8365 } else if (this.target.regexText.length){
8366 tip = this.target.regexText;
8370 this.toolTip.show(tip);
8372 this.intervalID = window.setInterval(function() {
8373 Roo.bootstrap.Form.popover.unmask();
8376 window.onwheel = function(){ return false;};
8378 (function(){ this.isMasked = true; }).defer(500, this);
8384 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8388 this.maskEl.top.setStyle('position', 'absolute');
8389 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8390 this.maskEl.top.hide();
8392 this.maskEl.left.setStyle('position', 'absolute');
8393 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8394 this.maskEl.left.hide();
8396 this.maskEl.bottom.setStyle('position', 'absolute');
8397 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8398 this.maskEl.bottom.hide();
8400 this.maskEl.right.setStyle('position', 'absolute');
8401 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8402 this.maskEl.right.hide();
8404 this.toolTip.hide();
8406 this.toolTip.el.hide();
8408 window.onwheel = function(){ return true;};
8410 if(this.intervalID){
8411 window.clearInterval(this.intervalID);
8412 this.intervalID = false;
8415 this.isMasked = false;
8425 * Ext JS Library 1.1.1
8426 * Copyright(c) 2006-2007, Ext JS, LLC.
8428 * Originally Released Under LGPL - original licence link has changed is not relivant.
8431 * <script type="text/javascript">
8434 * @class Roo.form.VTypes
8435 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8438 Roo.form.VTypes = function(){
8439 // closure these in so they are only created once.
8440 var alpha = /^[a-zA-Z_]+$/;
8441 var alphanum = /^[a-zA-Z0-9_]+$/;
8442 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8443 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8445 // All these messages and functions are configurable
8448 * The function used to validate email addresses
8449 * @param {String} value The email address
8451 'email' : function(v){
8452 return email.test(v);
8455 * The error text to display when the email validation function returns false
8458 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8460 * The keystroke filter mask to be applied on email input
8463 'emailMask' : /[a-z0-9_\.\-@]/i,
8466 * The function used to validate URLs
8467 * @param {String} value The URL
8469 'url' : function(v){
8473 * The error text to display when the url validation function returns false
8476 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8479 * The function used to validate alpha values
8480 * @param {String} value The value
8482 'alpha' : function(v){
8483 return alpha.test(v);
8486 * The error text to display when the alpha validation function returns false
8489 'alphaText' : 'This field should only contain letters and _',
8491 * The keystroke filter mask to be applied on alpha input
8494 'alphaMask' : /[a-z_]/i,
8497 * The function used to validate alphanumeric values
8498 * @param {String} value The value
8500 'alphanum' : function(v){
8501 return alphanum.test(v);
8504 * The error text to display when the alphanumeric validation function returns false
8507 'alphanumText' : 'This field should only contain letters, numbers and _',
8509 * The keystroke filter mask to be applied on alphanumeric input
8512 'alphanumMask' : /[a-z0-9_]/i
8522 * @class Roo.bootstrap.Input
8523 * @extends Roo.bootstrap.Component
8524 * Bootstrap Input class
8525 * @cfg {Boolean} disabled is it disabled
8526 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8527 * @cfg {String} name name of the input
8528 * @cfg {string} fieldLabel - the label associated
8529 * @cfg {string} placeholder - placeholder to put in text.
8530 * @cfg {string} before - input group add on before
8531 * @cfg {string} after - input group add on after
8532 * @cfg {string} size - (lg|sm) or leave empty..
8533 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8534 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8535 * @cfg {Number} md colspan out of 12 for computer-sized screens
8536 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8537 * @cfg {string} value default value of the input
8538 * @cfg {Number} labelWidth set the width of label
8539 * @cfg {Number} labellg set the width of label (1-12)
8540 * @cfg {Number} labelmd set the width of label (1-12)
8541 * @cfg {Number} labelsm set the width of label (1-12)
8542 * @cfg {Number} labelxs set the width of label (1-12)
8543 * @cfg {String} labelAlign (top|left)
8544 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8545 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8546 * @cfg {String} indicatorpos (left|right) default left
8547 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8548 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8550 * @cfg {String} align (left|center|right) Default left
8551 * @cfg {Boolean} forceFeedback (true|false) Default false
8554 * Create a new Input
8555 * @param {Object} config The config object
8558 Roo.bootstrap.Input = function(config){
8560 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8565 * Fires when this field receives input focus.
8566 * @param {Roo.form.Field} this
8571 * Fires when this field loses input focus.
8572 * @param {Roo.form.Field} this
8577 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8578 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8579 * @param {Roo.form.Field} this
8580 * @param {Roo.EventObject} e The event object
8585 * Fires just before the field blurs if the field value has changed.
8586 * @param {Roo.form.Field} this
8587 * @param {Mixed} newValue The new value
8588 * @param {Mixed} oldValue The original value
8593 * Fires after the field has been marked as invalid.
8594 * @param {Roo.form.Field} this
8595 * @param {String} msg The validation message
8600 * Fires after the field has been validated with no errors.
8601 * @param {Roo.form.Field} this
8606 * Fires after the key up
8607 * @param {Roo.form.Field} this
8608 * @param {Roo.EventObject} e The event Object
8614 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8616 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8617 automatic validation (defaults to "keyup").
8619 validationEvent : "keyup",
8621 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8623 validateOnBlur : true,
8625 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8627 validationDelay : 250,
8629 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8631 focusClass : "x-form-focus", // not needed???
8635 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8637 invalidClass : "has-warning",
8640 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8642 validClass : "has-success",
8645 * @cfg {Boolean} hasFeedback (true|false) default true
8650 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8652 invalidFeedbackClass : "glyphicon-warning-sign",
8655 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8657 validFeedbackClass : "glyphicon-ok",
8660 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8662 selectOnFocus : false,
8665 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8669 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8674 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8676 disableKeyFilter : false,
8679 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8683 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8687 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8689 blankText : "Please complete this mandatory field",
8692 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8696 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8698 maxLength : Number.MAX_VALUE,
8700 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8702 minLengthText : "The minimum length for this field is {0}",
8704 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8706 maxLengthText : "The maximum length for this field is {0}",
8710 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8711 * If available, this function will be called only after the basic validators all return true, and will be passed the
8712 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8716 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8717 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8718 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8722 * @cfg {String} regexText -- Depricated - use Invalid Text
8727 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8733 autocomplete: false,
8752 formatedValue : false,
8753 forceFeedback : false,
8755 indicatorpos : 'left',
8765 parentLabelAlign : function()
8768 while (parent.parent()) {
8769 parent = parent.parent();
8770 if (typeof(parent.labelAlign) !='undefined') {
8771 return parent.labelAlign;
8778 getAutoCreate : function()
8780 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8786 if(this.inputType != 'hidden'){
8787 cfg.cls = 'form-group' //input-group
8793 type : this.inputType,
8795 cls : 'form-control',
8796 placeholder : this.placeholder || '',
8797 autocomplete : this.autocomplete || 'new-password'
8800 if(this.capture.length){
8801 input.capture = this.capture;
8804 if(this.accept.length){
8805 input.accept = this.accept + "/*";
8809 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8812 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8813 input.maxLength = this.maxLength;
8816 if (this.disabled) {
8817 input.disabled=true;
8820 if (this.readOnly) {
8821 input.readonly=true;
8825 input.name = this.name;
8829 input.cls += ' input-' + this.size;
8833 ['xs','sm','md','lg'].map(function(size){
8834 if (settings[size]) {
8835 cfg.cls += ' col-' + size + '-' + settings[size];
8839 var inputblock = input;
8843 cls: 'glyphicon form-control-feedback'
8846 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8849 cls : 'has-feedback',
8857 if (this.before || this.after) {
8860 cls : 'input-group',
8864 if (this.before && typeof(this.before) == 'string') {
8866 inputblock.cn.push({
8868 cls : 'roo-input-before input-group-addon',
8872 if (this.before && typeof(this.before) == 'object') {
8873 this.before = Roo.factory(this.before);
8875 inputblock.cn.push({
8877 cls : 'roo-input-before input-group-' +
8878 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8882 inputblock.cn.push(input);
8884 if (this.after && typeof(this.after) == 'string') {
8885 inputblock.cn.push({
8887 cls : 'roo-input-after input-group-addon',
8891 if (this.after && typeof(this.after) == 'object') {
8892 this.after = Roo.factory(this.after);
8894 inputblock.cn.push({
8896 cls : 'roo-input-after input-group-' +
8897 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8901 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8902 inputblock.cls += ' has-feedback';
8903 inputblock.cn.push(feedback);
8907 if (align ==='left' && this.fieldLabel.length) {
8909 cfg.cls += ' roo-form-group-label-left';
8914 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8915 tooltip : 'This field is required'
8920 cls : 'control-label',
8921 html : this.fieldLabel
8932 var labelCfg = cfg.cn[1];
8933 var contentCfg = cfg.cn[2];
8935 if(this.indicatorpos == 'right'){
8940 cls : 'control-label',
8944 html : this.fieldLabel
8948 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8949 tooltip : 'This field is required'
8962 labelCfg = cfg.cn[0];
8963 contentCfg = cfg.cn[1];
8967 if(this.labelWidth > 12){
8968 labelCfg.style = "width: " + this.labelWidth + 'px';
8971 if(this.labelWidth < 13 && this.labelmd == 0){
8972 this.labelmd = this.labelWidth;
8975 if(this.labellg > 0){
8976 labelCfg.cls += ' col-lg-' + this.labellg;
8977 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8980 if(this.labelmd > 0){
8981 labelCfg.cls += ' col-md-' + this.labelmd;
8982 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8985 if(this.labelsm > 0){
8986 labelCfg.cls += ' col-sm-' + this.labelsm;
8987 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8990 if(this.labelxs > 0){
8991 labelCfg.cls += ' col-xs-' + this.labelxs;
8992 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8996 } else if ( this.fieldLabel.length) {
9001 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9002 tooltip : 'This field is required'
9006 //cls : 'input-group-addon',
9007 html : this.fieldLabel
9015 if(this.indicatorpos == 'right'){
9020 //cls : 'input-group-addon',
9021 html : this.fieldLabel
9026 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9027 tooltip : 'This field is required'
9047 if (this.parentType === 'Navbar' && this.parent().bar) {
9048 cfg.cls += ' navbar-form';
9051 if (this.parentType === 'NavGroup') {
9052 cfg.cls += ' navbar-form';
9060 * return the real input element.
9062 inputEl: function ()
9064 return this.el.select('input.form-control',true).first();
9067 tooltipEl : function()
9069 return this.inputEl();
9072 indicatorEl : function()
9074 var indicator = this.el.select('i.roo-required-indicator',true).first();
9084 setDisabled : function(v)
9086 var i = this.inputEl().dom;
9088 i.removeAttribute('disabled');
9092 i.setAttribute('disabled','true');
9094 initEvents : function()
9097 this.inputEl().on("keydown" , this.fireKey, this);
9098 this.inputEl().on("focus", this.onFocus, this);
9099 this.inputEl().on("blur", this.onBlur, this);
9101 this.inputEl().relayEvent('keyup', this);
9103 this.indicator = this.indicatorEl();
9106 this.indicator.addClass('invisible');
9109 // reference to original value for reset
9110 this.originalValue = this.getValue();
9111 //Roo.form.TextField.superclass.initEvents.call(this);
9112 if(this.validationEvent == 'keyup'){
9113 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9114 this.inputEl().on('keyup', this.filterValidation, this);
9116 else if(this.validationEvent !== false){
9117 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9120 if(this.selectOnFocus){
9121 this.on("focus", this.preFocus, this);
9124 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9125 this.inputEl().on("keypress", this.filterKeys, this);
9127 this.inputEl().relayEvent('keypress', this);
9130 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9131 this.el.on("click", this.autoSize, this);
9134 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9135 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9138 if (typeof(this.before) == 'object') {
9139 this.before.render(this.el.select('.roo-input-before',true).first());
9141 if (typeof(this.after) == 'object') {
9142 this.after.render(this.el.select('.roo-input-after',true).first());
9145 this.inputEl().on('change', this.onChange, this);
9148 filterValidation : function(e){
9149 if(!e.isNavKeyPress()){
9150 this.validationTask.delay(this.validationDelay);
9154 * Validates the field value
9155 * @return {Boolean} True if the value is valid, else false
9157 validate : function(){
9158 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9159 if(this.disabled || this.validateValue(this.getRawValue())){
9170 * Validates a value according to the field's validation rules and marks the field as invalid
9171 * if the validation fails
9172 * @param {Mixed} value The value to validate
9173 * @return {Boolean} True if the value is valid, else false
9175 validateValue : function(value)
9177 if(this.getVisibilityEl().hasClass('hidden')){
9181 if(value.length < 1) { // if it's blank
9182 if(this.allowBlank){
9188 if(value.length < this.minLength){
9191 if(value.length > this.maxLength){
9195 var vt = Roo.form.VTypes;
9196 if(!vt[this.vtype](value, this)){
9200 if(typeof this.validator == "function"){
9201 var msg = this.validator(value);
9205 if (typeof(msg) == 'string') {
9206 this.invalidText = msg;
9210 if(this.regex && !this.regex.test(value)){
9218 fireKey : function(e){
9219 //Roo.log('field ' + e.getKey());
9220 if(e.isNavKeyPress()){
9221 this.fireEvent("specialkey", this, e);
9224 focus : function (selectText){
9226 this.inputEl().focus();
9227 if(selectText === true){
9228 this.inputEl().dom.select();
9234 onFocus : function(){
9235 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9236 // this.el.addClass(this.focusClass);
9239 this.hasFocus = true;
9240 this.startValue = this.getValue();
9241 this.fireEvent("focus", this);
9245 beforeBlur : Roo.emptyFn,
9249 onBlur : function(){
9251 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9252 //this.el.removeClass(this.focusClass);
9254 this.hasFocus = false;
9255 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9258 var v = this.getValue();
9259 if(String(v) !== String(this.startValue)){
9260 this.fireEvent('change', this, v, this.startValue);
9262 this.fireEvent("blur", this);
9265 onChange : function(e)
9267 var v = this.getValue();
9268 if(String(v) !== String(this.startValue)){
9269 this.fireEvent('change', this, v, this.startValue);
9275 * Resets the current field value to the originally loaded value and clears any validation messages
9278 this.setValue(this.originalValue);
9282 * Returns the name of the field
9283 * @return {Mixed} name The name field
9285 getName: function(){
9289 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9290 * @return {Mixed} value The field value
9292 getValue : function(){
9294 var v = this.inputEl().getValue();
9299 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9300 * @return {Mixed} value The field value
9302 getRawValue : function(){
9303 var v = this.inputEl().getValue();
9309 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9310 * @param {Mixed} value The value to set
9312 setRawValue : function(v){
9313 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9316 selectText : function(start, end){
9317 var v = this.getRawValue();
9319 start = start === undefined ? 0 : start;
9320 end = end === undefined ? v.length : end;
9321 var d = this.inputEl().dom;
9322 if(d.setSelectionRange){
9323 d.setSelectionRange(start, end);
9324 }else if(d.createTextRange){
9325 var range = d.createTextRange();
9326 range.moveStart("character", start);
9327 range.moveEnd("character", v.length-end);
9334 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9335 * @param {Mixed} value The value to set
9337 setValue : function(v){
9340 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9346 processValue : function(value){
9347 if(this.stripCharsRe){
9348 var newValue = value.replace(this.stripCharsRe, '');
9349 if(newValue !== value){
9350 this.setRawValue(newValue);
9357 preFocus : function(){
9359 if(this.selectOnFocus){
9360 this.inputEl().dom.select();
9363 filterKeys : function(e){
9365 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9368 var c = e.getCharCode(), cc = String.fromCharCode(c);
9369 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9372 if(!this.maskRe.test(cc)){
9377 * Clear any invalid styles/messages for this field
9379 clearInvalid : function(){
9381 if(!this.el || this.preventMark){ // not rendered
9386 this.el.removeClass(this.invalidClass);
9388 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9390 var feedback = this.el.select('.form-control-feedback', true).first();
9393 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9398 this.fireEvent('valid', this);
9402 * Mark this field as valid
9404 markValid : function()
9406 if(!this.el || this.preventMark){ // not rendered...
9410 this.el.removeClass([this.invalidClass, this.validClass]);
9412 var feedback = this.el.select('.form-control-feedback', true).first();
9415 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9419 this.indicator.removeClass('visible');
9420 this.indicator.addClass('invisible');
9427 if(this.allowBlank && !this.getRawValue().length){
9431 this.el.addClass(this.validClass);
9433 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9435 var feedback = this.el.select('.form-control-feedback', true).first();
9438 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9439 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9444 this.fireEvent('valid', this);
9448 * Mark this field as invalid
9449 * @param {String} msg The validation message
9451 markInvalid : function(msg)
9453 if(!this.el || this.preventMark){ // not rendered
9457 this.el.removeClass([this.invalidClass, this.validClass]);
9459 var feedback = this.el.select('.form-control-feedback', true).first();
9462 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9469 if(this.allowBlank && !this.getRawValue().length){
9474 this.indicator.removeClass('invisible');
9475 this.indicator.addClass('visible');
9478 this.el.addClass(this.invalidClass);
9480 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9482 var feedback = this.el.select('.form-control-feedback', true).first();
9485 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9487 if(this.getValue().length || this.forceFeedback){
9488 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9495 this.fireEvent('invalid', this, msg);
9498 SafariOnKeyDown : function(event)
9500 // this is a workaround for a password hang bug on chrome/ webkit.
9501 if (this.inputEl().dom.type != 'password') {
9505 var isSelectAll = false;
9507 if(this.inputEl().dom.selectionEnd > 0){
9508 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9510 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9511 event.preventDefault();
9516 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9518 event.preventDefault();
9519 // this is very hacky as keydown always get's upper case.
9521 var cc = String.fromCharCode(event.getCharCode());
9522 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9526 adjustWidth : function(tag, w){
9527 tag = tag.toLowerCase();
9528 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9529 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9533 if(tag == 'textarea'){
9536 }else if(Roo.isOpera){
9540 if(tag == 'textarea'){
9548 setFieldLabel : function(v)
9555 var ar = this.el.select('label > span',true);
9557 if (ar.elements.length) {
9558 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9559 this.fieldLabel = v;
9563 var br = this.el.select('label',true);
9565 if(br.elements.length) {
9566 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9567 this.fieldLabel = v;
9571 Roo.log('Cannot Found any of label > span || label in input');
9575 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9576 this.fieldLabel = v;
9591 * @class Roo.bootstrap.TextArea
9592 * @extends Roo.bootstrap.Input
9593 * Bootstrap TextArea class
9594 * @cfg {Number} cols Specifies the visible width of a text area
9595 * @cfg {Number} rows Specifies the visible number of lines in a text area
9596 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9597 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9598 * @cfg {string} html text
9601 * Create a new TextArea
9602 * @param {Object} config The config object
9605 Roo.bootstrap.TextArea = function(config){
9606 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9610 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9620 getAutoCreate : function(){
9622 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9628 if(this.inputType != 'hidden'){
9629 cfg.cls = 'form-group' //input-group
9637 value : this.value || '',
9638 html: this.html || '',
9639 cls : 'form-control',
9640 placeholder : this.placeholder || ''
9644 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9645 input.maxLength = this.maxLength;
9649 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9653 input.cols = this.cols;
9656 if (this.readOnly) {
9657 input.readonly = true;
9661 input.name = this.name;
9665 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9669 ['xs','sm','md','lg'].map(function(size){
9670 if (settings[size]) {
9671 cfg.cls += ' col-' + size + '-' + settings[size];
9675 var inputblock = input;
9677 if(this.hasFeedback && !this.allowBlank){
9681 cls: 'glyphicon form-control-feedback'
9685 cls : 'has-feedback',
9694 if (this.before || this.after) {
9697 cls : 'input-group',
9701 inputblock.cn.push({
9703 cls : 'input-group-addon',
9708 inputblock.cn.push(input);
9710 if(this.hasFeedback && !this.allowBlank){
9711 inputblock.cls += ' has-feedback';
9712 inputblock.cn.push(feedback);
9716 inputblock.cn.push({
9718 cls : 'input-group-addon',
9725 if (align ==='left' && this.fieldLabel.length) {
9730 cls : 'control-label',
9731 html : this.fieldLabel
9742 if(this.labelWidth > 12){
9743 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9746 if(this.labelWidth < 13 && this.labelmd == 0){
9747 this.labelmd = this.labelWidth;
9750 if(this.labellg > 0){
9751 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9752 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9755 if(this.labelmd > 0){
9756 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9757 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9760 if(this.labelsm > 0){
9761 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9762 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9765 if(this.labelxs > 0){
9766 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9767 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9770 } else if ( this.fieldLabel.length) {
9775 //cls : 'input-group-addon',
9776 html : this.fieldLabel
9794 if (this.disabled) {
9795 input.disabled=true;
9802 * return the real textarea element.
9804 inputEl: function ()
9806 return this.el.select('textarea.form-control',true).first();
9810 * Clear any invalid styles/messages for this field
9812 clearInvalid : function()
9815 if(!this.el || this.preventMark){ // not rendered
9819 var label = this.el.select('label', true).first();
9820 var icon = this.el.select('i.fa-star', true).first();
9826 this.el.removeClass(this.invalidClass);
9828 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9830 var feedback = this.el.select('.form-control-feedback', true).first();
9833 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9838 this.fireEvent('valid', this);
9842 * Mark this field as valid
9844 markValid : function()
9846 if(!this.el || this.preventMark){ // not rendered
9850 this.el.removeClass([this.invalidClass, this.validClass]);
9852 var feedback = this.el.select('.form-control-feedback', true).first();
9855 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9858 if(this.disabled || this.allowBlank){
9862 var label = this.el.select('label', true).first();
9863 var icon = this.el.select('i.fa-star', true).first();
9869 this.el.addClass(this.validClass);
9871 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9873 var feedback = this.el.select('.form-control-feedback', true).first();
9876 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9877 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9882 this.fireEvent('valid', this);
9886 * Mark this field as invalid
9887 * @param {String} msg The validation message
9889 markInvalid : function(msg)
9891 if(!this.el || this.preventMark){ // not rendered
9895 this.el.removeClass([this.invalidClass, this.validClass]);
9897 var feedback = this.el.select('.form-control-feedback', true).first();
9900 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9903 if(this.disabled || this.allowBlank){
9907 var label = this.el.select('label', true).first();
9908 var icon = this.el.select('i.fa-star', true).first();
9910 if(!this.getValue().length && label && !icon){
9911 this.el.createChild({
9913 cls : 'text-danger fa fa-lg fa-star',
9914 tooltip : 'This field is required',
9915 style : 'margin-right:5px;'
9919 this.el.addClass(this.invalidClass);
9921 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9923 var feedback = this.el.select('.form-control-feedback', true).first();
9926 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9928 if(this.getValue().length || this.forceFeedback){
9929 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9936 this.fireEvent('invalid', this, msg);
9944 * trigger field - base class for combo..
9949 * @class Roo.bootstrap.TriggerField
9950 * @extends Roo.bootstrap.Input
9951 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9952 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9953 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9954 * for which you can provide a custom implementation. For example:
9956 var trigger = new Roo.bootstrap.TriggerField();
9957 trigger.onTriggerClick = myTriggerFn;
9958 trigger.applyTo('my-field');
9961 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9962 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9963 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9964 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9965 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9968 * Create a new TriggerField.
9969 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9970 * to the base TextField)
9972 Roo.bootstrap.TriggerField = function(config){
9973 this.mimicing = false;
9974 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9977 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9979 * @cfg {String} triggerClass A CSS class to apply to the trigger
9982 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9987 * @cfg {Boolean} removable (true|false) special filter default false
9991 /** @cfg {Boolean} grow @hide */
9992 /** @cfg {Number} growMin @hide */
9993 /** @cfg {Number} growMax @hide */
9999 autoSize: Roo.emptyFn,
10003 deferHeight : true,
10006 actionMode : 'wrap',
10011 getAutoCreate : function(){
10013 var align = this.labelAlign || this.parentLabelAlign();
10018 cls: 'form-group' //input-group
10025 type : this.inputType,
10026 cls : 'form-control',
10027 autocomplete: 'new-password',
10028 placeholder : this.placeholder || ''
10032 input.name = this.name;
10035 input.cls += ' input-' + this.size;
10038 if (this.disabled) {
10039 input.disabled=true;
10042 var inputblock = input;
10044 if(this.hasFeedback && !this.allowBlank){
10048 cls: 'glyphicon form-control-feedback'
10051 if(this.removable && !this.editable && !this.tickable){
10053 cls : 'has-feedback',
10059 cls : 'roo-combo-removable-btn close'
10066 cls : 'has-feedback',
10075 if(this.removable && !this.editable && !this.tickable){
10077 cls : 'roo-removable',
10083 cls : 'roo-combo-removable-btn close'
10090 if (this.before || this.after) {
10093 cls : 'input-group',
10097 inputblock.cn.push({
10099 cls : 'input-group-addon',
10104 inputblock.cn.push(input);
10106 if(this.hasFeedback && !this.allowBlank){
10107 inputblock.cls += ' has-feedback';
10108 inputblock.cn.push(feedback);
10112 inputblock.cn.push({
10114 cls : 'input-group-addon',
10127 cls: 'form-hidden-field'
10141 cls: 'form-hidden-field'
10145 cls: 'roo-select2-choices',
10149 cls: 'roo-select2-search-field',
10162 cls: 'roo-select2-container input-group',
10167 // cls: 'typeahead typeahead-long dropdown-menu',
10168 // style: 'display:none'
10173 if(!this.multiple && this.showToggleBtn){
10179 if (this.caret != false) {
10182 cls: 'fa fa-' + this.caret
10189 cls : 'input-group-addon btn dropdown-toggle',
10194 cls: 'combobox-clear',
10208 combobox.cls += ' roo-select2-container-multi';
10211 if (align ==='left' && this.fieldLabel.length) {
10213 cfg.cls += ' roo-form-group-label-left';
10218 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10219 tooltip : 'This field is required'
10224 cls : 'control-label',
10225 html : this.fieldLabel
10237 var labelCfg = cfg.cn[1];
10238 var contentCfg = cfg.cn[2];
10240 if(this.indicatorpos == 'right'){
10245 cls : 'control-label',
10249 html : this.fieldLabel
10253 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10254 tooltip : 'This field is required'
10267 labelCfg = cfg.cn[0];
10268 contentCfg = cfg.cn[1];
10271 if(this.labelWidth > 12){
10272 labelCfg.style = "width: " + this.labelWidth + 'px';
10275 if(this.labelWidth < 13 && this.labelmd == 0){
10276 this.labelmd = this.labelWidth;
10279 if(this.labellg > 0){
10280 labelCfg.cls += ' col-lg-' + this.labellg;
10281 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10284 if(this.labelmd > 0){
10285 labelCfg.cls += ' col-md-' + this.labelmd;
10286 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10289 if(this.labelsm > 0){
10290 labelCfg.cls += ' col-sm-' + this.labelsm;
10291 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10294 if(this.labelxs > 0){
10295 labelCfg.cls += ' col-xs-' + this.labelxs;
10296 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10299 } else if ( this.fieldLabel.length) {
10300 // Roo.log(" label");
10304 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10305 tooltip : 'This field is required'
10309 //cls : 'input-group-addon',
10310 html : this.fieldLabel
10318 if(this.indicatorpos == 'right'){
10326 html : this.fieldLabel
10330 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10331 tooltip : 'This field is required'
10344 // Roo.log(" no label && no align");
10351 ['xs','sm','md','lg'].map(function(size){
10352 if (settings[size]) {
10353 cfg.cls += ' col-' + size + '-' + settings[size];
10364 onResize : function(w, h){
10365 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10366 // if(typeof w == 'number'){
10367 // var x = w - this.trigger.getWidth();
10368 // this.inputEl().setWidth(this.adjustWidth('input', x));
10369 // this.trigger.setStyle('left', x+'px');
10374 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10377 getResizeEl : function(){
10378 return this.inputEl();
10382 getPositionEl : function(){
10383 return this.inputEl();
10387 alignErrorIcon : function(){
10388 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10392 initEvents : function(){
10396 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10397 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10398 if(!this.multiple && this.showToggleBtn){
10399 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10400 if(this.hideTrigger){
10401 this.trigger.setDisplayed(false);
10403 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10407 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10410 if(this.removable && !this.editable && !this.tickable){
10411 var close = this.closeTriggerEl();
10414 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10415 close.on('click', this.removeBtnClick, this, close);
10419 //this.trigger.addClassOnOver('x-form-trigger-over');
10420 //this.trigger.addClassOnClick('x-form-trigger-click');
10423 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10427 closeTriggerEl : function()
10429 var close = this.el.select('.roo-combo-removable-btn', true).first();
10430 return close ? close : false;
10433 removeBtnClick : function(e, h, el)
10435 e.preventDefault();
10437 if(this.fireEvent("remove", this) !== false){
10439 this.fireEvent("afterremove", this)
10443 createList : function()
10445 this.list = Roo.get(document.body).createChild({
10447 cls: 'typeahead typeahead-long dropdown-menu',
10448 style: 'display:none'
10451 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10456 initTrigger : function(){
10461 onDestroy : function(){
10463 this.trigger.removeAllListeners();
10464 // this.trigger.remove();
10467 // this.wrap.remove();
10469 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10473 onFocus : function(){
10474 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10476 if(!this.mimicing){
10477 this.wrap.addClass('x-trigger-wrap-focus');
10478 this.mimicing = true;
10479 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10480 if(this.monitorTab){
10481 this.el.on("keydown", this.checkTab, this);
10488 checkTab : function(e){
10489 if(e.getKey() == e.TAB){
10490 this.triggerBlur();
10495 onBlur : function(){
10500 mimicBlur : function(e, t){
10502 if(!this.wrap.contains(t) && this.validateBlur()){
10503 this.triggerBlur();
10509 triggerBlur : function(){
10510 this.mimicing = false;
10511 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10512 if(this.monitorTab){
10513 this.el.un("keydown", this.checkTab, this);
10515 //this.wrap.removeClass('x-trigger-wrap-focus');
10516 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10520 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10521 validateBlur : function(e, t){
10526 onDisable : function(){
10527 this.inputEl().dom.disabled = true;
10528 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10530 // this.wrap.addClass('x-item-disabled');
10535 onEnable : function(){
10536 this.inputEl().dom.disabled = false;
10537 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10539 // this.el.removeClass('x-item-disabled');
10544 onShow : function(){
10545 var ae = this.getActionEl();
10548 ae.dom.style.display = '';
10549 ae.dom.style.visibility = 'visible';
10555 onHide : function(){
10556 var ae = this.getActionEl();
10557 ae.dom.style.display = 'none';
10561 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10562 * by an implementing function.
10564 * @param {EventObject} e
10566 onTriggerClick : Roo.emptyFn
10570 * Ext JS Library 1.1.1
10571 * Copyright(c) 2006-2007, Ext JS, LLC.
10573 * Originally Released Under LGPL - original licence link has changed is not relivant.
10576 * <script type="text/javascript">
10581 * @class Roo.data.SortTypes
10583 * Defines the default sorting (casting?) comparison functions used when sorting data.
10585 Roo.data.SortTypes = {
10587 * Default sort that does nothing
10588 * @param {Mixed} s The value being converted
10589 * @return {Mixed} The comparison value
10591 none : function(s){
10596 * The regular expression used to strip tags
10600 stripTagsRE : /<\/?[^>]+>/gi,
10603 * Strips all HTML tags to sort on text only
10604 * @param {Mixed} s The value being converted
10605 * @return {String} The comparison value
10607 asText : function(s){
10608 return String(s).replace(this.stripTagsRE, "");
10612 * Strips all HTML tags to sort on text only - Case insensitive
10613 * @param {Mixed} s The value being converted
10614 * @return {String} The comparison value
10616 asUCText : function(s){
10617 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10621 * Case insensitive string
10622 * @param {Mixed} s The value being converted
10623 * @return {String} The comparison value
10625 asUCString : function(s) {
10626 return String(s).toUpperCase();
10631 * @param {Mixed} s The value being converted
10632 * @return {Number} The comparison value
10634 asDate : function(s) {
10638 if(s instanceof Date){
10639 return s.getTime();
10641 return Date.parse(String(s));
10646 * @param {Mixed} s The value being converted
10647 * @return {Float} The comparison value
10649 asFloat : function(s) {
10650 var val = parseFloat(String(s).replace(/,/g, ""));
10659 * @param {Mixed} s The value being converted
10660 * @return {Number} The comparison value
10662 asInt : function(s) {
10663 var val = parseInt(String(s).replace(/,/g, ""));
10671 * Ext JS Library 1.1.1
10672 * Copyright(c) 2006-2007, Ext JS, LLC.
10674 * Originally Released Under LGPL - original licence link has changed is not relivant.
10677 * <script type="text/javascript">
10681 * @class Roo.data.Record
10682 * Instances of this class encapsulate both record <em>definition</em> information, and record
10683 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10684 * to access Records cached in an {@link Roo.data.Store} object.<br>
10686 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10687 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10690 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10692 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10693 * {@link #create}. The parameters are the same.
10694 * @param {Array} data An associative Array of data values keyed by the field name.
10695 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10696 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10697 * not specified an integer id is generated.
10699 Roo.data.Record = function(data, id){
10700 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10705 * Generate a constructor for a specific record layout.
10706 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10707 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10708 * Each field definition object may contain the following properties: <ul>
10709 * <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,
10710 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10711 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10712 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10713 * is being used, then this is a string containing the javascript expression to reference the data relative to
10714 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10715 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10716 * this may be omitted.</p></li>
10717 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10718 * <ul><li>auto (Default, implies no conversion)</li>
10723 * <li>date</li></ul></p></li>
10724 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10725 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10726 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10727 * by the Reader into an object that will be stored in the Record. It is passed the
10728 * following parameters:<ul>
10729 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10731 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10733 * <br>usage:<br><pre><code>
10734 var TopicRecord = Roo.data.Record.create(
10735 {name: 'title', mapping: 'topic_title'},
10736 {name: 'author', mapping: 'username'},
10737 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10738 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10739 {name: 'lastPoster', mapping: 'user2'},
10740 {name: 'excerpt', mapping: 'post_text'}
10743 var myNewRecord = new TopicRecord({
10744 title: 'Do my job please',
10747 lastPost: new Date(),
10748 lastPoster: 'Animal',
10749 excerpt: 'No way dude!'
10751 myStore.add(myNewRecord);
10756 Roo.data.Record.create = function(o){
10757 var f = function(){
10758 f.superclass.constructor.apply(this, arguments);
10760 Roo.extend(f, Roo.data.Record);
10761 var p = f.prototype;
10762 p.fields = new Roo.util.MixedCollection(false, function(field){
10765 for(var i = 0, len = o.length; i < len; i++){
10766 p.fields.add(new Roo.data.Field(o[i]));
10768 f.getField = function(name){
10769 return p.fields.get(name);
10774 Roo.data.Record.AUTO_ID = 1000;
10775 Roo.data.Record.EDIT = 'edit';
10776 Roo.data.Record.REJECT = 'reject';
10777 Roo.data.Record.COMMIT = 'commit';
10779 Roo.data.Record.prototype = {
10781 * Readonly flag - true if this record has been modified.
10790 join : function(store){
10791 this.store = store;
10795 * Set the named field to the specified value.
10796 * @param {String} name The name of the field to set.
10797 * @param {Object} value The value to set the field to.
10799 set : function(name, value){
10800 if(this.data[name] == value){
10804 if(!this.modified){
10805 this.modified = {};
10807 if(typeof this.modified[name] == 'undefined'){
10808 this.modified[name] = this.data[name];
10810 this.data[name] = value;
10811 if(!this.editing && this.store){
10812 this.store.afterEdit(this);
10817 * Get the value of the named field.
10818 * @param {String} name The name of the field to get the value of.
10819 * @return {Object} The value of the field.
10821 get : function(name){
10822 return this.data[name];
10826 beginEdit : function(){
10827 this.editing = true;
10828 this.modified = {};
10832 cancelEdit : function(){
10833 this.editing = false;
10834 delete this.modified;
10838 endEdit : function(){
10839 this.editing = false;
10840 if(this.dirty && this.store){
10841 this.store.afterEdit(this);
10846 * Usually called by the {@link Roo.data.Store} which owns the Record.
10847 * Rejects all changes made to the Record since either creation, or the last commit operation.
10848 * Modified fields are reverted to their original values.
10850 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10851 * of reject operations.
10853 reject : function(){
10854 var m = this.modified;
10856 if(typeof m[n] != "function"){
10857 this.data[n] = m[n];
10860 this.dirty = false;
10861 delete this.modified;
10862 this.editing = false;
10864 this.store.afterReject(this);
10869 * Usually called by the {@link Roo.data.Store} which owns the Record.
10870 * Commits all changes made to the Record since either creation, or the last commit operation.
10872 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10873 * of commit operations.
10875 commit : function(){
10876 this.dirty = false;
10877 delete this.modified;
10878 this.editing = false;
10880 this.store.afterCommit(this);
10885 hasError : function(){
10886 return this.error != null;
10890 clearError : function(){
10895 * Creates a copy of this record.
10896 * @param {String} id (optional) A new record id if you don't want to use this record's id
10899 copy : function(newId) {
10900 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10904 * Ext JS Library 1.1.1
10905 * Copyright(c) 2006-2007, Ext JS, LLC.
10907 * Originally Released Under LGPL - original licence link has changed is not relivant.
10910 * <script type="text/javascript">
10916 * @class Roo.data.Store
10917 * @extends Roo.util.Observable
10918 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10919 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10921 * 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
10922 * has no knowledge of the format of the data returned by the Proxy.<br>
10924 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10925 * instances from the data object. These records are cached and made available through accessor functions.
10927 * Creates a new Store.
10928 * @param {Object} config A config object containing the objects needed for the Store to access data,
10929 * and read the data into Records.
10931 Roo.data.Store = function(config){
10932 this.data = new Roo.util.MixedCollection(false);
10933 this.data.getKey = function(o){
10936 this.baseParams = {};
10938 this.paramNames = {
10943 "multisort" : "_multisort"
10946 if(config && config.data){
10947 this.inlineData = config.data;
10948 delete config.data;
10951 Roo.apply(this, config);
10953 if(this.reader){ // reader passed
10954 this.reader = Roo.factory(this.reader, Roo.data);
10955 this.reader.xmodule = this.xmodule || false;
10956 if(!this.recordType){
10957 this.recordType = this.reader.recordType;
10959 if(this.reader.onMetaChange){
10960 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10964 if(this.recordType){
10965 this.fields = this.recordType.prototype.fields;
10967 this.modified = [];
10971 * @event datachanged
10972 * Fires when the data cache has changed, and a widget which is using this Store
10973 * as a Record cache should refresh its view.
10974 * @param {Store} this
10976 datachanged : true,
10978 * @event metachange
10979 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10980 * @param {Store} this
10981 * @param {Object} meta The JSON metadata
10986 * Fires when Records have been added to the Store
10987 * @param {Store} this
10988 * @param {Roo.data.Record[]} records The array of Records added
10989 * @param {Number} index The index at which the record(s) were added
10994 * Fires when a Record has been removed from the Store
10995 * @param {Store} this
10996 * @param {Roo.data.Record} record The Record that was removed
10997 * @param {Number} index The index at which the record was removed
11002 * Fires when a Record has been updated
11003 * @param {Store} this
11004 * @param {Roo.data.Record} record The Record that was updated
11005 * @param {String} operation The update operation being performed. Value may be one of:
11007 Roo.data.Record.EDIT
11008 Roo.data.Record.REJECT
11009 Roo.data.Record.COMMIT
11015 * Fires when the data cache has been cleared.
11016 * @param {Store} this
11020 * @event beforeload
11021 * Fires before a request is made for a new data object. If the beforeload handler returns false
11022 * the load action will be canceled.
11023 * @param {Store} this
11024 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11028 * @event beforeloadadd
11029 * Fires after a new set of Records has been loaded.
11030 * @param {Store} this
11031 * @param {Roo.data.Record[]} records The Records that were loaded
11032 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11034 beforeloadadd : true,
11037 * Fires after a new set of Records has been loaded, before they are added to the store.
11038 * @param {Store} this
11039 * @param {Roo.data.Record[]} records The Records that were loaded
11040 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11041 * @params {Object} return from reader
11045 * @event loadexception
11046 * Fires if an exception occurs in the Proxy during loading.
11047 * Called with the signature of the Proxy's "loadexception" event.
11048 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11051 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11052 * @param {Object} load options
11053 * @param {Object} jsonData from your request (normally this contains the Exception)
11055 loadexception : true
11059 this.proxy = Roo.factory(this.proxy, Roo.data);
11060 this.proxy.xmodule = this.xmodule || false;
11061 this.relayEvents(this.proxy, ["loadexception"]);
11063 this.sortToggle = {};
11064 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11066 Roo.data.Store.superclass.constructor.call(this);
11068 if(this.inlineData){
11069 this.loadData(this.inlineData);
11070 delete this.inlineData;
11074 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11076 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11077 * without a remote query - used by combo/forms at present.
11081 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11084 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11087 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11088 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11091 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11092 * on any HTTP request
11095 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11098 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11102 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11103 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11105 remoteSort : false,
11108 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11109 * loaded or when a record is removed. (defaults to false).
11111 pruneModifiedRecords : false,
11114 lastOptions : null,
11117 * Add Records to the Store and fires the add event.
11118 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11120 add : function(records){
11121 records = [].concat(records);
11122 for(var i = 0, len = records.length; i < len; i++){
11123 records[i].join(this);
11125 var index = this.data.length;
11126 this.data.addAll(records);
11127 this.fireEvent("add", this, records, index);
11131 * Remove a Record from the Store and fires the remove event.
11132 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11134 remove : function(record){
11135 var index = this.data.indexOf(record);
11136 this.data.removeAt(index);
11138 if(this.pruneModifiedRecords){
11139 this.modified.remove(record);
11141 this.fireEvent("remove", this, record, index);
11145 * Remove all Records from the Store and fires the clear event.
11147 removeAll : function(){
11149 if(this.pruneModifiedRecords){
11150 this.modified = [];
11152 this.fireEvent("clear", this);
11156 * Inserts Records to the Store at the given index and fires the add event.
11157 * @param {Number} index The start index at which to insert the passed Records.
11158 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11160 insert : function(index, records){
11161 records = [].concat(records);
11162 for(var i = 0, len = records.length; i < len; i++){
11163 this.data.insert(index, records[i]);
11164 records[i].join(this);
11166 this.fireEvent("add", this, records, index);
11170 * Get the index within the cache of the passed Record.
11171 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11172 * @return {Number} The index of the passed Record. Returns -1 if not found.
11174 indexOf : function(record){
11175 return this.data.indexOf(record);
11179 * Get the index within the cache of the Record with the passed id.
11180 * @param {String} id The id of the Record to find.
11181 * @return {Number} The index of the Record. Returns -1 if not found.
11183 indexOfId : function(id){
11184 return this.data.indexOfKey(id);
11188 * Get the Record with the specified id.
11189 * @param {String} id The id of the Record to find.
11190 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11192 getById : function(id){
11193 return this.data.key(id);
11197 * Get the Record at the specified index.
11198 * @param {Number} index The index of the Record to find.
11199 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11201 getAt : function(index){
11202 return this.data.itemAt(index);
11206 * Returns a range of Records between specified indices.
11207 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11208 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11209 * @return {Roo.data.Record[]} An array of Records
11211 getRange : function(start, end){
11212 return this.data.getRange(start, end);
11216 storeOptions : function(o){
11217 o = Roo.apply({}, o);
11220 this.lastOptions = o;
11224 * Loads the Record cache from the configured Proxy using the configured Reader.
11226 * If using remote paging, then the first load call must specify the <em>start</em>
11227 * and <em>limit</em> properties in the options.params property to establish the initial
11228 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11230 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11231 * and this call will return before the new data has been loaded. Perform any post-processing
11232 * in a callback function, or in a "load" event handler.</strong>
11234 * @param {Object} options An object containing properties which control loading options:<ul>
11235 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11236 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11237 * passed the following arguments:<ul>
11238 * <li>r : Roo.data.Record[]</li>
11239 * <li>options: Options object from the load call</li>
11240 * <li>success: Boolean success indicator</li></ul></li>
11241 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11242 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11245 load : function(options){
11246 options = options || {};
11247 if(this.fireEvent("beforeload", this, options) !== false){
11248 this.storeOptions(options);
11249 var p = Roo.apply(options.params || {}, this.baseParams);
11250 // if meta was not loaded from remote source.. try requesting it.
11251 if (!this.reader.metaFromRemote) {
11252 p._requestMeta = 1;
11254 if(this.sortInfo && this.remoteSort){
11255 var pn = this.paramNames;
11256 p[pn["sort"]] = this.sortInfo.field;
11257 p[pn["dir"]] = this.sortInfo.direction;
11259 if (this.multiSort) {
11260 var pn = this.paramNames;
11261 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11264 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11269 * Reloads the Record cache from the configured Proxy using the configured Reader and
11270 * the options from the last load operation performed.
11271 * @param {Object} options (optional) An object containing properties which may override the options
11272 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11273 * the most recently used options are reused).
11275 reload : function(options){
11276 this.load(Roo.applyIf(options||{}, this.lastOptions));
11280 // Called as a callback by the Reader during a load operation.
11281 loadRecords : function(o, options, success){
11282 if(!o || success === false){
11283 if(success !== false){
11284 this.fireEvent("load", this, [], options, o);
11286 if(options.callback){
11287 options.callback.call(options.scope || this, [], options, false);
11291 // if data returned failure - throw an exception.
11292 if (o.success === false) {
11293 // show a message if no listener is registered.
11294 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11295 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11297 // loadmask wil be hooked into this..
11298 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11301 var r = o.records, t = o.totalRecords || r.length;
11303 this.fireEvent("beforeloadadd", this, r, options, o);
11305 if(!options || options.add !== true){
11306 if(this.pruneModifiedRecords){
11307 this.modified = [];
11309 for(var i = 0, len = r.length; i < len; i++){
11313 this.data = this.snapshot;
11314 delete this.snapshot;
11317 this.data.addAll(r);
11318 this.totalLength = t;
11320 this.fireEvent("datachanged", this);
11322 this.totalLength = Math.max(t, this.data.length+r.length);
11326 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11328 var e = new Roo.data.Record({});
11330 e.set(this.parent.displayField, this.parent.emptyTitle);
11331 e.set(this.parent.valueField, '');
11336 this.fireEvent("load", this, r, options, o);
11337 if(options.callback){
11338 options.callback.call(options.scope || this, r, options, true);
11344 * Loads data from a passed data block. A Reader which understands the format of the data
11345 * must have been configured in the constructor.
11346 * @param {Object} data The data block from which to read the Records. The format of the data expected
11347 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11348 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11350 loadData : function(o, append){
11351 var r = this.reader.readRecords(o);
11352 this.loadRecords(r, {add: append}, true);
11356 * Gets the number of cached records.
11358 * <em>If using paging, this may not be the total size of the dataset. If the data object
11359 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11360 * the data set size</em>
11362 getCount : function(){
11363 return this.data.length || 0;
11367 * Gets the total number of records in the dataset as returned by the server.
11369 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11370 * the dataset size</em>
11372 getTotalCount : function(){
11373 return this.totalLength || 0;
11377 * Returns the sort state of the Store as an object with two properties:
11379 field {String} The name of the field by which the Records are sorted
11380 direction {String} The sort order, "ASC" or "DESC"
11383 getSortState : function(){
11384 return this.sortInfo;
11388 applySort : function(){
11389 if(this.sortInfo && !this.remoteSort){
11390 var s = this.sortInfo, f = s.field;
11391 var st = this.fields.get(f).sortType;
11392 var fn = function(r1, r2){
11393 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11394 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11396 this.data.sort(s.direction, fn);
11397 if(this.snapshot && this.snapshot != this.data){
11398 this.snapshot.sort(s.direction, fn);
11404 * Sets the default sort column and order to be used by the next load operation.
11405 * @param {String} fieldName The name of the field to sort by.
11406 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11408 setDefaultSort : function(field, dir){
11409 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11413 * Sort the Records.
11414 * If remote sorting is used, the sort is performed on the server, and the cache is
11415 * reloaded. If local sorting is used, the cache is sorted internally.
11416 * @param {String} fieldName The name of the field to sort by.
11417 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11419 sort : function(fieldName, dir){
11420 var f = this.fields.get(fieldName);
11422 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11424 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11425 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11430 this.sortToggle[f.name] = dir;
11431 this.sortInfo = {field: f.name, direction: dir};
11432 if(!this.remoteSort){
11434 this.fireEvent("datachanged", this);
11436 this.load(this.lastOptions);
11441 * Calls the specified function for each of the Records in the cache.
11442 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11443 * Returning <em>false</em> aborts and exits the iteration.
11444 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11446 each : function(fn, scope){
11447 this.data.each(fn, scope);
11451 * Gets all records modified since the last commit. Modified records are persisted across load operations
11452 * (e.g., during paging).
11453 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11455 getModifiedRecords : function(){
11456 return this.modified;
11460 createFilterFn : function(property, value, anyMatch){
11461 if(!value.exec){ // not a regex
11462 value = String(value);
11463 if(value.length == 0){
11466 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11468 return function(r){
11469 return value.test(r.data[property]);
11474 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11475 * @param {String} property A field on your records
11476 * @param {Number} start The record index to start at (defaults to 0)
11477 * @param {Number} end The last record index to include (defaults to length - 1)
11478 * @return {Number} The sum
11480 sum : function(property, start, end){
11481 var rs = this.data.items, v = 0;
11482 start = start || 0;
11483 end = (end || end === 0) ? end : rs.length-1;
11485 for(var i = start; i <= end; i++){
11486 v += (rs[i].data[property] || 0);
11492 * Filter the records by a specified property.
11493 * @param {String} field A field on your records
11494 * @param {String/RegExp} value Either a string that the field
11495 * should start with or a RegExp to test against the field
11496 * @param {Boolean} anyMatch True to match any part not just the beginning
11498 filter : function(property, value, anyMatch){
11499 var fn = this.createFilterFn(property, value, anyMatch);
11500 return fn ? this.filterBy(fn) : this.clearFilter();
11504 * Filter by a function. The specified function will be called with each
11505 * record in this data source. If the function returns true the record is included,
11506 * otherwise it is filtered.
11507 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11508 * @param {Object} scope (optional) The scope of the function (defaults to this)
11510 filterBy : function(fn, scope){
11511 this.snapshot = this.snapshot || this.data;
11512 this.data = this.queryBy(fn, scope||this);
11513 this.fireEvent("datachanged", this);
11517 * Query the records by a specified property.
11518 * @param {String} field A field on your records
11519 * @param {String/RegExp} value Either a string that the field
11520 * should start with or a RegExp to test against the field
11521 * @param {Boolean} anyMatch True to match any part not just the beginning
11522 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11524 query : function(property, value, anyMatch){
11525 var fn = this.createFilterFn(property, value, anyMatch);
11526 return fn ? this.queryBy(fn) : this.data.clone();
11530 * Query by a function. The specified function will be called with each
11531 * record in this data source. If the function returns true the record is included
11533 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11534 * @param {Object} scope (optional) The scope of the function (defaults to this)
11535 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11537 queryBy : function(fn, scope){
11538 var data = this.snapshot || this.data;
11539 return data.filterBy(fn, scope||this);
11543 * Collects unique values for a particular dataIndex from this store.
11544 * @param {String} dataIndex The property to collect
11545 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11546 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11547 * @return {Array} An array of the unique values
11549 collect : function(dataIndex, allowNull, bypassFilter){
11550 var d = (bypassFilter === true && this.snapshot) ?
11551 this.snapshot.items : this.data.items;
11552 var v, sv, r = [], l = {};
11553 for(var i = 0, len = d.length; i < len; i++){
11554 v = d[i].data[dataIndex];
11556 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11565 * Revert to a view of the Record cache with no filtering applied.
11566 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11568 clearFilter : function(suppressEvent){
11569 if(this.snapshot && this.snapshot != this.data){
11570 this.data = this.snapshot;
11571 delete this.snapshot;
11572 if(suppressEvent !== true){
11573 this.fireEvent("datachanged", this);
11579 afterEdit : function(record){
11580 if(this.modified.indexOf(record) == -1){
11581 this.modified.push(record);
11583 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11587 afterReject : function(record){
11588 this.modified.remove(record);
11589 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11593 afterCommit : function(record){
11594 this.modified.remove(record);
11595 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11599 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11600 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11602 commitChanges : function(){
11603 var m = this.modified.slice(0);
11604 this.modified = [];
11605 for(var i = 0, len = m.length; i < len; i++){
11611 * Cancel outstanding changes on all changed records.
11613 rejectChanges : function(){
11614 var m = this.modified.slice(0);
11615 this.modified = [];
11616 for(var i = 0, len = m.length; i < len; i++){
11621 onMetaChange : function(meta, rtype, o){
11622 this.recordType = rtype;
11623 this.fields = rtype.prototype.fields;
11624 delete this.snapshot;
11625 this.sortInfo = meta.sortInfo || this.sortInfo;
11626 this.modified = [];
11627 this.fireEvent('metachange', this, this.reader.meta);
11630 moveIndex : function(data, type)
11632 var index = this.indexOf(data);
11634 var newIndex = index + type;
11638 this.insert(newIndex, data);
11643 * Ext JS Library 1.1.1
11644 * Copyright(c) 2006-2007, Ext JS, LLC.
11646 * Originally Released Under LGPL - original licence link has changed is not relivant.
11649 * <script type="text/javascript">
11653 * @class Roo.data.SimpleStore
11654 * @extends Roo.data.Store
11655 * Small helper class to make creating Stores from Array data easier.
11656 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11657 * @cfg {Array} fields An array of field definition objects, or field name strings.
11658 * @cfg {Array} data The multi-dimensional array of data
11660 * @param {Object} config
11662 Roo.data.SimpleStore = function(config){
11663 Roo.data.SimpleStore.superclass.constructor.call(this, {
11665 reader: new Roo.data.ArrayReader({
11668 Roo.data.Record.create(config.fields)
11670 proxy : new Roo.data.MemoryProxy(config.data)
11674 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11676 * Ext JS Library 1.1.1
11677 * Copyright(c) 2006-2007, Ext JS, LLC.
11679 * Originally Released Under LGPL - original licence link has changed is not relivant.
11682 * <script type="text/javascript">
11687 * @extends Roo.data.Store
11688 * @class Roo.data.JsonStore
11689 * Small helper class to make creating Stores for JSON data easier. <br/>
11691 var store = new Roo.data.JsonStore({
11692 url: 'get-images.php',
11694 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11697 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11698 * JsonReader and HttpProxy (unless inline data is provided).</b>
11699 * @cfg {Array} fields An array of field definition objects, or field name strings.
11701 * @param {Object} config
11703 Roo.data.JsonStore = function(c){
11704 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11705 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11706 reader: new Roo.data.JsonReader(c, c.fields)
11709 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11711 * Ext JS Library 1.1.1
11712 * Copyright(c) 2006-2007, Ext JS, LLC.
11714 * Originally Released Under LGPL - original licence link has changed is not relivant.
11717 * <script type="text/javascript">
11721 Roo.data.Field = function(config){
11722 if(typeof config == "string"){
11723 config = {name: config};
11725 Roo.apply(this, config);
11728 this.type = "auto";
11731 var st = Roo.data.SortTypes;
11732 // named sortTypes are supported, here we look them up
11733 if(typeof this.sortType == "string"){
11734 this.sortType = st[this.sortType];
11737 // set default sortType for strings and dates
11738 if(!this.sortType){
11741 this.sortType = st.asUCString;
11744 this.sortType = st.asDate;
11747 this.sortType = st.none;
11752 var stripRe = /[\$,%]/g;
11754 // prebuilt conversion function for this field, instead of
11755 // switching every time we're reading a value
11757 var cv, dateFormat = this.dateFormat;
11762 cv = function(v){ return v; };
11765 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11769 return v !== undefined && v !== null && v !== '' ?
11770 parseInt(String(v).replace(stripRe, ""), 10) : '';
11775 return v !== undefined && v !== null && v !== '' ?
11776 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11781 cv = function(v){ return v === true || v === "true" || v == 1; };
11788 if(v instanceof Date){
11792 if(dateFormat == "timestamp"){
11793 return new Date(v*1000);
11795 return Date.parseDate(v, dateFormat);
11797 var parsed = Date.parse(v);
11798 return parsed ? new Date(parsed) : null;
11807 Roo.data.Field.prototype = {
11815 * Ext JS Library 1.1.1
11816 * Copyright(c) 2006-2007, Ext JS, LLC.
11818 * Originally Released Under LGPL - original licence link has changed is not relivant.
11821 * <script type="text/javascript">
11824 // Base class for reading structured data from a data source. This class is intended to be
11825 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11828 * @class Roo.data.DataReader
11829 * Base class for reading structured data from a data source. This class is intended to be
11830 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11833 Roo.data.DataReader = function(meta, recordType){
11837 this.recordType = recordType instanceof Array ?
11838 Roo.data.Record.create(recordType) : recordType;
11841 Roo.data.DataReader.prototype = {
11843 * Create an empty record
11844 * @param {Object} data (optional) - overlay some values
11845 * @return {Roo.data.Record} record created.
11847 newRow : function(d) {
11849 this.recordType.prototype.fields.each(function(c) {
11851 case 'int' : da[c.name] = 0; break;
11852 case 'date' : da[c.name] = new Date(); break;
11853 case 'float' : da[c.name] = 0.0; break;
11854 case 'boolean' : da[c.name] = false; break;
11855 default : da[c.name] = ""; break;
11859 return new this.recordType(Roo.apply(da, d));
11864 * Ext JS Library 1.1.1
11865 * Copyright(c) 2006-2007, Ext JS, LLC.
11867 * Originally Released Under LGPL - original licence link has changed is not relivant.
11870 * <script type="text/javascript">
11874 * @class Roo.data.DataProxy
11875 * @extends Roo.data.Observable
11876 * This class is an abstract base class for implementations which provide retrieval of
11877 * unformatted data objects.<br>
11879 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11880 * (of the appropriate type which knows how to parse the data object) to provide a block of
11881 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11883 * Custom implementations must implement the load method as described in
11884 * {@link Roo.data.HttpProxy#load}.
11886 Roo.data.DataProxy = function(){
11889 * @event beforeload
11890 * Fires before a network request is made to retrieve a data object.
11891 * @param {Object} This DataProxy object.
11892 * @param {Object} params The params parameter to the load function.
11897 * Fires before the load method's callback is called.
11898 * @param {Object} This DataProxy object.
11899 * @param {Object} o The data object.
11900 * @param {Object} arg The callback argument object passed to the load function.
11904 * @event loadexception
11905 * Fires if an Exception occurs during data retrieval.
11906 * @param {Object} This DataProxy object.
11907 * @param {Object} o The data object.
11908 * @param {Object} arg The callback argument object passed to the load function.
11909 * @param {Object} e The Exception.
11911 loadexception : true
11913 Roo.data.DataProxy.superclass.constructor.call(this);
11916 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11919 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11923 * Ext JS Library 1.1.1
11924 * Copyright(c) 2006-2007, Ext JS, LLC.
11926 * Originally Released Under LGPL - original licence link has changed is not relivant.
11929 * <script type="text/javascript">
11932 * @class Roo.data.MemoryProxy
11933 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11934 * to the Reader when its load method is called.
11936 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11938 Roo.data.MemoryProxy = function(data){
11942 Roo.data.MemoryProxy.superclass.constructor.call(this);
11946 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11949 * Load data from the requested source (in this case an in-memory
11950 * data object passed to the constructor), read the data object into
11951 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11952 * process that block using the passed callback.
11953 * @param {Object} params This parameter is not used by the MemoryProxy class.
11954 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11955 * object into a block of Roo.data.Records.
11956 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11957 * The function must be passed <ul>
11958 * <li>The Record block object</li>
11959 * <li>The "arg" argument from the load function</li>
11960 * <li>A boolean success indicator</li>
11962 * @param {Object} scope The scope in which to call the callback
11963 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11965 load : function(params, reader, callback, scope, arg){
11966 params = params || {};
11969 result = reader.readRecords(this.data);
11971 this.fireEvent("loadexception", this, arg, null, e);
11972 callback.call(scope, null, arg, false);
11975 callback.call(scope, result, arg, true);
11979 update : function(params, records){
11984 * Ext JS Library 1.1.1
11985 * Copyright(c) 2006-2007, Ext JS, LLC.
11987 * Originally Released Under LGPL - original licence link has changed is not relivant.
11990 * <script type="text/javascript">
11993 * @class Roo.data.HttpProxy
11994 * @extends Roo.data.DataProxy
11995 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11996 * configured to reference a certain URL.<br><br>
11998 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11999 * from which the running page was served.<br><br>
12001 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12003 * Be aware that to enable the browser to parse an XML document, the server must set
12004 * the Content-Type header in the HTTP response to "text/xml".
12006 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12007 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12008 * will be used to make the request.
12010 Roo.data.HttpProxy = function(conn){
12011 Roo.data.HttpProxy.superclass.constructor.call(this);
12012 // is conn a conn config or a real conn?
12014 this.useAjax = !conn || !conn.events;
12018 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12019 // thse are take from connection...
12022 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12025 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12026 * extra parameters to each request made by this object. (defaults to undefined)
12029 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12030 * to each request made by this object. (defaults to undefined)
12033 * @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)
12036 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12039 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12045 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12049 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12050 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12051 * a finer-grained basis than the DataProxy events.
12053 getConnection : function(){
12054 return this.useAjax ? Roo.Ajax : this.conn;
12058 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12059 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12060 * process that block using the passed callback.
12061 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12062 * for the request to the remote server.
12063 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12064 * object into a block of Roo.data.Records.
12065 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12066 * The function must be passed <ul>
12067 * <li>The Record block object</li>
12068 * <li>The "arg" argument from the load function</li>
12069 * <li>A boolean success indicator</li>
12071 * @param {Object} scope The scope in which to call the callback
12072 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12074 load : function(params, reader, callback, scope, arg){
12075 if(this.fireEvent("beforeload", this, params) !== false){
12077 params : params || {},
12079 callback : callback,
12084 callback : this.loadResponse,
12088 Roo.applyIf(o, this.conn);
12089 if(this.activeRequest){
12090 Roo.Ajax.abort(this.activeRequest);
12092 this.activeRequest = Roo.Ajax.request(o);
12094 this.conn.request(o);
12097 callback.call(scope||this, null, arg, false);
12102 loadResponse : function(o, success, response){
12103 delete this.activeRequest;
12105 this.fireEvent("loadexception", this, o, response);
12106 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12111 result = o.reader.read(response);
12113 this.fireEvent("loadexception", this, o, response, e);
12114 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12118 this.fireEvent("load", this, o, o.request.arg);
12119 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12123 update : function(dataSet){
12128 updateResponse : function(dataSet){
12133 * Ext JS Library 1.1.1
12134 * Copyright(c) 2006-2007, Ext JS, LLC.
12136 * Originally Released Under LGPL - original licence link has changed is not relivant.
12139 * <script type="text/javascript">
12143 * @class Roo.data.ScriptTagProxy
12144 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12145 * other than the originating domain of the running page.<br><br>
12147 * <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
12148 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12150 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12151 * source code that is used as the source inside a <script> tag.<br><br>
12153 * In order for the browser to process the returned data, the server must wrap the data object
12154 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12155 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12156 * depending on whether the callback name was passed:
12159 boolean scriptTag = false;
12160 String cb = request.getParameter("callback");
12163 response.setContentType("text/javascript");
12165 response.setContentType("application/x-json");
12167 Writer out = response.getWriter();
12169 out.write(cb + "(");
12171 out.print(dataBlock.toJsonString());
12178 * @param {Object} config A configuration object.
12180 Roo.data.ScriptTagProxy = function(config){
12181 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12182 Roo.apply(this, config);
12183 this.head = document.getElementsByTagName("head")[0];
12186 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12188 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12190 * @cfg {String} url The URL from which to request the data object.
12193 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12197 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12198 * the server the name of the callback function set up by the load call to process the returned data object.
12199 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12200 * javascript output which calls this named function passing the data object as its only parameter.
12202 callbackParam : "callback",
12204 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12205 * name to the request.
12210 * Load data from the configured URL, read the data object into
12211 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12212 * process that block using the passed callback.
12213 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12214 * for the request to the remote server.
12215 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12216 * object into a block of Roo.data.Records.
12217 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12218 * The function must be passed <ul>
12219 * <li>The Record block object</li>
12220 * <li>The "arg" argument from the load function</li>
12221 * <li>A boolean success indicator</li>
12223 * @param {Object} scope The scope in which to call the callback
12224 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12226 load : function(params, reader, callback, scope, arg){
12227 if(this.fireEvent("beforeload", this, params) !== false){
12229 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12231 var url = this.url;
12232 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12234 url += "&_dc=" + (new Date().getTime());
12236 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12239 cb : "stcCallback"+transId,
12240 scriptId : "stcScript"+transId,
12244 callback : callback,
12250 window[trans.cb] = function(o){
12251 conn.handleResponse(o, trans);
12254 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12256 if(this.autoAbort !== false){
12260 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12262 var script = document.createElement("script");
12263 script.setAttribute("src", url);
12264 script.setAttribute("type", "text/javascript");
12265 script.setAttribute("id", trans.scriptId);
12266 this.head.appendChild(script);
12268 this.trans = trans;
12270 callback.call(scope||this, null, arg, false);
12275 isLoading : function(){
12276 return this.trans ? true : false;
12280 * Abort the current server request.
12282 abort : function(){
12283 if(this.isLoading()){
12284 this.destroyTrans(this.trans);
12289 destroyTrans : function(trans, isLoaded){
12290 this.head.removeChild(document.getElementById(trans.scriptId));
12291 clearTimeout(trans.timeoutId);
12293 window[trans.cb] = undefined;
12295 delete window[trans.cb];
12298 // if hasn't been loaded, wait for load to remove it to prevent script error
12299 window[trans.cb] = function(){
12300 window[trans.cb] = undefined;
12302 delete window[trans.cb];
12309 handleResponse : function(o, trans){
12310 this.trans = false;
12311 this.destroyTrans(trans, true);
12314 result = trans.reader.readRecords(o);
12316 this.fireEvent("loadexception", this, o, trans.arg, e);
12317 trans.callback.call(trans.scope||window, null, trans.arg, false);
12320 this.fireEvent("load", this, o, trans.arg);
12321 trans.callback.call(trans.scope||window, result, trans.arg, true);
12325 handleFailure : function(trans){
12326 this.trans = false;
12327 this.destroyTrans(trans, false);
12328 this.fireEvent("loadexception", this, null, trans.arg);
12329 trans.callback.call(trans.scope||window, null, trans.arg, false);
12333 * Ext JS Library 1.1.1
12334 * Copyright(c) 2006-2007, Ext JS, LLC.
12336 * Originally Released Under LGPL - original licence link has changed is not relivant.
12339 * <script type="text/javascript">
12343 * @class Roo.data.JsonReader
12344 * @extends Roo.data.DataReader
12345 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12346 * based on mappings in a provided Roo.data.Record constructor.
12348 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12349 * in the reply previously.
12354 var RecordDef = Roo.data.Record.create([
12355 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12356 {name: 'occupation'} // This field will use "occupation" as the mapping.
12358 var myReader = new Roo.data.JsonReader({
12359 totalProperty: "results", // The property which contains the total dataset size (optional)
12360 root: "rows", // The property which contains an Array of row objects
12361 id: "id" // The property within each row object that provides an ID for the record (optional)
12365 * This would consume a JSON file like this:
12367 { 'results': 2, 'rows': [
12368 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12369 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12372 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12373 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12374 * paged from the remote server.
12375 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12376 * @cfg {String} root name of the property which contains the Array of row objects.
12377 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12378 * @cfg {Array} fields Array of field definition objects
12380 * Create a new JsonReader
12381 * @param {Object} meta Metadata configuration options
12382 * @param {Object} recordType Either an Array of field definition objects,
12383 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12385 Roo.data.JsonReader = function(meta, recordType){
12388 // set some defaults:
12389 Roo.applyIf(meta, {
12390 totalProperty: 'total',
12391 successProperty : 'success',
12396 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12398 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12401 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12402 * Used by Store query builder to append _requestMeta to params.
12405 metaFromRemote : false,
12407 * This method is only used by a DataProxy which has retrieved data from a remote server.
12408 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12409 * @return {Object} data A data block which is used by an Roo.data.Store object as
12410 * a cache of Roo.data.Records.
12412 read : function(response){
12413 var json = response.responseText;
12415 var o = /* eval:var:o */ eval("("+json+")");
12417 throw {message: "JsonReader.read: Json object not found"};
12423 this.metaFromRemote = true;
12424 this.meta = o.metaData;
12425 this.recordType = Roo.data.Record.create(o.metaData.fields);
12426 this.onMetaChange(this.meta, this.recordType, o);
12428 return this.readRecords(o);
12431 // private function a store will implement
12432 onMetaChange : function(meta, recordType, o){
12439 simpleAccess: function(obj, subsc) {
12446 getJsonAccessor: function(){
12448 return function(expr) {
12450 return(re.test(expr))
12451 ? new Function("obj", "return obj." + expr)
12456 return Roo.emptyFn;
12461 * Create a data block containing Roo.data.Records from an XML document.
12462 * @param {Object} o An object which contains an Array of row objects in the property specified
12463 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12464 * which contains the total size of the dataset.
12465 * @return {Object} data A data block which is used by an Roo.data.Store object as
12466 * a cache of Roo.data.Records.
12468 readRecords : function(o){
12470 * After any data loads, the raw JSON data is available for further custom processing.
12474 var s = this.meta, Record = this.recordType,
12475 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12477 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12479 if(s.totalProperty) {
12480 this.getTotal = this.getJsonAccessor(s.totalProperty);
12482 if(s.successProperty) {
12483 this.getSuccess = this.getJsonAccessor(s.successProperty);
12485 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12487 var g = this.getJsonAccessor(s.id);
12488 this.getId = function(rec) {
12490 return (r === undefined || r === "") ? null : r;
12493 this.getId = function(){return null;};
12496 for(var jj = 0; jj < fl; jj++){
12498 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12499 this.ef[jj] = this.getJsonAccessor(map);
12503 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12504 if(s.totalProperty){
12505 var vt = parseInt(this.getTotal(o), 10);
12510 if(s.successProperty){
12511 var vs = this.getSuccess(o);
12512 if(vs === false || vs === 'false'){
12517 for(var i = 0; i < c; i++){
12520 var id = this.getId(n);
12521 for(var j = 0; j < fl; j++){
12523 var v = this.ef[j](n);
12525 Roo.log('missing convert for ' + f.name);
12529 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12531 var record = new Record(values, id);
12533 records[i] = record;
12539 totalRecords : totalRecords
12544 * Ext JS Library 1.1.1
12545 * Copyright(c) 2006-2007, Ext JS, LLC.
12547 * Originally Released Under LGPL - original licence link has changed is not relivant.
12550 * <script type="text/javascript">
12554 * @class Roo.data.ArrayReader
12555 * @extends Roo.data.DataReader
12556 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12557 * Each element of that Array represents a row of data fields. The
12558 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12559 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12563 var RecordDef = Roo.data.Record.create([
12564 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12565 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12567 var myReader = new Roo.data.ArrayReader({
12568 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12572 * This would consume an Array like this:
12574 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12576 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12578 * Create a new JsonReader
12579 * @param {Object} meta Metadata configuration options.
12580 * @param {Object} recordType Either an Array of field definition objects
12581 * as specified to {@link Roo.data.Record#create},
12582 * or an {@link Roo.data.Record} object
12583 * created using {@link Roo.data.Record#create}.
12585 Roo.data.ArrayReader = function(meta, recordType){
12586 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12589 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12591 * Create a data block containing Roo.data.Records from an XML document.
12592 * @param {Object} o An Array of row objects which represents the dataset.
12593 * @return {Object} data A data block which is used by an Roo.data.Store object as
12594 * a cache of Roo.data.Records.
12596 readRecords : function(o){
12597 var sid = this.meta ? this.meta.id : null;
12598 var recordType = this.recordType, fields = recordType.prototype.fields;
12601 for(var i = 0; i < root.length; i++){
12604 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12605 for(var j = 0, jlen = fields.length; j < jlen; j++){
12606 var f = fields.items[j];
12607 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12608 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12610 values[f.name] = v;
12612 var record = new recordType(values, id);
12614 records[records.length] = record;
12618 totalRecords : records.length
12627 * @class Roo.bootstrap.ComboBox
12628 * @extends Roo.bootstrap.TriggerField
12629 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12630 * @cfg {Boolean} append (true|false) default false
12631 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12632 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12633 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12634 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12635 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12636 * @cfg {Boolean} animate default true
12637 * @cfg {Boolean} emptyResultText only for touch device
12638 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12639 * @cfg {String} emptyTitle default ''
12641 * Create a new ComboBox.
12642 * @param {Object} config Configuration options
12644 Roo.bootstrap.ComboBox = function(config){
12645 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12649 * Fires when the dropdown list is expanded
12650 * @param {Roo.bootstrap.ComboBox} combo This combo box
12655 * Fires when the dropdown list is collapsed
12656 * @param {Roo.bootstrap.ComboBox} combo This combo box
12660 * @event beforeselect
12661 * Fires before a list item is selected. Return false to cancel the selection.
12662 * @param {Roo.bootstrap.ComboBox} combo This combo box
12663 * @param {Roo.data.Record} record The data record returned from the underlying store
12664 * @param {Number} index The index of the selected item in the dropdown list
12666 'beforeselect' : true,
12669 * Fires when a list item is selected
12670 * @param {Roo.bootstrap.ComboBox} combo This combo box
12671 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12672 * @param {Number} index The index of the selected item in the dropdown list
12676 * @event beforequery
12677 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12678 * The event object passed has these properties:
12679 * @param {Roo.bootstrap.ComboBox} combo This combo box
12680 * @param {String} query The query
12681 * @param {Boolean} forceAll true to force "all" query
12682 * @param {Boolean} cancel true to cancel the query
12683 * @param {Object} e The query event object
12685 'beforequery': true,
12688 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12689 * @param {Roo.bootstrap.ComboBox} combo This combo box
12694 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12695 * @param {Roo.bootstrap.ComboBox} combo This combo box
12696 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12701 * Fires when the remove value from the combobox array
12702 * @param {Roo.bootstrap.ComboBox} combo This combo box
12706 * @event afterremove
12707 * Fires when the remove value from the combobox array
12708 * @param {Roo.bootstrap.ComboBox} combo This combo box
12710 'afterremove' : true,
12712 * @event specialfilter
12713 * Fires when specialfilter
12714 * @param {Roo.bootstrap.ComboBox} combo This combo box
12716 'specialfilter' : true,
12719 * Fires when tick the element
12720 * @param {Roo.bootstrap.ComboBox} combo This combo box
12724 * @event touchviewdisplay
12725 * Fires when touch view require special display (default is using displayField)
12726 * @param {Roo.bootstrap.ComboBox} combo This combo box
12727 * @param {Object} cfg set html .
12729 'touchviewdisplay' : true
12734 this.tickItems = [];
12736 this.selectedIndex = -1;
12737 if(this.mode == 'local'){
12738 if(config.queryDelay === undefined){
12739 this.queryDelay = 10;
12741 if(config.minChars === undefined){
12747 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12750 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12751 * rendering into an Roo.Editor, defaults to false)
12754 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12755 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12758 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12761 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12762 * the dropdown list (defaults to undefined, with no header element)
12766 * @cfg {String/Roo.Template} tpl The template to use to render the output
12770 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12772 listWidth: undefined,
12774 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12775 * mode = 'remote' or 'text' if mode = 'local')
12777 displayField: undefined,
12780 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12781 * mode = 'remote' or 'value' if mode = 'local').
12782 * Note: use of a valueField requires the user make a selection
12783 * in order for a value to be mapped.
12785 valueField: undefined,
12787 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12792 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12793 * field's data value (defaults to the underlying DOM element's name)
12795 hiddenName: undefined,
12797 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12801 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12803 selectedClass: 'active',
12806 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12810 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12811 * anchor positions (defaults to 'tl-bl')
12813 listAlign: 'tl-bl?',
12815 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12819 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12820 * query specified by the allQuery config option (defaults to 'query')
12822 triggerAction: 'query',
12824 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12825 * (defaults to 4, does not apply if editable = false)
12829 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12830 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12834 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12835 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12839 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12840 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12844 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12845 * when editable = true (defaults to false)
12847 selectOnFocus:false,
12849 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12851 queryParam: 'query',
12853 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12854 * when mode = 'remote' (defaults to 'Loading...')
12856 loadingText: 'Loading...',
12858 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12862 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12866 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12867 * traditional select (defaults to true)
12871 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12875 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12879 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12880 * listWidth has a higher value)
12884 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12885 * allow the user to set arbitrary text into the field (defaults to false)
12887 forceSelection:false,
12889 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12890 * if typeAhead = true (defaults to 250)
12892 typeAheadDelay : 250,
12894 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12895 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12897 valueNotFoundText : undefined,
12899 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12901 blockFocus : false,
12904 * @cfg {Boolean} disableClear Disable showing of clear button.
12906 disableClear : false,
12908 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12910 alwaysQuery : false,
12913 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12918 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12920 invalidClass : "has-warning",
12923 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12925 validClass : "has-success",
12928 * @cfg {Boolean} specialFilter (true|false) special filter default false
12930 specialFilter : false,
12933 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12935 mobileTouchView : true,
12938 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12940 useNativeIOS : false,
12942 ios_options : false,
12954 btnPosition : 'right',
12955 triggerList : true,
12956 showToggleBtn : true,
12958 emptyResultText: 'Empty',
12959 triggerText : 'Select',
12962 // element that contains real text value.. (when hidden is used..)
12964 getAutoCreate : function()
12969 * Render classic select for iso
12972 if(Roo.isIOS && this.useNativeIOS){
12973 cfg = this.getAutoCreateNativeIOS();
12981 if(Roo.isTouch && this.mobileTouchView){
12982 cfg = this.getAutoCreateTouchView();
12989 if(!this.tickable){
12990 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12995 * ComboBox with tickable selections
12998 var align = this.labelAlign || this.parentLabelAlign();
13001 cls : 'form-group roo-combobox-tickable' //input-group
13004 var btn_text_select = '';
13005 var btn_text_done = '';
13006 var btn_text_cancel = '';
13008 if (this.btn_text_show) {
13009 btn_text_select = 'Select';
13010 btn_text_done = 'Done';
13011 btn_text_cancel = 'Cancel';
13016 cls : 'tickable-buttons',
13021 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13022 //html : this.triggerText
13023 html: btn_text_select
13029 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13031 html: btn_text_done
13037 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13039 html: btn_text_cancel
13045 buttons.cn.unshift({
13047 cls: 'roo-select2-search-field-input'
13053 Roo.each(buttons.cn, function(c){
13055 c.cls += ' btn-' + _this.size;
13058 if (_this.disabled) {
13069 cls: 'form-hidden-field'
13073 cls: 'roo-select2-choices',
13077 cls: 'roo-select2-search-field',
13088 cls: 'roo-select2-container input-group roo-select2-container-multi',
13093 // cls: 'typeahead typeahead-long dropdown-menu',
13094 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13099 if(this.hasFeedback && !this.allowBlank){
13103 cls: 'glyphicon form-control-feedback'
13106 combobox.cn.push(feedback);
13110 if (align ==='left' && this.fieldLabel.length) {
13112 cfg.cls += ' roo-form-group-label-left';
13117 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13118 tooltip : 'This field is required'
13123 cls : 'control-label',
13124 html : this.fieldLabel
13136 var labelCfg = cfg.cn[1];
13137 var contentCfg = cfg.cn[2];
13140 if(this.indicatorpos == 'right'){
13146 cls : 'control-label',
13150 html : this.fieldLabel
13154 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13155 tooltip : 'This field is required'
13170 labelCfg = cfg.cn[0];
13171 contentCfg = cfg.cn[1];
13175 if(this.labelWidth > 12){
13176 labelCfg.style = "width: " + this.labelWidth + 'px';
13179 if(this.labelWidth < 13 && this.labelmd == 0){
13180 this.labelmd = this.labelWidth;
13183 if(this.labellg > 0){
13184 labelCfg.cls += ' col-lg-' + this.labellg;
13185 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13188 if(this.labelmd > 0){
13189 labelCfg.cls += ' col-md-' + this.labelmd;
13190 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13193 if(this.labelsm > 0){
13194 labelCfg.cls += ' col-sm-' + this.labelsm;
13195 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13198 if(this.labelxs > 0){
13199 labelCfg.cls += ' col-xs-' + this.labelxs;
13200 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13204 } else if ( this.fieldLabel.length) {
13205 // Roo.log(" label");
13209 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13210 tooltip : 'This field is required'
13214 //cls : 'input-group-addon',
13215 html : this.fieldLabel
13220 if(this.indicatorpos == 'right'){
13224 //cls : 'input-group-addon',
13225 html : this.fieldLabel
13229 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13230 tooltip : 'This field is required'
13239 // Roo.log(" no label && no align");
13246 ['xs','sm','md','lg'].map(function(size){
13247 if (settings[size]) {
13248 cfg.cls += ' col-' + size + '-' + settings[size];
13256 _initEventsCalled : false,
13259 initEvents: function()
13261 if (this._initEventsCalled) { // as we call render... prevent looping...
13264 this._initEventsCalled = true;
13267 throw "can not find store for combo";
13270 this.indicator = this.indicatorEl();
13272 this.store = Roo.factory(this.store, Roo.data);
13273 this.store.parent = this;
13275 // if we are building from html. then this element is so complex, that we can not really
13276 // use the rendered HTML.
13277 // so we have to trash and replace the previous code.
13278 if (Roo.XComponent.build_from_html) {
13279 // remove this element....
13280 var e = this.el.dom, k=0;
13281 while (e ) { e = e.previousSibling; ++k;}
13286 this.rendered = false;
13288 this.render(this.parent().getChildContainer(true), k);
13291 if(Roo.isIOS && this.useNativeIOS){
13292 this.initIOSView();
13300 if(Roo.isTouch && this.mobileTouchView){
13301 this.initTouchView();
13306 this.initTickableEvents();
13310 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13312 if(this.hiddenName){
13314 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13316 this.hiddenField.dom.value =
13317 this.hiddenValue !== undefined ? this.hiddenValue :
13318 this.value !== undefined ? this.value : '';
13320 // prevent input submission
13321 this.el.dom.removeAttribute('name');
13322 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13327 // this.el.dom.setAttribute('autocomplete', 'off');
13330 var cls = 'x-combo-list';
13332 //this.list = new Roo.Layer({
13333 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13339 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13340 _this.list.setWidth(lw);
13343 this.list.on('mouseover', this.onViewOver, this);
13344 this.list.on('mousemove', this.onViewMove, this);
13345 this.list.on('scroll', this.onViewScroll, this);
13348 this.list.swallowEvent('mousewheel');
13349 this.assetHeight = 0;
13352 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13353 this.assetHeight += this.header.getHeight();
13356 this.innerList = this.list.createChild({cls:cls+'-inner'});
13357 this.innerList.on('mouseover', this.onViewOver, this);
13358 this.innerList.on('mousemove', this.onViewMove, this);
13359 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13361 if(this.allowBlank && !this.pageSize && !this.disableClear){
13362 this.footer = this.list.createChild({cls:cls+'-ft'});
13363 this.pageTb = new Roo.Toolbar(this.footer);
13367 this.footer = this.list.createChild({cls:cls+'-ft'});
13368 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13369 {pageSize: this.pageSize});
13373 if (this.pageTb && this.allowBlank && !this.disableClear) {
13375 this.pageTb.add(new Roo.Toolbar.Fill(), {
13376 cls: 'x-btn-icon x-btn-clear',
13378 handler: function()
13381 _this.clearValue();
13382 _this.onSelect(false, -1);
13387 this.assetHeight += this.footer.getHeight();
13392 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13395 this.view = new Roo.View(this.list, this.tpl, {
13396 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13398 //this.view.wrapEl.setDisplayed(false);
13399 this.view.on('click', this.onViewClick, this);
13402 this.store.on('beforeload', this.onBeforeLoad, this);
13403 this.store.on('load', this.onLoad, this);
13404 this.store.on('loadexception', this.onLoadException, this);
13406 if(this.resizable){
13407 this.resizer = new Roo.Resizable(this.list, {
13408 pinned:true, handles:'se'
13410 this.resizer.on('resize', function(r, w, h){
13411 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13412 this.listWidth = w;
13413 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13414 this.restrictHeight();
13416 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13419 if(!this.editable){
13420 this.editable = true;
13421 this.setEditable(false);
13426 if (typeof(this.events.add.listeners) != 'undefined') {
13428 this.addicon = this.wrap.createChild(
13429 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13431 this.addicon.on('click', function(e) {
13432 this.fireEvent('add', this);
13435 if (typeof(this.events.edit.listeners) != 'undefined') {
13437 this.editicon = this.wrap.createChild(
13438 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13439 if (this.addicon) {
13440 this.editicon.setStyle('margin-left', '40px');
13442 this.editicon.on('click', function(e) {
13444 // we fire even if inothing is selected..
13445 this.fireEvent('edit', this, this.lastData );
13451 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13452 "up" : function(e){
13453 this.inKeyMode = true;
13457 "down" : function(e){
13458 if(!this.isExpanded()){
13459 this.onTriggerClick();
13461 this.inKeyMode = true;
13466 "enter" : function(e){
13467 // this.onViewClick();
13471 if(this.fireEvent("specialkey", this, e)){
13472 this.onViewClick(false);
13478 "esc" : function(e){
13482 "tab" : function(e){
13485 if(this.fireEvent("specialkey", this, e)){
13486 this.onViewClick(false);
13494 doRelay : function(foo, bar, hname){
13495 if(hname == 'down' || this.scope.isExpanded()){
13496 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13505 this.queryDelay = Math.max(this.queryDelay || 10,
13506 this.mode == 'local' ? 10 : 250);
13509 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13511 if(this.typeAhead){
13512 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13514 if(this.editable !== false){
13515 this.inputEl().on("keyup", this.onKeyUp, this);
13517 if(this.forceSelection){
13518 this.inputEl().on('blur', this.doForce, this);
13522 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13523 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13527 initTickableEvents: function()
13531 if(this.hiddenName){
13533 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13535 this.hiddenField.dom.value =
13536 this.hiddenValue !== undefined ? this.hiddenValue :
13537 this.value !== undefined ? this.value : '';
13539 // prevent input submission
13540 this.el.dom.removeAttribute('name');
13541 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13546 // this.list = this.el.select('ul.dropdown-menu',true).first();
13548 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13549 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13550 if(this.triggerList){
13551 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13554 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13555 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13557 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13558 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13560 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13561 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13563 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13564 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13565 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13568 this.cancelBtn.hide();
13573 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13574 _this.list.setWidth(lw);
13577 this.list.on('mouseover', this.onViewOver, this);
13578 this.list.on('mousemove', this.onViewMove, this);
13580 this.list.on('scroll', this.onViewScroll, this);
13583 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>';
13586 this.view = new Roo.View(this.list, this.tpl, {
13587 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13590 //this.view.wrapEl.setDisplayed(false);
13591 this.view.on('click', this.onViewClick, this);
13595 this.store.on('beforeload', this.onBeforeLoad, this);
13596 this.store.on('load', this.onLoad, this);
13597 this.store.on('loadexception', this.onLoadException, this);
13600 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13601 "up" : function(e){
13602 this.inKeyMode = true;
13606 "down" : function(e){
13607 this.inKeyMode = true;
13611 "enter" : function(e){
13612 if(this.fireEvent("specialkey", this, e)){
13613 this.onViewClick(false);
13619 "esc" : function(e){
13620 this.onTickableFooterButtonClick(e, false, false);
13623 "tab" : function(e){
13624 this.fireEvent("specialkey", this, e);
13626 this.onTickableFooterButtonClick(e, false, false);
13633 doRelay : function(e, fn, key){
13634 if(this.scope.isExpanded()){
13635 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13644 this.queryDelay = Math.max(this.queryDelay || 10,
13645 this.mode == 'local' ? 10 : 250);
13648 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13650 if(this.typeAhead){
13651 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13654 if(this.editable !== false){
13655 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13658 this.indicator = this.indicatorEl();
13660 if(this.indicator){
13661 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13662 this.indicator.hide();
13667 onDestroy : function(){
13669 this.view.setStore(null);
13670 this.view.el.removeAllListeners();
13671 this.view.el.remove();
13672 this.view.purgeListeners();
13675 this.list.dom.innerHTML = '';
13679 this.store.un('beforeload', this.onBeforeLoad, this);
13680 this.store.un('load', this.onLoad, this);
13681 this.store.un('loadexception', this.onLoadException, this);
13683 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13687 fireKey : function(e){
13688 if(e.isNavKeyPress() && !this.list.isVisible()){
13689 this.fireEvent("specialkey", this, e);
13694 onResize: function(w, h){
13695 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13697 // if(typeof w != 'number'){
13698 // // we do not handle it!?!?
13701 // var tw = this.trigger.getWidth();
13702 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13703 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13705 // this.inputEl().setWidth( this.adjustWidth('input', x));
13707 // //this.trigger.setStyle('left', x+'px');
13709 // if(this.list && this.listWidth === undefined){
13710 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13711 // this.list.setWidth(lw);
13712 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13720 * Allow or prevent the user from directly editing the field text. If false is passed,
13721 * the user will only be able to select from the items defined in the dropdown list. This method
13722 * is the runtime equivalent of setting the 'editable' config option at config time.
13723 * @param {Boolean} value True to allow the user to directly edit the field text
13725 setEditable : function(value){
13726 if(value == this.editable){
13729 this.editable = value;
13731 this.inputEl().dom.setAttribute('readOnly', true);
13732 this.inputEl().on('mousedown', this.onTriggerClick, this);
13733 this.inputEl().addClass('x-combo-noedit');
13735 this.inputEl().dom.setAttribute('readOnly', false);
13736 this.inputEl().un('mousedown', this.onTriggerClick, this);
13737 this.inputEl().removeClass('x-combo-noedit');
13743 onBeforeLoad : function(combo,opts){
13744 if(!this.hasFocus){
13748 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13750 this.restrictHeight();
13751 this.selectedIndex = -1;
13755 onLoad : function(){
13757 this.hasQuery = false;
13759 if(!this.hasFocus){
13763 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13764 this.loading.hide();
13767 if(this.store.getCount() > 0){
13770 this.restrictHeight();
13771 if(this.lastQuery == this.allQuery){
13772 if(this.editable && !this.tickable){
13773 this.inputEl().dom.select();
13777 !this.selectByValue(this.value, true) &&
13780 !this.store.lastOptions ||
13781 typeof(this.store.lastOptions.add) == 'undefined' ||
13782 this.store.lastOptions.add != true
13785 this.select(0, true);
13788 if(this.autoFocus){
13791 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13792 this.taTask.delay(this.typeAheadDelay);
13796 this.onEmptyResults();
13802 onLoadException : function()
13804 this.hasQuery = false;
13806 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13807 this.loading.hide();
13810 if(this.tickable && this.editable){
13815 // only causes errors at present
13816 //Roo.log(this.store.reader.jsonData);
13817 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13819 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13825 onTypeAhead : function(){
13826 if(this.store.getCount() > 0){
13827 var r = this.store.getAt(0);
13828 var newValue = r.data[this.displayField];
13829 var len = newValue.length;
13830 var selStart = this.getRawValue().length;
13832 if(selStart != len){
13833 this.setRawValue(newValue);
13834 this.selectText(selStart, newValue.length);
13840 onSelect : function(record, index){
13842 if(this.fireEvent('beforeselect', this, record, index) !== false){
13844 this.setFromData(index > -1 ? record.data : false);
13847 this.fireEvent('select', this, record, index);
13852 * Returns the currently selected field value or empty string if no value is set.
13853 * @return {String} value The selected value
13855 getValue : function()
13857 if(Roo.isIOS && this.useNativeIOS){
13858 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13862 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13865 if(this.valueField){
13866 return typeof this.value != 'undefined' ? this.value : '';
13868 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13872 getRawValue : function()
13874 if(Roo.isIOS && this.useNativeIOS){
13875 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13878 var v = this.inputEl().getValue();
13884 * Clears any text/value currently set in the field
13886 clearValue : function(){
13888 if(this.hiddenField){
13889 this.hiddenField.dom.value = '';
13892 this.setRawValue('');
13893 this.lastSelectionText = '';
13894 this.lastData = false;
13896 var close = this.closeTriggerEl();
13907 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13908 * will be displayed in the field. If the value does not match the data value of an existing item,
13909 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13910 * Otherwise the field will be blank (although the value will still be set).
13911 * @param {String} value The value to match
13913 setValue : function(v)
13915 if(Roo.isIOS && this.useNativeIOS){
13916 this.setIOSValue(v);
13926 if(this.valueField){
13927 var r = this.findRecord(this.valueField, v);
13929 text = r.data[this.displayField];
13930 }else if(this.valueNotFoundText !== undefined){
13931 text = this.valueNotFoundText;
13934 this.lastSelectionText = text;
13935 if(this.hiddenField){
13936 this.hiddenField.dom.value = v;
13938 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13941 var close = this.closeTriggerEl();
13944 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13950 * @property {Object} the last set data for the element
13955 * Sets the value of the field based on a object which is related to the record format for the store.
13956 * @param {Object} value the value to set as. or false on reset?
13958 setFromData : function(o){
13965 var dv = ''; // display value
13966 var vv = ''; // value value..
13968 if (this.displayField) {
13969 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13971 // this is an error condition!!!
13972 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13975 if(this.valueField){
13976 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13979 var close = this.closeTriggerEl();
13982 if(dv.length || vv * 1 > 0){
13984 this.blockFocus=true;
13990 if(this.hiddenField){
13991 this.hiddenField.dom.value = vv;
13993 this.lastSelectionText = dv;
13994 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13998 // no hidden field.. - we store the value in 'value', but still display
13999 // display field!!!!
14000 this.lastSelectionText = dv;
14001 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14008 reset : function(){
14009 // overridden so that last data is reset..
14016 this.setValue(this.originalValue);
14017 //this.clearInvalid();
14018 this.lastData = false;
14020 this.view.clearSelections();
14026 findRecord : function(prop, value){
14028 if(this.store.getCount() > 0){
14029 this.store.each(function(r){
14030 if(r.data[prop] == value){
14040 getName: function()
14042 // returns hidden if it's set..
14043 if (!this.rendered) {return ''};
14044 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14048 onViewMove : function(e, t){
14049 this.inKeyMode = false;
14053 onViewOver : function(e, t){
14054 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14057 var item = this.view.findItemFromChild(t);
14060 var index = this.view.indexOf(item);
14061 this.select(index, false);
14066 onViewClick : function(view, doFocus, el, e)
14068 var index = this.view.getSelectedIndexes()[0];
14070 var r = this.store.getAt(index);
14074 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14081 Roo.each(this.tickItems, function(v,k){
14083 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14085 _this.tickItems.splice(k, 1);
14087 if(typeof(e) == 'undefined' && view == false){
14088 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14100 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14101 this.tickItems.push(r.data);
14104 if(typeof(e) == 'undefined' && view == false){
14105 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14112 this.onSelect(r, index);
14114 if(doFocus !== false && !this.blockFocus){
14115 this.inputEl().focus();
14120 restrictHeight : function(){
14121 //this.innerList.dom.style.height = '';
14122 //var inner = this.innerList.dom;
14123 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14124 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14125 //this.list.beginUpdate();
14126 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14127 this.list.alignTo(this.inputEl(), this.listAlign);
14128 this.list.alignTo(this.inputEl(), this.listAlign);
14129 //this.list.endUpdate();
14133 onEmptyResults : function(){
14135 if(this.tickable && this.editable){
14136 this.hasFocus = false;
14137 this.restrictHeight();
14145 * Returns true if the dropdown list is expanded, else false.
14147 isExpanded : function(){
14148 return this.list.isVisible();
14152 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14153 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14154 * @param {String} value The data value of the item to select
14155 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14156 * selected item if it is not currently in view (defaults to true)
14157 * @return {Boolean} True if the value matched an item in the list, else false
14159 selectByValue : function(v, scrollIntoView){
14160 if(v !== undefined && v !== null){
14161 var r = this.findRecord(this.valueField || this.displayField, v);
14163 this.select(this.store.indexOf(r), scrollIntoView);
14171 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14172 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14173 * @param {Number} index The zero-based index of the list item to select
14174 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14175 * selected item if it is not currently in view (defaults to true)
14177 select : function(index, scrollIntoView){
14178 this.selectedIndex = index;
14179 this.view.select(index);
14180 if(scrollIntoView !== false){
14181 var el = this.view.getNode(index);
14183 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14186 this.list.scrollChildIntoView(el, false);
14192 selectNext : function(){
14193 var ct = this.store.getCount();
14195 if(this.selectedIndex == -1){
14197 }else if(this.selectedIndex < ct-1){
14198 this.select(this.selectedIndex+1);
14204 selectPrev : function(){
14205 var ct = this.store.getCount();
14207 if(this.selectedIndex == -1){
14209 }else if(this.selectedIndex != 0){
14210 this.select(this.selectedIndex-1);
14216 onKeyUp : function(e){
14217 if(this.editable !== false && !e.isSpecialKey()){
14218 this.lastKey = e.getKey();
14219 this.dqTask.delay(this.queryDelay);
14224 validateBlur : function(){
14225 return !this.list || !this.list.isVisible();
14229 initQuery : function(){
14231 var v = this.getRawValue();
14233 if(this.tickable && this.editable){
14234 v = this.tickableInputEl().getValue();
14241 doForce : function(){
14242 if(this.inputEl().dom.value.length > 0){
14243 this.inputEl().dom.value =
14244 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14250 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14251 * query allowing the query action to be canceled if needed.
14252 * @param {String} query The SQL query to execute
14253 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14254 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14255 * saved in the current store (defaults to false)
14257 doQuery : function(q, forceAll){
14259 if(q === undefined || q === null){
14264 forceAll: forceAll,
14268 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14273 forceAll = qe.forceAll;
14274 if(forceAll === true || (q.length >= this.minChars)){
14276 this.hasQuery = true;
14278 if(this.lastQuery != q || this.alwaysQuery){
14279 this.lastQuery = q;
14280 if(this.mode == 'local'){
14281 this.selectedIndex = -1;
14283 this.store.clearFilter();
14286 if(this.specialFilter){
14287 this.fireEvent('specialfilter', this);
14292 this.store.filter(this.displayField, q);
14295 this.store.fireEvent("datachanged", this.store);
14302 this.store.baseParams[this.queryParam] = q;
14304 var options = {params : this.getParams(q)};
14307 options.add = true;
14308 options.params.start = this.page * this.pageSize;
14311 this.store.load(options);
14314 * this code will make the page width larger, at the beginning, the list not align correctly,
14315 * we should expand the list on onLoad
14316 * so command out it
14321 this.selectedIndex = -1;
14326 this.loadNext = false;
14330 getParams : function(q){
14332 //p[this.queryParam] = q;
14336 p.limit = this.pageSize;
14342 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14344 collapse : function(){
14345 if(!this.isExpanded()){
14351 this.hasFocus = false;
14355 this.cancelBtn.hide();
14356 this.trigger.show();
14359 this.tickableInputEl().dom.value = '';
14360 this.tickableInputEl().blur();
14365 Roo.get(document).un('mousedown', this.collapseIf, this);
14366 Roo.get(document).un('mousewheel', this.collapseIf, this);
14367 if (!this.editable) {
14368 Roo.get(document).un('keydown', this.listKeyPress, this);
14370 this.fireEvent('collapse', this);
14376 collapseIf : function(e){
14377 var in_combo = e.within(this.el);
14378 var in_list = e.within(this.list);
14379 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14381 if (in_combo || in_list || is_list) {
14382 //e.stopPropagation();
14387 this.onTickableFooterButtonClick(e, false, false);
14395 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14397 expand : function(){
14399 if(this.isExpanded() || !this.hasFocus){
14403 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14404 this.list.setWidth(lw);
14410 this.restrictHeight();
14414 this.tickItems = Roo.apply([], this.item);
14417 this.cancelBtn.show();
14418 this.trigger.hide();
14421 this.tickableInputEl().focus();
14426 Roo.get(document).on('mousedown', this.collapseIf, this);
14427 Roo.get(document).on('mousewheel', this.collapseIf, this);
14428 if (!this.editable) {
14429 Roo.get(document).on('keydown', this.listKeyPress, this);
14432 this.fireEvent('expand', this);
14436 // Implements the default empty TriggerField.onTriggerClick function
14437 onTriggerClick : function(e)
14439 Roo.log('trigger click');
14441 if(this.disabled || !this.triggerList){
14446 this.loadNext = false;
14448 if(this.isExpanded()){
14450 if (!this.blockFocus) {
14451 this.inputEl().focus();
14455 this.hasFocus = true;
14456 if(this.triggerAction == 'all') {
14457 this.doQuery(this.allQuery, true);
14459 this.doQuery(this.getRawValue());
14461 if (!this.blockFocus) {
14462 this.inputEl().focus();
14467 onTickableTriggerClick : function(e)
14474 this.loadNext = false;
14475 this.hasFocus = true;
14477 if(this.triggerAction == 'all') {
14478 this.doQuery(this.allQuery, true);
14480 this.doQuery(this.getRawValue());
14484 onSearchFieldClick : function(e)
14486 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14487 this.onTickableFooterButtonClick(e, false, false);
14491 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14496 this.loadNext = false;
14497 this.hasFocus = true;
14499 if(this.triggerAction == 'all') {
14500 this.doQuery(this.allQuery, true);
14502 this.doQuery(this.getRawValue());
14506 listKeyPress : function(e)
14508 //Roo.log('listkeypress');
14509 // scroll to first matching element based on key pres..
14510 if (e.isSpecialKey()) {
14513 var k = String.fromCharCode(e.getKey()).toUpperCase();
14516 var csel = this.view.getSelectedNodes();
14517 var cselitem = false;
14519 var ix = this.view.indexOf(csel[0]);
14520 cselitem = this.store.getAt(ix);
14521 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14527 this.store.each(function(v) {
14529 // start at existing selection.
14530 if (cselitem.id == v.id) {
14536 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14537 match = this.store.indexOf(v);
14543 if (match === false) {
14544 return true; // no more action?
14547 this.view.select(match);
14548 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14549 sn.scrollIntoView(sn.dom.parentNode, false);
14552 onViewScroll : function(e, t){
14554 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){
14558 this.hasQuery = true;
14560 this.loading = this.list.select('.loading', true).first();
14562 if(this.loading === null){
14563 this.list.createChild({
14565 cls: 'loading roo-select2-more-results roo-select2-active',
14566 html: 'Loading more results...'
14569 this.loading = this.list.select('.loading', true).first();
14571 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14573 this.loading.hide();
14576 this.loading.show();
14581 this.loadNext = true;
14583 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14588 addItem : function(o)
14590 var dv = ''; // display value
14592 if (this.displayField) {
14593 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14595 // this is an error condition!!!
14596 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14603 var choice = this.choices.createChild({
14605 cls: 'roo-select2-search-choice',
14614 cls: 'roo-select2-search-choice-close fa fa-times',
14619 }, this.searchField);
14621 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14623 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14631 this.inputEl().dom.value = '';
14636 onRemoveItem : function(e, _self, o)
14638 e.preventDefault();
14640 this.lastItem = Roo.apply([], this.item);
14642 var index = this.item.indexOf(o.data) * 1;
14645 Roo.log('not this item?!');
14649 this.item.splice(index, 1);
14654 this.fireEvent('remove', this, e);
14660 syncValue : function()
14662 if(!this.item.length){
14669 Roo.each(this.item, function(i){
14670 if(_this.valueField){
14671 value.push(i[_this.valueField]);
14678 this.value = value.join(',');
14680 if(this.hiddenField){
14681 this.hiddenField.dom.value = this.value;
14684 this.store.fireEvent("datachanged", this.store);
14689 clearItem : function()
14691 if(!this.multiple){
14697 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14705 if(this.tickable && !Roo.isTouch){
14706 this.view.refresh();
14710 inputEl: function ()
14712 if(Roo.isIOS && this.useNativeIOS){
14713 return this.el.select('select.roo-ios-select', true).first();
14716 if(Roo.isTouch && this.mobileTouchView){
14717 return this.el.select('input.form-control',true).first();
14721 return this.searchField;
14724 return this.el.select('input.form-control',true).first();
14727 onTickableFooterButtonClick : function(e, btn, el)
14729 e.preventDefault();
14731 this.lastItem = Roo.apply([], this.item);
14733 if(btn && btn.name == 'cancel'){
14734 this.tickItems = Roo.apply([], this.item);
14743 Roo.each(this.tickItems, function(o){
14751 validate : function()
14753 if(this.getVisibilityEl().hasClass('hidden')){
14757 var v = this.getRawValue();
14760 v = this.getValue();
14763 if(this.disabled || this.allowBlank || v.length){
14768 this.markInvalid();
14772 tickableInputEl : function()
14774 if(!this.tickable || !this.editable){
14775 return this.inputEl();
14778 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14782 getAutoCreateTouchView : function()
14787 cls: 'form-group' //input-group
14793 type : this.inputType,
14794 cls : 'form-control x-combo-noedit',
14795 autocomplete: 'new-password',
14796 placeholder : this.placeholder || '',
14801 input.name = this.name;
14805 input.cls += ' input-' + this.size;
14808 if (this.disabled) {
14809 input.disabled = true;
14820 inputblock.cls += ' input-group';
14822 inputblock.cn.unshift({
14824 cls : 'input-group-addon',
14829 if(this.removable && !this.multiple){
14830 inputblock.cls += ' roo-removable';
14832 inputblock.cn.push({
14835 cls : 'roo-combo-removable-btn close'
14839 if(this.hasFeedback && !this.allowBlank){
14841 inputblock.cls += ' has-feedback';
14843 inputblock.cn.push({
14845 cls: 'glyphicon form-control-feedback'
14852 inputblock.cls += (this.before) ? '' : ' input-group';
14854 inputblock.cn.push({
14856 cls : 'input-group-addon',
14867 cls: 'form-hidden-field'
14881 cls: 'form-hidden-field'
14885 cls: 'roo-select2-choices',
14889 cls: 'roo-select2-search-field',
14902 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14908 if(!this.multiple && this.showToggleBtn){
14915 if (this.caret != false) {
14918 cls: 'fa fa-' + this.caret
14925 cls : 'input-group-addon btn dropdown-toggle',
14930 cls: 'combobox-clear',
14944 combobox.cls += ' roo-select2-container-multi';
14947 var align = this.labelAlign || this.parentLabelAlign();
14949 if (align ==='left' && this.fieldLabel.length) {
14954 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14955 tooltip : 'This field is required'
14959 cls : 'control-label',
14960 html : this.fieldLabel
14971 var labelCfg = cfg.cn[1];
14972 var contentCfg = cfg.cn[2];
14975 if(this.indicatorpos == 'right'){
14980 cls : 'control-label',
14984 html : this.fieldLabel
14988 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14989 tooltip : 'This field is required'
15002 labelCfg = cfg.cn[0];
15003 contentCfg = cfg.cn[1];
15008 if(this.labelWidth > 12){
15009 labelCfg.style = "width: " + this.labelWidth + 'px';
15012 if(this.labelWidth < 13 && this.labelmd == 0){
15013 this.labelmd = this.labelWidth;
15016 if(this.labellg > 0){
15017 labelCfg.cls += ' col-lg-' + this.labellg;
15018 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15021 if(this.labelmd > 0){
15022 labelCfg.cls += ' col-md-' + this.labelmd;
15023 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15026 if(this.labelsm > 0){
15027 labelCfg.cls += ' col-sm-' + this.labelsm;
15028 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15031 if(this.labelxs > 0){
15032 labelCfg.cls += ' col-xs-' + this.labelxs;
15033 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15037 } else if ( this.fieldLabel.length) {
15041 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15042 tooltip : 'This field is required'
15046 cls : 'control-label',
15047 html : this.fieldLabel
15058 if(this.indicatorpos == 'right'){
15062 cls : 'control-label',
15063 html : this.fieldLabel,
15067 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15068 tooltip : 'This field is required'
15085 var settings = this;
15087 ['xs','sm','md','lg'].map(function(size){
15088 if (settings[size]) {
15089 cfg.cls += ' col-' + size + '-' + settings[size];
15096 initTouchView : function()
15098 this.renderTouchView();
15100 this.touchViewEl.on('scroll', function(){
15101 this.el.dom.scrollTop = 0;
15104 this.originalValue = this.getValue();
15106 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15108 this.inputEl().on("click", this.showTouchView, this);
15109 if (this.triggerEl) {
15110 this.triggerEl.on("click", this.showTouchView, this);
15114 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15115 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15117 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15119 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15120 this.store.on('load', this.onTouchViewLoad, this);
15121 this.store.on('loadexception', this.onTouchViewLoadException, this);
15123 if(this.hiddenName){
15125 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15127 this.hiddenField.dom.value =
15128 this.hiddenValue !== undefined ? this.hiddenValue :
15129 this.value !== undefined ? this.value : '';
15131 this.el.dom.removeAttribute('name');
15132 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15136 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15137 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15140 if(this.removable && !this.multiple){
15141 var close = this.closeTriggerEl();
15143 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15144 close.on('click', this.removeBtnClick, this, close);
15148 * fix the bug in Safari iOS8
15150 this.inputEl().on("focus", function(e){
15151 document.activeElement.blur();
15159 renderTouchView : function()
15161 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15162 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15164 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15165 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15167 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15168 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15169 this.touchViewBodyEl.setStyle('overflow', 'auto');
15171 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15172 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15174 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15175 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15179 showTouchView : function()
15185 this.touchViewHeaderEl.hide();
15187 if(this.modalTitle.length){
15188 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15189 this.touchViewHeaderEl.show();
15192 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15193 this.touchViewEl.show();
15195 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15197 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15198 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15200 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15202 if(this.modalTitle.length){
15203 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15206 this.touchViewBodyEl.setHeight(bodyHeight);
15210 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15212 this.touchViewEl.addClass('in');
15215 this.doTouchViewQuery();
15219 hideTouchView : function()
15221 this.touchViewEl.removeClass('in');
15225 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15227 this.touchViewEl.setStyle('display', 'none');
15232 setTouchViewValue : function()
15239 Roo.each(this.tickItems, function(o){
15244 this.hideTouchView();
15247 doTouchViewQuery : function()
15256 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15260 if(!this.alwaysQuery || this.mode == 'local'){
15261 this.onTouchViewLoad();
15268 onTouchViewBeforeLoad : function(combo,opts)
15274 onTouchViewLoad : function()
15276 if(this.store.getCount() < 1){
15277 this.onTouchViewEmptyResults();
15281 this.clearTouchView();
15283 var rawValue = this.getRawValue();
15285 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15287 this.tickItems = [];
15289 this.store.data.each(function(d, rowIndex){
15290 var row = this.touchViewListGroup.createChild(template);
15292 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15293 row.addClass(d.data.cls);
15296 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15299 html : d.data[this.displayField]
15302 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15303 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15306 row.removeClass('selected');
15307 if(!this.multiple && this.valueField &&
15308 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15311 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15312 row.addClass('selected');
15315 if(this.multiple && this.valueField &&
15316 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15320 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15321 this.tickItems.push(d.data);
15324 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15328 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15330 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15332 if(this.modalTitle.length){
15333 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15336 var listHeight = this.touchViewListGroup.getHeight();
15340 if(firstChecked && listHeight > bodyHeight){
15341 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15346 onTouchViewLoadException : function()
15348 this.hideTouchView();
15351 onTouchViewEmptyResults : function()
15353 this.clearTouchView();
15355 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15357 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15361 clearTouchView : function()
15363 this.touchViewListGroup.dom.innerHTML = '';
15366 onTouchViewClick : function(e, el, o)
15368 e.preventDefault();
15371 var rowIndex = o.rowIndex;
15373 var r = this.store.getAt(rowIndex);
15375 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15377 if(!this.multiple){
15378 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15379 c.dom.removeAttribute('checked');
15382 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15384 this.setFromData(r.data);
15386 var close = this.closeTriggerEl();
15392 this.hideTouchView();
15394 this.fireEvent('select', this, r, rowIndex);
15399 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15400 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15401 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15405 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15406 this.addItem(r.data);
15407 this.tickItems.push(r.data);
15411 getAutoCreateNativeIOS : function()
15414 cls: 'form-group' //input-group,
15419 cls : 'roo-ios-select'
15423 combobox.name = this.name;
15426 if (this.disabled) {
15427 combobox.disabled = true;
15430 var settings = this;
15432 ['xs','sm','md','lg'].map(function(size){
15433 if (settings[size]) {
15434 cfg.cls += ' col-' + size + '-' + settings[size];
15444 initIOSView : function()
15446 this.store.on('load', this.onIOSViewLoad, this);
15451 onIOSViewLoad : function()
15453 if(this.store.getCount() < 1){
15457 this.clearIOSView();
15459 if(this.allowBlank) {
15461 var default_text = '-- SELECT --';
15463 if(this.placeholder.length){
15464 default_text = this.placeholder;
15467 if(this.emptyTitle.length){
15468 default_text += ' - ' + this.emptyTitle + ' -';
15471 var opt = this.inputEl().createChild({
15474 html : default_text
15478 o[this.valueField] = 0;
15479 o[this.displayField] = default_text;
15481 this.ios_options.push({
15488 this.store.data.each(function(d, rowIndex){
15492 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15493 html = d.data[this.displayField];
15498 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15499 value = d.data[this.valueField];
15508 if(this.value == d.data[this.valueField]){
15509 option['selected'] = true;
15512 var opt = this.inputEl().createChild(option);
15514 this.ios_options.push({
15521 this.inputEl().on('change', function(){
15522 this.fireEvent('select', this);
15527 clearIOSView: function()
15529 this.inputEl().dom.innerHTML = '';
15531 this.ios_options = [];
15534 setIOSValue: function(v)
15538 if(!this.ios_options){
15542 Roo.each(this.ios_options, function(opts){
15544 opts.el.dom.removeAttribute('selected');
15546 if(opts.data[this.valueField] != v){
15550 opts.el.dom.setAttribute('selected', true);
15556 * @cfg {Boolean} grow
15560 * @cfg {Number} growMin
15564 * @cfg {Number} growMax
15573 Roo.apply(Roo.bootstrap.ComboBox, {
15577 cls: 'modal-header',
15599 cls: 'list-group-item',
15603 cls: 'roo-combobox-list-group-item-value'
15607 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15621 listItemCheckbox : {
15623 cls: 'list-group-item',
15627 cls: 'roo-combobox-list-group-item-value'
15631 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15647 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15652 cls: 'modal-footer',
15660 cls: 'col-xs-6 text-left',
15663 cls: 'btn btn-danger roo-touch-view-cancel',
15669 cls: 'col-xs-6 text-right',
15672 cls: 'btn btn-success roo-touch-view-ok',
15683 Roo.apply(Roo.bootstrap.ComboBox, {
15685 touchViewTemplate : {
15687 cls: 'modal fade roo-combobox-touch-view',
15691 cls: 'modal-dialog',
15692 style : 'position:fixed', // we have to fix position....
15696 cls: 'modal-content',
15698 Roo.bootstrap.ComboBox.header,
15699 Roo.bootstrap.ComboBox.body,
15700 Roo.bootstrap.ComboBox.footer
15709 * Ext JS Library 1.1.1
15710 * Copyright(c) 2006-2007, Ext JS, LLC.
15712 * Originally Released Under LGPL - original licence link has changed is not relivant.
15715 * <script type="text/javascript">
15720 * @extends Roo.util.Observable
15721 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15722 * This class also supports single and multi selection modes. <br>
15723 * Create a data model bound view:
15725 var store = new Roo.data.Store(...);
15727 var view = new Roo.View({
15729 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15731 singleSelect: true,
15732 selectedClass: "ydataview-selected",
15736 // listen for node click?
15737 view.on("click", function(vw, index, node, e){
15738 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15742 dataModel.load("foobar.xml");
15744 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15746 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15747 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15749 * Note: old style constructor is still suported (container, template, config)
15752 * Create a new View
15753 * @param {Object} config The config object
15756 Roo.View = function(config, depreciated_tpl, depreciated_config){
15758 this.parent = false;
15760 if (typeof(depreciated_tpl) == 'undefined') {
15761 // new way.. - universal constructor.
15762 Roo.apply(this, config);
15763 this.el = Roo.get(this.el);
15766 this.el = Roo.get(config);
15767 this.tpl = depreciated_tpl;
15768 Roo.apply(this, depreciated_config);
15770 this.wrapEl = this.el.wrap().wrap();
15771 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15774 if(typeof(this.tpl) == "string"){
15775 this.tpl = new Roo.Template(this.tpl);
15777 // support xtype ctors..
15778 this.tpl = new Roo.factory(this.tpl, Roo);
15782 this.tpl.compile();
15787 * @event beforeclick
15788 * Fires before a click is processed. Returns false to cancel the default action.
15789 * @param {Roo.View} this
15790 * @param {Number} index The index of the target node
15791 * @param {HTMLElement} node The target node
15792 * @param {Roo.EventObject} e The raw event object
15794 "beforeclick" : true,
15797 * Fires when a template node is clicked.
15798 * @param {Roo.View} this
15799 * @param {Number} index The index of the target node
15800 * @param {HTMLElement} node The target node
15801 * @param {Roo.EventObject} e The raw event object
15806 * Fires when a template node is double clicked.
15807 * @param {Roo.View} this
15808 * @param {Number} index The index of the target node
15809 * @param {HTMLElement} node The target node
15810 * @param {Roo.EventObject} e The raw event object
15814 * @event contextmenu
15815 * Fires when a template node is right clicked.
15816 * @param {Roo.View} this
15817 * @param {Number} index The index of the target node
15818 * @param {HTMLElement} node The target node
15819 * @param {Roo.EventObject} e The raw event object
15821 "contextmenu" : true,
15823 * @event selectionchange
15824 * Fires when the selected nodes change.
15825 * @param {Roo.View} this
15826 * @param {Array} selections Array of the selected nodes
15828 "selectionchange" : true,
15831 * @event beforeselect
15832 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15833 * @param {Roo.View} this
15834 * @param {HTMLElement} node The node to be selected
15835 * @param {Array} selections Array of currently selected nodes
15837 "beforeselect" : true,
15839 * @event preparedata
15840 * Fires on every row to render, to allow you to change the data.
15841 * @param {Roo.View} this
15842 * @param {Object} data to be rendered (change this)
15844 "preparedata" : true
15852 "click": this.onClick,
15853 "dblclick": this.onDblClick,
15854 "contextmenu": this.onContextMenu,
15858 this.selections = [];
15860 this.cmp = new Roo.CompositeElementLite([]);
15862 this.store = Roo.factory(this.store, Roo.data);
15863 this.setStore(this.store, true);
15866 if ( this.footer && this.footer.xtype) {
15868 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15870 this.footer.dataSource = this.store;
15871 this.footer.container = fctr;
15872 this.footer = Roo.factory(this.footer, Roo);
15873 fctr.insertFirst(this.el);
15875 // this is a bit insane - as the paging toolbar seems to detach the el..
15876 // dom.parentNode.parentNode.parentNode
15877 // they get detached?
15881 Roo.View.superclass.constructor.call(this);
15886 Roo.extend(Roo.View, Roo.util.Observable, {
15889 * @cfg {Roo.data.Store} store Data store to load data from.
15894 * @cfg {String|Roo.Element} el The container element.
15899 * @cfg {String|Roo.Template} tpl The template used by this View
15903 * @cfg {String} dataName the named area of the template to use as the data area
15904 * Works with domtemplates roo-name="name"
15908 * @cfg {String} selectedClass The css class to add to selected nodes
15910 selectedClass : "x-view-selected",
15912 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15917 * @cfg {String} text to display on mask (default Loading)
15921 * @cfg {Boolean} multiSelect Allow multiple selection
15923 multiSelect : false,
15925 * @cfg {Boolean} singleSelect Allow single selection
15927 singleSelect: false,
15930 * @cfg {Boolean} toggleSelect - selecting
15932 toggleSelect : false,
15935 * @cfg {Boolean} tickable - selecting
15940 * Returns the element this view is bound to.
15941 * @return {Roo.Element}
15943 getEl : function(){
15944 return this.wrapEl;
15950 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15952 refresh : function(){
15953 //Roo.log('refresh');
15956 // if we are using something like 'domtemplate', then
15957 // the what gets used is:
15958 // t.applySubtemplate(NAME, data, wrapping data..)
15959 // the outer template then get' applied with
15960 // the store 'extra data'
15961 // and the body get's added to the
15962 // roo-name="data" node?
15963 // <span class='roo-tpl-{name}'></span> ?????
15967 this.clearSelections();
15968 this.el.update("");
15970 var records = this.store.getRange();
15971 if(records.length < 1) {
15973 // is this valid?? = should it render a template??
15975 this.el.update(this.emptyText);
15979 if (this.dataName) {
15980 this.el.update(t.apply(this.store.meta)); //????
15981 el = this.el.child('.roo-tpl-' + this.dataName);
15984 for(var i = 0, len = records.length; i < len; i++){
15985 var data = this.prepareData(records[i].data, i, records[i]);
15986 this.fireEvent("preparedata", this, data, i, records[i]);
15988 var d = Roo.apply({}, data);
15991 Roo.apply(d, {'roo-id' : Roo.id()});
15995 Roo.each(this.parent.item, function(item){
15996 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15999 Roo.apply(d, {'roo-data-checked' : 'checked'});
16003 html[html.length] = Roo.util.Format.trim(
16005 t.applySubtemplate(this.dataName, d, this.store.meta) :
16012 el.update(html.join(""));
16013 this.nodes = el.dom.childNodes;
16014 this.updateIndexes(0);
16019 * Function to override to reformat the data that is sent to
16020 * the template for each node.
16021 * DEPRICATED - use the preparedata event handler.
16022 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16023 * a JSON object for an UpdateManager bound view).
16025 prepareData : function(data, index, record)
16027 this.fireEvent("preparedata", this, data, index, record);
16031 onUpdate : function(ds, record){
16032 // Roo.log('on update');
16033 this.clearSelections();
16034 var index = this.store.indexOf(record);
16035 var n = this.nodes[index];
16036 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16037 n.parentNode.removeChild(n);
16038 this.updateIndexes(index, index);
16044 onAdd : function(ds, records, index)
16046 //Roo.log(['on Add', ds, records, index] );
16047 this.clearSelections();
16048 if(this.nodes.length == 0){
16052 var n = this.nodes[index];
16053 for(var i = 0, len = records.length; i < len; i++){
16054 var d = this.prepareData(records[i].data, i, records[i]);
16056 this.tpl.insertBefore(n, d);
16059 this.tpl.append(this.el, d);
16062 this.updateIndexes(index);
16065 onRemove : function(ds, record, index){
16066 // Roo.log('onRemove');
16067 this.clearSelections();
16068 var el = this.dataName ?
16069 this.el.child('.roo-tpl-' + this.dataName) :
16072 el.dom.removeChild(this.nodes[index]);
16073 this.updateIndexes(index);
16077 * Refresh an individual node.
16078 * @param {Number} index
16080 refreshNode : function(index){
16081 this.onUpdate(this.store, this.store.getAt(index));
16084 updateIndexes : function(startIndex, endIndex){
16085 var ns = this.nodes;
16086 startIndex = startIndex || 0;
16087 endIndex = endIndex || ns.length - 1;
16088 for(var i = startIndex; i <= endIndex; i++){
16089 ns[i].nodeIndex = i;
16094 * Changes the data store this view uses and refresh the view.
16095 * @param {Store} store
16097 setStore : function(store, initial){
16098 if(!initial && this.store){
16099 this.store.un("datachanged", this.refresh);
16100 this.store.un("add", this.onAdd);
16101 this.store.un("remove", this.onRemove);
16102 this.store.un("update", this.onUpdate);
16103 this.store.un("clear", this.refresh);
16104 this.store.un("beforeload", this.onBeforeLoad);
16105 this.store.un("load", this.onLoad);
16106 this.store.un("loadexception", this.onLoad);
16110 store.on("datachanged", this.refresh, this);
16111 store.on("add", this.onAdd, this);
16112 store.on("remove", this.onRemove, this);
16113 store.on("update", this.onUpdate, this);
16114 store.on("clear", this.refresh, this);
16115 store.on("beforeload", this.onBeforeLoad, this);
16116 store.on("load", this.onLoad, this);
16117 store.on("loadexception", this.onLoad, this);
16125 * onbeforeLoad - masks the loading area.
16128 onBeforeLoad : function(store,opts)
16130 //Roo.log('onBeforeLoad');
16132 this.el.update("");
16134 this.el.mask(this.mask ? this.mask : "Loading" );
16136 onLoad : function ()
16143 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16144 * @param {HTMLElement} node
16145 * @return {HTMLElement} The template node
16147 findItemFromChild : function(node){
16148 var el = this.dataName ?
16149 this.el.child('.roo-tpl-' + this.dataName,true) :
16152 if(!node || node.parentNode == el){
16155 var p = node.parentNode;
16156 while(p && p != el){
16157 if(p.parentNode == el){
16166 onClick : function(e){
16167 var item = this.findItemFromChild(e.getTarget());
16169 var index = this.indexOf(item);
16170 if(this.onItemClick(item, index, e) !== false){
16171 this.fireEvent("click", this, index, item, e);
16174 this.clearSelections();
16179 onContextMenu : function(e){
16180 var item = this.findItemFromChild(e.getTarget());
16182 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16187 onDblClick : function(e){
16188 var item = this.findItemFromChild(e.getTarget());
16190 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16194 onItemClick : function(item, index, e)
16196 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16199 if (this.toggleSelect) {
16200 var m = this.isSelected(item) ? 'unselect' : 'select';
16203 _t[m](item, true, false);
16206 if(this.multiSelect || this.singleSelect){
16207 if(this.multiSelect && e.shiftKey && this.lastSelection){
16208 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16210 this.select(item, this.multiSelect && e.ctrlKey);
16211 this.lastSelection = item;
16214 if(!this.tickable){
16215 e.preventDefault();
16223 * Get the number of selected nodes.
16226 getSelectionCount : function(){
16227 return this.selections.length;
16231 * Get the currently selected nodes.
16232 * @return {Array} An array of HTMLElements
16234 getSelectedNodes : function(){
16235 return this.selections;
16239 * Get the indexes of the selected nodes.
16242 getSelectedIndexes : function(){
16243 var indexes = [], s = this.selections;
16244 for(var i = 0, len = s.length; i < len; i++){
16245 indexes.push(s[i].nodeIndex);
16251 * Clear all selections
16252 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16254 clearSelections : function(suppressEvent){
16255 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16256 this.cmp.elements = this.selections;
16257 this.cmp.removeClass(this.selectedClass);
16258 this.selections = [];
16259 if(!suppressEvent){
16260 this.fireEvent("selectionchange", this, this.selections);
16266 * Returns true if the passed node is selected
16267 * @param {HTMLElement/Number} node The node or node index
16268 * @return {Boolean}
16270 isSelected : function(node){
16271 var s = this.selections;
16275 node = this.getNode(node);
16276 return s.indexOf(node) !== -1;
16281 * @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
16282 * @param {Boolean} keepExisting (optional) true to keep existing selections
16283 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16285 select : function(nodeInfo, keepExisting, suppressEvent){
16286 if(nodeInfo instanceof Array){
16288 this.clearSelections(true);
16290 for(var i = 0, len = nodeInfo.length; i < len; i++){
16291 this.select(nodeInfo[i], true, true);
16295 var node = this.getNode(nodeInfo);
16296 if(!node || this.isSelected(node)){
16297 return; // already selected.
16300 this.clearSelections(true);
16303 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16304 Roo.fly(node).addClass(this.selectedClass);
16305 this.selections.push(node);
16306 if(!suppressEvent){
16307 this.fireEvent("selectionchange", this, this.selections);
16315 * @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
16316 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16317 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16319 unselect : function(nodeInfo, keepExisting, suppressEvent)
16321 if(nodeInfo instanceof Array){
16322 Roo.each(this.selections, function(s) {
16323 this.unselect(s, nodeInfo);
16327 var node = this.getNode(nodeInfo);
16328 if(!node || !this.isSelected(node)){
16329 //Roo.log("not selected");
16330 return; // not selected.
16334 Roo.each(this.selections, function(s) {
16336 Roo.fly(node).removeClass(this.selectedClass);
16343 this.selections= ns;
16344 this.fireEvent("selectionchange", this, this.selections);
16348 * Gets a template node.
16349 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16350 * @return {HTMLElement} The node or null if it wasn't found
16352 getNode : function(nodeInfo){
16353 if(typeof nodeInfo == "string"){
16354 return document.getElementById(nodeInfo);
16355 }else if(typeof nodeInfo == "number"){
16356 return this.nodes[nodeInfo];
16362 * Gets a range template nodes.
16363 * @param {Number} startIndex
16364 * @param {Number} endIndex
16365 * @return {Array} An array of nodes
16367 getNodes : function(start, end){
16368 var ns = this.nodes;
16369 start = start || 0;
16370 end = typeof end == "undefined" ? ns.length - 1 : end;
16373 for(var i = start; i <= end; i++){
16377 for(var i = start; i >= end; i--){
16385 * Finds the index of the passed node
16386 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16387 * @return {Number} The index of the node or -1
16389 indexOf : function(node){
16390 node = this.getNode(node);
16391 if(typeof node.nodeIndex == "number"){
16392 return node.nodeIndex;
16394 var ns = this.nodes;
16395 for(var i = 0, len = ns.length; i < len; i++){
16406 * based on jquery fullcalendar
16410 Roo.bootstrap = Roo.bootstrap || {};
16412 * @class Roo.bootstrap.Calendar
16413 * @extends Roo.bootstrap.Component
16414 * Bootstrap Calendar class
16415 * @cfg {Boolean} loadMask (true|false) default false
16416 * @cfg {Object} header generate the user specific header of the calendar, default false
16419 * Create a new Container
16420 * @param {Object} config The config object
16425 Roo.bootstrap.Calendar = function(config){
16426 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16430 * Fires when a date is selected
16431 * @param {DatePicker} this
16432 * @param {Date} date The selected date
16436 * @event monthchange
16437 * Fires when the displayed month changes
16438 * @param {DatePicker} this
16439 * @param {Date} date The selected month
16441 'monthchange': true,
16443 * @event evententer
16444 * Fires when mouse over an event
16445 * @param {Calendar} this
16446 * @param {event} Event
16448 'evententer': true,
16450 * @event eventleave
16451 * Fires when the mouse leaves an
16452 * @param {Calendar} this
16455 'eventleave': true,
16457 * @event eventclick
16458 * Fires when the mouse click an
16459 * @param {Calendar} this
16468 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16471 * @cfg {Number} startDay
16472 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16480 getAutoCreate : function(){
16483 var fc_button = function(name, corner, style, content ) {
16484 return Roo.apply({},{
16486 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16488 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16491 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16502 style : 'width:100%',
16509 cls : 'fc-header-left',
16511 fc_button('prev', 'left', 'arrow', '‹' ),
16512 fc_button('next', 'right', 'arrow', '›' ),
16513 { tag: 'span', cls: 'fc-header-space' },
16514 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16522 cls : 'fc-header-center',
16526 cls: 'fc-header-title',
16529 html : 'month / year'
16537 cls : 'fc-header-right',
16539 /* fc_button('month', 'left', '', 'month' ),
16540 fc_button('week', '', '', 'week' ),
16541 fc_button('day', 'right', '', 'day' )
16553 header = this.header;
16556 var cal_heads = function() {
16558 // fixme - handle this.
16560 for (var i =0; i < Date.dayNames.length; i++) {
16561 var d = Date.dayNames[i];
16564 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16565 html : d.substring(0,3)
16569 ret[0].cls += ' fc-first';
16570 ret[6].cls += ' fc-last';
16573 var cal_cell = function(n) {
16576 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16581 cls: 'fc-day-number',
16585 cls: 'fc-day-content',
16589 style: 'position: relative;' // height: 17px;
16601 var cal_rows = function() {
16604 for (var r = 0; r < 6; r++) {
16611 for (var i =0; i < Date.dayNames.length; i++) {
16612 var d = Date.dayNames[i];
16613 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16616 row.cn[0].cls+=' fc-first';
16617 row.cn[0].cn[0].style = 'min-height:90px';
16618 row.cn[6].cls+=' fc-last';
16622 ret[0].cls += ' fc-first';
16623 ret[4].cls += ' fc-prev-last';
16624 ret[5].cls += ' fc-last';
16631 cls: 'fc-border-separate',
16632 style : 'width:100%',
16640 cls : 'fc-first fc-last',
16658 cls : 'fc-content',
16659 style : "position: relative;",
16662 cls : 'fc-view fc-view-month fc-grid',
16663 style : 'position: relative',
16664 unselectable : 'on',
16667 cls : 'fc-event-container',
16668 style : 'position:absolute;z-index:8;top:0;left:0;'
16686 initEvents : function()
16689 throw "can not find store for calendar";
16695 style: "text-align:center",
16699 style: "background-color:white;width:50%;margin:250 auto",
16703 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16714 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16716 var size = this.el.select('.fc-content', true).first().getSize();
16717 this.maskEl.setSize(size.width, size.height);
16718 this.maskEl.enableDisplayMode("block");
16719 if(!this.loadMask){
16720 this.maskEl.hide();
16723 this.store = Roo.factory(this.store, Roo.data);
16724 this.store.on('load', this.onLoad, this);
16725 this.store.on('beforeload', this.onBeforeLoad, this);
16729 this.cells = this.el.select('.fc-day',true);
16730 //Roo.log(this.cells);
16731 this.textNodes = this.el.query('.fc-day-number');
16732 this.cells.addClassOnOver('fc-state-hover');
16734 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16735 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16736 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16737 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16739 this.on('monthchange', this.onMonthChange, this);
16741 this.update(new Date().clearTime());
16744 resize : function() {
16745 var sz = this.el.getSize();
16747 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16748 this.el.select('.fc-day-content div',true).setHeight(34);
16753 showPrevMonth : function(e){
16754 this.update(this.activeDate.add("mo", -1));
16756 showToday : function(e){
16757 this.update(new Date().clearTime());
16760 showNextMonth : function(e){
16761 this.update(this.activeDate.add("mo", 1));
16765 showPrevYear : function(){
16766 this.update(this.activeDate.add("y", -1));
16770 showNextYear : function(){
16771 this.update(this.activeDate.add("y", 1));
16776 update : function(date)
16778 var vd = this.activeDate;
16779 this.activeDate = date;
16780 // if(vd && this.el){
16781 // var t = date.getTime();
16782 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16783 // Roo.log('using add remove');
16785 // this.fireEvent('monthchange', this, date);
16787 // this.cells.removeClass("fc-state-highlight");
16788 // this.cells.each(function(c){
16789 // if(c.dateValue == t){
16790 // c.addClass("fc-state-highlight");
16791 // setTimeout(function(){
16792 // try{c.dom.firstChild.focus();}catch(e){}
16802 var days = date.getDaysInMonth();
16804 var firstOfMonth = date.getFirstDateOfMonth();
16805 var startingPos = firstOfMonth.getDay()-this.startDay;
16807 if(startingPos < this.startDay){
16811 var pm = date.add(Date.MONTH, -1);
16812 var prevStart = pm.getDaysInMonth()-startingPos;
16814 this.cells = this.el.select('.fc-day',true);
16815 this.textNodes = this.el.query('.fc-day-number');
16816 this.cells.addClassOnOver('fc-state-hover');
16818 var cells = this.cells.elements;
16819 var textEls = this.textNodes;
16821 Roo.each(cells, function(cell){
16822 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16825 days += startingPos;
16827 // convert everything to numbers so it's fast
16828 var day = 86400000;
16829 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16832 //Roo.log(prevStart);
16834 var today = new Date().clearTime().getTime();
16835 var sel = date.clearTime().getTime();
16836 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16837 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16838 var ddMatch = this.disabledDatesRE;
16839 var ddText = this.disabledDatesText;
16840 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16841 var ddaysText = this.disabledDaysText;
16842 var format = this.format;
16844 var setCellClass = function(cal, cell){
16848 //Roo.log('set Cell Class');
16850 var t = d.getTime();
16854 cell.dateValue = t;
16856 cell.className += " fc-today";
16857 cell.className += " fc-state-highlight";
16858 cell.title = cal.todayText;
16861 // disable highlight in other month..
16862 //cell.className += " fc-state-highlight";
16867 cell.className = " fc-state-disabled";
16868 cell.title = cal.minText;
16872 cell.className = " fc-state-disabled";
16873 cell.title = cal.maxText;
16877 if(ddays.indexOf(d.getDay()) != -1){
16878 cell.title = ddaysText;
16879 cell.className = " fc-state-disabled";
16882 if(ddMatch && format){
16883 var fvalue = d.dateFormat(format);
16884 if(ddMatch.test(fvalue)){
16885 cell.title = ddText.replace("%0", fvalue);
16886 cell.className = " fc-state-disabled";
16890 if (!cell.initialClassName) {
16891 cell.initialClassName = cell.dom.className;
16894 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16899 for(; i < startingPos; i++) {
16900 textEls[i].innerHTML = (++prevStart);
16901 d.setDate(d.getDate()+1);
16903 cells[i].className = "fc-past fc-other-month";
16904 setCellClass(this, cells[i]);
16909 for(; i < days; i++){
16910 intDay = i - startingPos + 1;
16911 textEls[i].innerHTML = (intDay);
16912 d.setDate(d.getDate()+1);
16914 cells[i].className = ''; // "x-date-active";
16915 setCellClass(this, cells[i]);
16919 for(; i < 42; i++) {
16920 textEls[i].innerHTML = (++extraDays);
16921 d.setDate(d.getDate()+1);
16923 cells[i].className = "fc-future fc-other-month";
16924 setCellClass(this, cells[i]);
16927 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16929 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16931 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16932 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16934 if(totalRows != 6){
16935 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16936 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16939 this.fireEvent('monthchange', this, date);
16943 if(!this.internalRender){
16944 var main = this.el.dom.firstChild;
16945 var w = main.offsetWidth;
16946 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16947 Roo.fly(main).setWidth(w);
16948 this.internalRender = true;
16949 // opera does not respect the auto grow header center column
16950 // then, after it gets a width opera refuses to recalculate
16951 // without a second pass
16952 if(Roo.isOpera && !this.secondPass){
16953 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16954 this.secondPass = true;
16955 this.update.defer(10, this, [date]);
16962 findCell : function(dt) {
16963 dt = dt.clearTime().getTime();
16965 this.cells.each(function(c){
16966 //Roo.log("check " +c.dateValue + '?=' + dt);
16967 if(c.dateValue == dt){
16977 findCells : function(ev) {
16978 var s = ev.start.clone().clearTime().getTime();
16980 var e= ev.end.clone().clearTime().getTime();
16983 this.cells.each(function(c){
16984 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16986 if(c.dateValue > e){
16989 if(c.dateValue < s){
16998 // findBestRow: function(cells)
17002 // for (var i =0 ; i < cells.length;i++) {
17003 // ret = Math.max(cells[i].rows || 0,ret);
17010 addItem : function(ev)
17012 // look for vertical location slot in
17013 var cells = this.findCells(ev);
17015 // ev.row = this.findBestRow(cells);
17017 // work out the location.
17021 for(var i =0; i < cells.length; i++) {
17023 cells[i].row = cells[0].row;
17026 cells[i].row = cells[i].row + 1;
17036 if (crow.start.getY() == cells[i].getY()) {
17038 crow.end = cells[i];
17055 cells[0].events.push(ev);
17057 this.calevents.push(ev);
17060 clearEvents: function() {
17062 if(!this.calevents){
17066 Roo.each(this.cells.elements, function(c){
17072 Roo.each(this.calevents, function(e) {
17073 Roo.each(e.els, function(el) {
17074 el.un('mouseenter' ,this.onEventEnter, this);
17075 el.un('mouseleave' ,this.onEventLeave, this);
17080 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17086 renderEvents: function()
17090 this.cells.each(function(c) {
17099 if(c.row != c.events.length){
17100 r = 4 - (4 - (c.row - c.events.length));
17103 c.events = ev.slice(0, r);
17104 c.more = ev.slice(r);
17106 if(c.more.length && c.more.length == 1){
17107 c.events.push(c.more.pop());
17110 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17114 this.cells.each(function(c) {
17116 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17119 for (var e = 0; e < c.events.length; e++){
17120 var ev = c.events[e];
17121 var rows = ev.rows;
17123 for(var i = 0; i < rows.length; i++) {
17125 // how many rows should it span..
17128 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17129 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17131 unselectable : "on",
17134 cls: 'fc-event-inner',
17138 // cls: 'fc-event-time',
17139 // html : cells.length > 1 ? '' : ev.time
17143 cls: 'fc-event-title',
17144 html : String.format('{0}', ev.title)
17151 cls: 'ui-resizable-handle ui-resizable-e',
17152 html : '  '
17159 cfg.cls += ' fc-event-start';
17161 if ((i+1) == rows.length) {
17162 cfg.cls += ' fc-event-end';
17165 var ctr = _this.el.select('.fc-event-container',true).first();
17166 var cg = ctr.createChild(cfg);
17168 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17169 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17171 var r = (c.more.length) ? 1 : 0;
17172 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17173 cg.setWidth(ebox.right - sbox.x -2);
17175 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17176 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17177 cg.on('click', _this.onEventClick, _this, ev);
17188 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17189 style : 'position: absolute',
17190 unselectable : "on",
17193 cls: 'fc-event-inner',
17197 cls: 'fc-event-title',
17205 cls: 'ui-resizable-handle ui-resizable-e',
17206 html : '  '
17212 var ctr = _this.el.select('.fc-event-container',true).first();
17213 var cg = ctr.createChild(cfg);
17215 var sbox = c.select('.fc-day-content',true).first().getBox();
17216 var ebox = c.select('.fc-day-content',true).first().getBox();
17218 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17219 cg.setWidth(ebox.right - sbox.x -2);
17221 cg.on('click', _this.onMoreEventClick, _this, c.more);
17231 onEventEnter: function (e, el,event,d) {
17232 this.fireEvent('evententer', this, el, event);
17235 onEventLeave: function (e, el,event,d) {
17236 this.fireEvent('eventleave', this, el, event);
17239 onEventClick: function (e, el,event,d) {
17240 this.fireEvent('eventclick', this, el, event);
17243 onMonthChange: function () {
17247 onMoreEventClick: function(e, el, more)
17251 this.calpopover.placement = 'right';
17252 this.calpopover.setTitle('More');
17254 this.calpopover.setContent('');
17256 var ctr = this.calpopover.el.select('.popover-content', true).first();
17258 Roo.each(more, function(m){
17260 cls : 'fc-event-hori fc-event-draggable',
17263 var cg = ctr.createChild(cfg);
17265 cg.on('click', _this.onEventClick, _this, m);
17268 this.calpopover.show(el);
17273 onLoad: function ()
17275 this.calevents = [];
17278 if(this.store.getCount() > 0){
17279 this.store.data.each(function(d){
17282 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17283 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17284 time : d.data.start_time,
17285 title : d.data.title,
17286 description : d.data.description,
17287 venue : d.data.venue
17292 this.renderEvents();
17294 if(this.calevents.length && this.loadMask){
17295 this.maskEl.hide();
17299 onBeforeLoad: function()
17301 this.clearEvents();
17303 this.maskEl.show();
17317 * @class Roo.bootstrap.Popover
17318 * @extends Roo.bootstrap.Component
17319 * Bootstrap Popover class
17320 * @cfg {String} html contents of the popover (or false to use children..)
17321 * @cfg {String} title of popover (or false to hide)
17322 * @cfg {String} placement how it is placed
17323 * @cfg {String} trigger click || hover (or false to trigger manually)
17324 * @cfg {String} over what (parent or false to trigger manually.)
17325 * @cfg {Number} delay - delay before showing
17328 * Create a new Popover
17329 * @param {Object} config The config object
17332 Roo.bootstrap.Popover = function(config){
17333 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17339 * After the popover show
17341 * @param {Roo.bootstrap.Popover} this
17346 * After the popover hide
17348 * @param {Roo.bootstrap.Popover} this
17354 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17356 title: 'Fill in a title',
17359 placement : 'right',
17360 trigger : 'hover', // hover
17366 can_build_overlaid : false,
17368 getChildContainer : function()
17370 return this.el.select('.popover-content',true).first();
17373 getAutoCreate : function(){
17376 cls : 'popover roo-dynamic',
17377 style: 'display:block',
17383 cls : 'popover-inner',
17387 cls: 'popover-title',
17391 cls : 'popover-content',
17402 setTitle: function(str)
17405 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17407 setContent: function(str)
17410 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17412 // as it get's added to the bottom of the page.
17413 onRender : function(ct, position)
17415 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17417 var cfg = Roo.apply({}, this.getAutoCreate());
17421 cfg.cls += ' ' + this.cls;
17424 cfg.style = this.style;
17426 //Roo.log("adding to ");
17427 this.el = Roo.get(document.body).createChild(cfg, position);
17428 // Roo.log(this.el);
17433 initEvents : function()
17435 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17436 this.el.enableDisplayMode('block');
17438 if (this.over === false) {
17441 if (this.triggers === false) {
17444 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17445 var triggers = this.trigger ? this.trigger.split(' ') : [];
17446 Roo.each(triggers, function(trigger) {
17448 if (trigger == 'click') {
17449 on_el.on('click', this.toggle, this);
17450 } else if (trigger != 'manual') {
17451 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17452 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17454 on_el.on(eventIn ,this.enter, this);
17455 on_el.on(eventOut, this.leave, this);
17466 toggle : function () {
17467 this.hoverState == 'in' ? this.leave() : this.enter();
17470 enter : function () {
17472 clearTimeout(this.timeout);
17474 this.hoverState = 'in';
17476 if (!this.delay || !this.delay.show) {
17481 this.timeout = setTimeout(function () {
17482 if (_t.hoverState == 'in') {
17485 }, this.delay.show)
17488 leave : function() {
17489 clearTimeout(this.timeout);
17491 this.hoverState = 'out';
17493 if (!this.delay || !this.delay.hide) {
17498 this.timeout = setTimeout(function () {
17499 if (_t.hoverState == 'out') {
17502 }, this.delay.hide)
17505 show : function (on_el)
17508 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17512 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17513 if (this.html !== false) {
17514 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17516 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17517 if (!this.title.length) {
17518 this.el.select('.popover-title',true).hide();
17521 var placement = typeof this.placement == 'function' ?
17522 this.placement.call(this, this.el, on_el) :
17525 var autoToken = /\s?auto?\s?/i;
17526 var autoPlace = autoToken.test(placement);
17528 placement = placement.replace(autoToken, '') || 'top';
17532 //this.el.setXY([0,0]);
17534 this.el.dom.style.display='block';
17535 this.el.addClass(placement);
17537 //this.el.appendTo(on_el);
17539 var p = this.getPosition();
17540 var box = this.el.getBox();
17545 var align = Roo.bootstrap.Popover.alignment[placement];
17548 this.el.alignTo(on_el, align[0],align[1]);
17549 //var arrow = this.el.select('.arrow',true).first();
17550 //arrow.set(align[2],
17552 this.el.addClass('in');
17555 if (this.el.hasClass('fade')) {
17559 this.hoverState = 'in';
17561 this.fireEvent('show', this);
17566 this.el.setXY([0,0]);
17567 this.el.removeClass('in');
17569 this.hoverState = null;
17571 this.fireEvent('hide', this);
17576 Roo.bootstrap.Popover.alignment = {
17577 'left' : ['r-l', [-10,0], 'right'],
17578 'right' : ['l-r', [10,0], 'left'],
17579 'bottom' : ['t-b', [0,10], 'top'],
17580 'top' : [ 'b-t', [0,-10], 'bottom']
17591 * @class Roo.bootstrap.Progress
17592 * @extends Roo.bootstrap.Component
17593 * Bootstrap Progress class
17594 * @cfg {Boolean} striped striped of the progress bar
17595 * @cfg {Boolean} active animated of the progress bar
17599 * Create a new Progress
17600 * @param {Object} config The config object
17603 Roo.bootstrap.Progress = function(config){
17604 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17607 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17612 getAutoCreate : function(){
17620 cfg.cls += ' progress-striped';
17624 cfg.cls += ' active';
17643 * @class Roo.bootstrap.ProgressBar
17644 * @extends Roo.bootstrap.Component
17645 * Bootstrap ProgressBar class
17646 * @cfg {Number} aria_valuenow aria-value now
17647 * @cfg {Number} aria_valuemin aria-value min
17648 * @cfg {Number} aria_valuemax aria-value max
17649 * @cfg {String} label label for the progress bar
17650 * @cfg {String} panel (success | info | warning | danger )
17651 * @cfg {String} role role of the progress bar
17652 * @cfg {String} sr_only text
17656 * Create a new ProgressBar
17657 * @param {Object} config The config object
17660 Roo.bootstrap.ProgressBar = function(config){
17661 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17664 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17668 aria_valuemax : 100,
17674 getAutoCreate : function()
17679 cls: 'progress-bar',
17680 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17692 cfg.role = this.role;
17695 if(this.aria_valuenow){
17696 cfg['aria-valuenow'] = this.aria_valuenow;
17699 if(this.aria_valuemin){
17700 cfg['aria-valuemin'] = this.aria_valuemin;
17703 if(this.aria_valuemax){
17704 cfg['aria-valuemax'] = this.aria_valuemax;
17707 if(this.label && !this.sr_only){
17708 cfg.html = this.label;
17712 cfg.cls += ' progress-bar-' + this.panel;
17718 update : function(aria_valuenow)
17720 this.aria_valuenow = aria_valuenow;
17722 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17737 * @class Roo.bootstrap.TabGroup
17738 * @extends Roo.bootstrap.Column
17739 * Bootstrap Column class
17740 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17741 * @cfg {Boolean} carousel true to make the group behave like a carousel
17742 * @cfg {Boolean} bullets show bullets for the panels
17743 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17744 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17745 * @cfg {Boolean} showarrow (true|false) show arrow default true
17748 * Create a new TabGroup
17749 * @param {Object} config The config object
17752 Roo.bootstrap.TabGroup = function(config){
17753 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17755 this.navId = Roo.id();
17758 Roo.bootstrap.TabGroup.register(this);
17762 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17765 transition : false,
17770 slideOnTouch : false,
17773 getAutoCreate : function()
17775 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17777 cfg.cls += ' tab-content';
17779 if (this.carousel) {
17780 cfg.cls += ' carousel slide';
17783 cls : 'carousel-inner',
17787 if(this.bullets && !Roo.isTouch){
17790 cls : 'carousel-bullets',
17794 if(this.bullets_cls){
17795 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17802 cfg.cn[0].cn.push(bullets);
17805 if(this.showarrow){
17806 cfg.cn[0].cn.push({
17808 class : 'carousel-arrow',
17812 class : 'carousel-prev',
17816 class : 'fa fa-chevron-left'
17822 class : 'carousel-next',
17826 class : 'fa fa-chevron-right'
17839 initEvents: function()
17841 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17842 // this.el.on("touchstart", this.onTouchStart, this);
17845 if(this.autoslide){
17848 this.slideFn = window.setInterval(function() {
17849 _this.showPanelNext();
17853 if(this.showarrow){
17854 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17855 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17861 // onTouchStart : function(e, el, o)
17863 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17867 // this.showPanelNext();
17871 getChildContainer : function()
17873 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17877 * register a Navigation item
17878 * @param {Roo.bootstrap.NavItem} the navitem to add
17880 register : function(item)
17882 this.tabs.push( item);
17883 item.navId = this.navId; // not really needed..
17888 getActivePanel : function()
17891 Roo.each(this.tabs, function(t) {
17901 getPanelByName : function(n)
17904 Roo.each(this.tabs, function(t) {
17905 if (t.tabId == n) {
17913 indexOfPanel : function(p)
17916 Roo.each(this.tabs, function(t,i) {
17917 if (t.tabId == p.tabId) {
17926 * show a specific panel
17927 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17928 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17930 showPanel : function (pan)
17932 if(this.transition || typeof(pan) == 'undefined'){
17933 Roo.log("waiting for the transitionend");
17937 if (typeof(pan) == 'number') {
17938 pan = this.tabs[pan];
17941 if (typeof(pan) == 'string') {
17942 pan = this.getPanelByName(pan);
17945 var cur = this.getActivePanel();
17948 Roo.log('pan or acitve pan is undefined');
17952 if (pan.tabId == this.getActivePanel().tabId) {
17956 if (false === cur.fireEvent('beforedeactivate')) {
17960 if(this.bullets > 0 && !Roo.isTouch){
17961 this.setActiveBullet(this.indexOfPanel(pan));
17964 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17966 this.transition = true;
17967 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17968 var lr = dir == 'next' ? 'left' : 'right';
17969 pan.el.addClass(dir); // or prev
17970 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17971 cur.el.addClass(lr); // or right
17972 pan.el.addClass(lr);
17975 cur.el.on('transitionend', function() {
17976 Roo.log("trans end?");
17978 pan.el.removeClass([lr,dir]);
17979 pan.setActive(true);
17981 cur.el.removeClass([lr]);
17982 cur.setActive(false);
17984 _this.transition = false;
17986 }, this, { single: true } );
17991 cur.setActive(false);
17992 pan.setActive(true);
17997 showPanelNext : function()
17999 var i = this.indexOfPanel(this.getActivePanel());
18001 if (i >= this.tabs.length - 1 && !this.autoslide) {
18005 if (i >= this.tabs.length - 1 && this.autoslide) {
18009 this.showPanel(this.tabs[i+1]);
18012 showPanelPrev : function()
18014 var i = this.indexOfPanel(this.getActivePanel());
18016 if (i < 1 && !this.autoslide) {
18020 if (i < 1 && this.autoslide) {
18021 i = this.tabs.length;
18024 this.showPanel(this.tabs[i-1]);
18028 addBullet: function()
18030 if(!this.bullets || Roo.isTouch){
18033 var ctr = this.el.select('.carousel-bullets',true).first();
18034 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18035 var bullet = ctr.createChild({
18036 cls : 'bullet bullet-' + i
18037 },ctr.dom.lastChild);
18042 bullet.on('click', (function(e, el, o, ii, t){
18044 e.preventDefault();
18046 this.showPanel(ii);
18048 if(this.autoslide && this.slideFn){
18049 clearInterval(this.slideFn);
18050 this.slideFn = window.setInterval(function() {
18051 _this.showPanelNext();
18055 }).createDelegate(this, [i, bullet], true));
18060 setActiveBullet : function(i)
18066 Roo.each(this.el.select('.bullet', true).elements, function(el){
18067 el.removeClass('selected');
18070 var bullet = this.el.select('.bullet-' + i, true).first();
18076 bullet.addClass('selected');
18087 Roo.apply(Roo.bootstrap.TabGroup, {
18091 * register a Navigation Group
18092 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18094 register : function(navgrp)
18096 this.groups[navgrp.navId] = navgrp;
18100 * fetch a Navigation Group based on the navigation ID
18101 * if one does not exist , it will get created.
18102 * @param {string} the navgroup to add
18103 * @returns {Roo.bootstrap.NavGroup} the navgroup
18105 get: function(navId) {
18106 if (typeof(this.groups[navId]) == 'undefined') {
18107 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18109 return this.groups[navId] ;
18124 * @class Roo.bootstrap.TabPanel
18125 * @extends Roo.bootstrap.Component
18126 * Bootstrap TabPanel class
18127 * @cfg {Boolean} active panel active
18128 * @cfg {String} html panel content
18129 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18130 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18131 * @cfg {String} href click to link..
18135 * Create a new TabPanel
18136 * @param {Object} config The config object
18139 Roo.bootstrap.TabPanel = function(config){
18140 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18144 * Fires when the active status changes
18145 * @param {Roo.bootstrap.TabPanel} this
18146 * @param {Boolean} state the new state
18151 * @event beforedeactivate
18152 * Fires before a tab is de-activated - can be used to do validation on a form.
18153 * @param {Roo.bootstrap.TabPanel} this
18154 * @return {Boolean} false if there is an error
18157 'beforedeactivate': true
18160 this.tabId = this.tabId || Roo.id();
18164 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18172 getAutoCreate : function(){
18175 // item is needed for carousel - not sure if it has any effect otherwise
18176 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18177 html: this.html || ''
18181 cfg.cls += ' active';
18185 cfg.tabId = this.tabId;
18192 initEvents: function()
18194 var p = this.parent();
18196 this.navId = this.navId || p.navId;
18198 if (typeof(this.navId) != 'undefined') {
18199 // not really needed.. but just in case.. parent should be a NavGroup.
18200 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18204 var i = tg.tabs.length - 1;
18206 if(this.active && tg.bullets > 0 && i < tg.bullets){
18207 tg.setActiveBullet(i);
18211 this.el.on('click', this.onClick, this);
18214 this.el.on("touchstart", this.onTouchStart, this);
18215 this.el.on("touchmove", this.onTouchMove, this);
18216 this.el.on("touchend", this.onTouchEnd, this);
18221 onRender : function(ct, position)
18223 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18226 setActive : function(state)
18228 Roo.log("panel - set active " + this.tabId + "=" + state);
18230 this.active = state;
18232 this.el.removeClass('active');
18234 } else if (!this.el.hasClass('active')) {
18235 this.el.addClass('active');
18238 this.fireEvent('changed', this, state);
18241 onClick : function(e)
18243 e.preventDefault();
18245 if(!this.href.length){
18249 window.location.href = this.href;
18258 onTouchStart : function(e)
18260 this.swiping = false;
18262 this.startX = e.browserEvent.touches[0].clientX;
18263 this.startY = e.browserEvent.touches[0].clientY;
18266 onTouchMove : function(e)
18268 this.swiping = true;
18270 this.endX = e.browserEvent.touches[0].clientX;
18271 this.endY = e.browserEvent.touches[0].clientY;
18274 onTouchEnd : function(e)
18281 var tabGroup = this.parent();
18283 if(this.endX > this.startX){ // swiping right
18284 tabGroup.showPanelPrev();
18288 if(this.startX > this.endX){ // swiping left
18289 tabGroup.showPanelNext();
18308 * @class Roo.bootstrap.DateField
18309 * @extends Roo.bootstrap.Input
18310 * Bootstrap DateField class
18311 * @cfg {Number} weekStart default 0
18312 * @cfg {String} viewMode default empty, (months|years)
18313 * @cfg {String} minViewMode default empty, (months|years)
18314 * @cfg {Number} startDate default -Infinity
18315 * @cfg {Number} endDate default Infinity
18316 * @cfg {Boolean} todayHighlight default false
18317 * @cfg {Boolean} todayBtn default false
18318 * @cfg {Boolean} calendarWeeks default false
18319 * @cfg {Object} daysOfWeekDisabled default empty
18320 * @cfg {Boolean} singleMode default false (true | false)
18322 * @cfg {Boolean} keyboardNavigation default true
18323 * @cfg {String} language default en
18326 * Create a new DateField
18327 * @param {Object} config The config object
18330 Roo.bootstrap.DateField = function(config){
18331 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18335 * Fires when this field show.
18336 * @param {Roo.bootstrap.DateField} this
18337 * @param {Mixed} date The date value
18342 * Fires when this field hide.
18343 * @param {Roo.bootstrap.DateField} this
18344 * @param {Mixed} date The date value
18349 * Fires when select a date.
18350 * @param {Roo.bootstrap.DateField} this
18351 * @param {Mixed} date The date value
18355 * @event beforeselect
18356 * Fires when before select a date.
18357 * @param {Roo.bootstrap.DateField} this
18358 * @param {Mixed} date The date value
18360 beforeselect : true
18364 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18367 * @cfg {String} format
18368 * The default date format string which can be overriden for localization support. The format must be
18369 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18373 * @cfg {String} altFormats
18374 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18375 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18377 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18385 todayHighlight : false,
18391 keyboardNavigation: true,
18393 calendarWeeks: false,
18395 startDate: -Infinity,
18399 daysOfWeekDisabled: [],
18403 singleMode : false,
18405 UTCDate: function()
18407 return new Date(Date.UTC.apply(Date, arguments));
18410 UTCToday: function()
18412 var today = new Date();
18413 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18416 getDate: function() {
18417 var d = this.getUTCDate();
18418 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18421 getUTCDate: function() {
18425 setDate: function(d) {
18426 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18429 setUTCDate: function(d) {
18431 this.setValue(this.formatDate(this.date));
18434 onRender: function(ct, position)
18437 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18439 this.language = this.language || 'en';
18440 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18441 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18443 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18444 this.format = this.format || 'm/d/y';
18445 this.isInline = false;
18446 this.isInput = true;
18447 this.component = this.el.select('.add-on', true).first() || false;
18448 this.component = (this.component && this.component.length === 0) ? false : this.component;
18449 this.hasInput = this.component && this.inputEl().length;
18451 if (typeof(this.minViewMode === 'string')) {
18452 switch (this.minViewMode) {
18454 this.minViewMode = 1;
18457 this.minViewMode = 2;
18460 this.minViewMode = 0;
18465 if (typeof(this.viewMode === 'string')) {
18466 switch (this.viewMode) {
18479 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18481 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18483 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18485 this.picker().on('mousedown', this.onMousedown, this);
18486 this.picker().on('click', this.onClick, this);
18488 this.picker().addClass('datepicker-dropdown');
18490 this.startViewMode = this.viewMode;
18492 if(this.singleMode){
18493 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18494 v.setVisibilityMode(Roo.Element.DISPLAY);
18498 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18499 v.setStyle('width', '189px');
18503 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18504 if(!this.calendarWeeks){
18509 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18510 v.attr('colspan', function(i, val){
18511 return parseInt(val) + 1;
18516 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18518 this.setStartDate(this.startDate);
18519 this.setEndDate(this.endDate);
18521 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18528 if(this.isInline) {
18533 picker : function()
18535 return this.pickerEl;
18536 // return this.el.select('.datepicker', true).first();
18539 fillDow: function()
18541 var dowCnt = this.weekStart;
18550 if(this.calendarWeeks){
18558 while (dowCnt < this.weekStart + 7) {
18562 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18566 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18569 fillMonths: function()
18572 var months = this.picker().select('>.datepicker-months td', true).first();
18574 months.dom.innerHTML = '';
18580 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18583 months.createChild(month);
18590 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;
18592 if (this.date < this.startDate) {
18593 this.viewDate = new Date(this.startDate);
18594 } else if (this.date > this.endDate) {
18595 this.viewDate = new Date(this.endDate);
18597 this.viewDate = new Date(this.date);
18605 var d = new Date(this.viewDate),
18606 year = d.getUTCFullYear(),
18607 month = d.getUTCMonth(),
18608 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18609 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18610 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18611 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18612 currentDate = this.date && this.date.valueOf(),
18613 today = this.UTCToday();
18615 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18617 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18619 // this.picker.select('>tfoot th.today').
18620 // .text(dates[this.language].today)
18621 // .toggle(this.todayBtn !== false);
18623 this.updateNavArrows();
18626 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18628 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18630 prevMonth.setUTCDate(day);
18632 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18634 var nextMonth = new Date(prevMonth);
18636 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18638 nextMonth = nextMonth.valueOf();
18640 var fillMonths = false;
18642 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18644 while(prevMonth.valueOf() <= nextMonth) {
18647 if (prevMonth.getUTCDay() === this.weekStart) {
18649 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18657 if(this.calendarWeeks){
18658 // ISO 8601: First week contains first thursday.
18659 // ISO also states week starts on Monday, but we can be more abstract here.
18661 // Start of current week: based on weekstart/current date
18662 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18663 // Thursday of this week
18664 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18665 // First Thursday of year, year from thursday
18666 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18667 // Calendar week: ms between thursdays, div ms per day, div 7 days
18668 calWeek = (th - yth) / 864e5 / 7 + 1;
18670 fillMonths.cn.push({
18678 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18680 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18683 if (this.todayHighlight &&
18684 prevMonth.getUTCFullYear() == today.getFullYear() &&
18685 prevMonth.getUTCMonth() == today.getMonth() &&
18686 prevMonth.getUTCDate() == today.getDate()) {
18687 clsName += ' today';
18690 if (currentDate && prevMonth.valueOf() === currentDate) {
18691 clsName += ' active';
18694 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18695 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18696 clsName += ' disabled';
18699 fillMonths.cn.push({
18701 cls: 'day ' + clsName,
18702 html: prevMonth.getDate()
18705 prevMonth.setDate(prevMonth.getDate()+1);
18708 var currentYear = this.date && this.date.getUTCFullYear();
18709 var currentMonth = this.date && this.date.getUTCMonth();
18711 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18713 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18714 v.removeClass('active');
18716 if(currentYear === year && k === currentMonth){
18717 v.addClass('active');
18720 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18721 v.addClass('disabled');
18727 year = parseInt(year/10, 10) * 10;
18729 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18731 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18734 for (var i = -1; i < 11; i++) {
18735 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18737 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18745 showMode: function(dir)
18748 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18751 Roo.each(this.picker().select('>div',true).elements, function(v){
18752 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18755 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18760 if(this.isInline) {
18764 this.picker().removeClass(['bottom', 'top']);
18766 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18768 * place to the top of element!
18772 this.picker().addClass('top');
18773 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18778 this.picker().addClass('bottom');
18780 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18783 parseDate : function(value)
18785 if(!value || value instanceof Date){
18788 var v = Date.parseDate(value, this.format);
18789 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18790 v = Date.parseDate(value, 'Y-m-d');
18792 if(!v && this.altFormats){
18793 if(!this.altFormatsArray){
18794 this.altFormatsArray = this.altFormats.split("|");
18796 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18797 v = Date.parseDate(value, this.altFormatsArray[i]);
18803 formatDate : function(date, fmt)
18805 return (!date || !(date instanceof Date)) ?
18806 date : date.dateFormat(fmt || this.format);
18809 onFocus : function()
18811 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18815 onBlur : function()
18817 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18819 var d = this.inputEl().getValue();
18828 this.picker().show();
18832 this.fireEvent('show', this, this.date);
18837 if(this.isInline) {
18840 this.picker().hide();
18841 this.viewMode = this.startViewMode;
18844 this.fireEvent('hide', this, this.date);
18848 onMousedown: function(e)
18850 e.stopPropagation();
18851 e.preventDefault();
18856 Roo.bootstrap.DateField.superclass.keyup.call(this);
18860 setValue: function(v)
18862 if(this.fireEvent('beforeselect', this, v) !== false){
18863 var d = new Date(this.parseDate(v) ).clearTime();
18865 if(isNaN(d.getTime())){
18866 this.date = this.viewDate = '';
18867 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18871 v = this.formatDate(d);
18873 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18875 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18879 this.fireEvent('select', this, this.date);
18883 getValue: function()
18885 return this.formatDate(this.date);
18888 fireKey: function(e)
18890 if (!this.picker().isVisible()){
18891 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18897 var dateChanged = false,
18899 newDate, newViewDate;
18904 e.preventDefault();
18908 if (!this.keyboardNavigation) {
18911 dir = e.keyCode == 37 ? -1 : 1;
18914 newDate = this.moveYear(this.date, dir);
18915 newViewDate = this.moveYear(this.viewDate, dir);
18916 } else if (e.shiftKey){
18917 newDate = this.moveMonth(this.date, dir);
18918 newViewDate = this.moveMonth(this.viewDate, dir);
18920 newDate = new Date(this.date);
18921 newDate.setUTCDate(this.date.getUTCDate() + dir);
18922 newViewDate = new Date(this.viewDate);
18923 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18925 if (this.dateWithinRange(newDate)){
18926 this.date = newDate;
18927 this.viewDate = newViewDate;
18928 this.setValue(this.formatDate(this.date));
18930 e.preventDefault();
18931 dateChanged = true;
18936 if (!this.keyboardNavigation) {
18939 dir = e.keyCode == 38 ? -1 : 1;
18941 newDate = this.moveYear(this.date, dir);
18942 newViewDate = this.moveYear(this.viewDate, dir);
18943 } else if (e.shiftKey){
18944 newDate = this.moveMonth(this.date, dir);
18945 newViewDate = this.moveMonth(this.viewDate, dir);
18947 newDate = new Date(this.date);
18948 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18949 newViewDate = new Date(this.viewDate);
18950 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18952 if (this.dateWithinRange(newDate)){
18953 this.date = newDate;
18954 this.viewDate = newViewDate;
18955 this.setValue(this.formatDate(this.date));
18957 e.preventDefault();
18958 dateChanged = true;
18962 this.setValue(this.formatDate(this.date));
18964 e.preventDefault();
18967 this.setValue(this.formatDate(this.date));
18981 onClick: function(e)
18983 e.stopPropagation();
18984 e.preventDefault();
18986 var target = e.getTarget();
18988 if(target.nodeName.toLowerCase() === 'i'){
18989 target = Roo.get(target).dom.parentNode;
18992 var nodeName = target.nodeName;
18993 var className = target.className;
18994 var html = target.innerHTML;
18995 //Roo.log(nodeName);
18997 switch(nodeName.toLowerCase()) {
18999 switch(className) {
19005 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19006 switch(this.viewMode){
19008 this.viewDate = this.moveMonth(this.viewDate, dir);
19012 this.viewDate = this.moveYear(this.viewDate, dir);
19018 var date = new Date();
19019 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19021 this.setValue(this.formatDate(this.date));
19028 if (className.indexOf('disabled') < 0) {
19029 this.viewDate.setUTCDate(1);
19030 if (className.indexOf('month') > -1) {
19031 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19033 var year = parseInt(html, 10) || 0;
19034 this.viewDate.setUTCFullYear(year);
19038 if(this.singleMode){
19039 this.setValue(this.formatDate(this.viewDate));
19050 //Roo.log(className);
19051 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19052 var day = parseInt(html, 10) || 1;
19053 var year = this.viewDate.getUTCFullYear(),
19054 month = this.viewDate.getUTCMonth();
19056 if (className.indexOf('old') > -1) {
19063 } else if (className.indexOf('new') > -1) {
19071 //Roo.log([year,month,day]);
19072 this.date = this.UTCDate(year, month, day,0,0,0,0);
19073 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19075 //Roo.log(this.formatDate(this.date));
19076 this.setValue(this.formatDate(this.date));
19083 setStartDate: function(startDate)
19085 this.startDate = startDate || -Infinity;
19086 if (this.startDate !== -Infinity) {
19087 this.startDate = this.parseDate(this.startDate);
19090 this.updateNavArrows();
19093 setEndDate: function(endDate)
19095 this.endDate = endDate || Infinity;
19096 if (this.endDate !== Infinity) {
19097 this.endDate = this.parseDate(this.endDate);
19100 this.updateNavArrows();
19103 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19105 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19106 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19107 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19109 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19110 return parseInt(d, 10);
19113 this.updateNavArrows();
19116 updateNavArrows: function()
19118 if(this.singleMode){
19122 var d = new Date(this.viewDate),
19123 year = d.getUTCFullYear(),
19124 month = d.getUTCMonth();
19126 Roo.each(this.picker().select('.prev', true).elements, function(v){
19128 switch (this.viewMode) {
19131 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19137 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19144 Roo.each(this.picker().select('.next', true).elements, function(v){
19146 switch (this.viewMode) {
19149 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19155 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19163 moveMonth: function(date, dir)
19168 var new_date = new Date(date.valueOf()),
19169 day = new_date.getUTCDate(),
19170 month = new_date.getUTCMonth(),
19171 mag = Math.abs(dir),
19173 dir = dir > 0 ? 1 : -1;
19176 // If going back one month, make sure month is not current month
19177 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19179 return new_date.getUTCMonth() == month;
19181 // If going forward one month, make sure month is as expected
19182 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19184 return new_date.getUTCMonth() != new_month;
19186 new_month = month + dir;
19187 new_date.setUTCMonth(new_month);
19188 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19189 if (new_month < 0 || new_month > 11) {
19190 new_month = (new_month + 12) % 12;
19193 // For magnitudes >1, move one month at a time...
19194 for (var i=0; i<mag; i++) {
19195 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19196 new_date = this.moveMonth(new_date, dir);
19198 // ...then reset the day, keeping it in the new month
19199 new_month = new_date.getUTCMonth();
19200 new_date.setUTCDate(day);
19202 return new_month != new_date.getUTCMonth();
19205 // Common date-resetting loop -- if date is beyond end of month, make it
19208 new_date.setUTCDate(--day);
19209 new_date.setUTCMonth(new_month);
19214 moveYear: function(date, dir)
19216 return this.moveMonth(date, dir*12);
19219 dateWithinRange: function(date)
19221 return date >= this.startDate && date <= this.endDate;
19227 this.picker().remove();
19230 validateValue : function(value)
19232 if(this.getVisibilityEl().hasClass('hidden')){
19236 if(value.length < 1) {
19237 if(this.allowBlank){
19243 if(value.length < this.minLength){
19246 if(value.length > this.maxLength){
19250 var vt = Roo.form.VTypes;
19251 if(!vt[this.vtype](value, this)){
19255 if(typeof this.validator == "function"){
19256 var msg = this.validator(value);
19262 if(this.regex && !this.regex.test(value)){
19266 if(typeof(this.parseDate(value)) == 'undefined'){
19270 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19274 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19282 setVisible : function(visible)
19288 this.getEl().removeClass('hidden');
19294 this.getEl().addClass('hidden');
19299 Roo.apply(Roo.bootstrap.DateField, {
19310 html: '<i class="fa fa-arrow-left"/>'
19320 html: '<i class="fa fa-arrow-right"/>'
19362 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19363 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19364 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19365 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19366 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19379 navFnc: 'FullYear',
19384 navFnc: 'FullYear',
19389 Roo.apply(Roo.bootstrap.DateField, {
19393 cls: 'datepicker dropdown-menu roo-dynamic',
19397 cls: 'datepicker-days',
19401 cls: 'table-condensed',
19403 Roo.bootstrap.DateField.head,
19407 Roo.bootstrap.DateField.footer
19414 cls: 'datepicker-months',
19418 cls: 'table-condensed',
19420 Roo.bootstrap.DateField.head,
19421 Roo.bootstrap.DateField.content,
19422 Roo.bootstrap.DateField.footer
19429 cls: 'datepicker-years',
19433 cls: 'table-condensed',
19435 Roo.bootstrap.DateField.head,
19436 Roo.bootstrap.DateField.content,
19437 Roo.bootstrap.DateField.footer
19456 * @class Roo.bootstrap.TimeField
19457 * @extends Roo.bootstrap.Input
19458 * Bootstrap DateField class
19462 * Create a new TimeField
19463 * @param {Object} config The config object
19466 Roo.bootstrap.TimeField = function(config){
19467 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19471 * Fires when this field show.
19472 * @param {Roo.bootstrap.DateField} thisthis
19473 * @param {Mixed} date The date value
19478 * Fires when this field hide.
19479 * @param {Roo.bootstrap.DateField} this
19480 * @param {Mixed} date The date value
19485 * Fires when select a date.
19486 * @param {Roo.bootstrap.DateField} this
19487 * @param {Mixed} date The date value
19493 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19496 * @cfg {String} format
19497 * The default time format string which can be overriden for localization support. The format must be
19498 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19502 onRender: function(ct, position)
19505 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19507 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19509 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19511 this.pop = this.picker().select('>.datepicker-time',true).first();
19512 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19514 this.picker().on('mousedown', this.onMousedown, this);
19515 this.picker().on('click', this.onClick, this);
19517 this.picker().addClass('datepicker-dropdown');
19522 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19523 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19524 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19525 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19526 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19527 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19531 fireKey: function(e){
19532 if (!this.picker().isVisible()){
19533 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19539 e.preventDefault();
19547 this.onTogglePeriod();
19550 this.onIncrementMinutes();
19553 this.onDecrementMinutes();
19562 onClick: function(e) {
19563 e.stopPropagation();
19564 e.preventDefault();
19567 picker : function()
19569 return this.el.select('.datepicker', true).first();
19572 fillTime: function()
19574 var time = this.pop.select('tbody', true).first();
19576 time.dom.innerHTML = '';
19591 cls: 'hours-up glyphicon glyphicon-chevron-up'
19611 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19632 cls: 'timepicker-hour',
19647 cls: 'timepicker-minute',
19662 cls: 'btn btn-primary period',
19684 cls: 'hours-down glyphicon glyphicon-chevron-down'
19704 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19722 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19729 var hours = this.time.getHours();
19730 var minutes = this.time.getMinutes();
19743 hours = hours - 12;
19747 hours = '0' + hours;
19751 minutes = '0' + minutes;
19754 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19755 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19756 this.pop.select('button', true).first().dom.innerHTML = period;
19762 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19764 var cls = ['bottom'];
19766 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19773 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19778 this.picker().addClass(cls.join('-'));
19782 Roo.each(cls, function(c){
19784 _this.picker().setTop(_this.inputEl().getHeight());
19788 _this.picker().setTop(0 - _this.picker().getHeight());
19793 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19797 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19804 onFocus : function()
19806 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19810 onBlur : function()
19812 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19818 this.picker().show();
19823 this.fireEvent('show', this, this.date);
19828 this.picker().hide();
19831 this.fireEvent('hide', this, this.date);
19834 setTime : function()
19837 this.setValue(this.time.format(this.format));
19839 this.fireEvent('select', this, this.date);
19844 onMousedown: function(e){
19845 e.stopPropagation();
19846 e.preventDefault();
19849 onIncrementHours: function()
19851 Roo.log('onIncrementHours');
19852 this.time = this.time.add(Date.HOUR, 1);
19857 onDecrementHours: function()
19859 Roo.log('onDecrementHours');
19860 this.time = this.time.add(Date.HOUR, -1);
19864 onIncrementMinutes: function()
19866 Roo.log('onIncrementMinutes');
19867 this.time = this.time.add(Date.MINUTE, 1);
19871 onDecrementMinutes: function()
19873 Roo.log('onDecrementMinutes');
19874 this.time = this.time.add(Date.MINUTE, -1);
19878 onTogglePeriod: function()
19880 Roo.log('onTogglePeriod');
19881 this.time = this.time.add(Date.HOUR, 12);
19888 Roo.apply(Roo.bootstrap.TimeField, {
19918 cls: 'btn btn-info ok',
19930 Roo.apply(Roo.bootstrap.TimeField, {
19934 cls: 'datepicker dropdown-menu',
19938 cls: 'datepicker-time',
19942 cls: 'table-condensed',
19944 Roo.bootstrap.TimeField.content,
19945 Roo.bootstrap.TimeField.footer
19964 * @class Roo.bootstrap.MonthField
19965 * @extends Roo.bootstrap.Input
19966 * Bootstrap MonthField class
19968 * @cfg {String} language default en
19971 * Create a new MonthField
19972 * @param {Object} config The config object
19975 Roo.bootstrap.MonthField = function(config){
19976 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19981 * Fires when this field show.
19982 * @param {Roo.bootstrap.MonthField} this
19983 * @param {Mixed} date The date value
19988 * Fires when this field hide.
19989 * @param {Roo.bootstrap.MonthField} this
19990 * @param {Mixed} date The date value
19995 * Fires when select a date.
19996 * @param {Roo.bootstrap.MonthField} this
19997 * @param {String} oldvalue The old value
19998 * @param {String} newvalue The new value
20004 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20006 onRender: function(ct, position)
20009 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20011 this.language = this.language || 'en';
20012 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20013 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20015 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20016 this.isInline = false;
20017 this.isInput = true;
20018 this.component = this.el.select('.add-on', true).first() || false;
20019 this.component = (this.component && this.component.length === 0) ? false : this.component;
20020 this.hasInput = this.component && this.inputEL().length;
20022 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20024 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20026 this.picker().on('mousedown', this.onMousedown, this);
20027 this.picker().on('click', this.onClick, this);
20029 this.picker().addClass('datepicker-dropdown');
20031 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20032 v.setStyle('width', '189px');
20039 if(this.isInline) {
20045 setValue: function(v, suppressEvent)
20047 var o = this.getValue();
20049 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20053 if(suppressEvent !== true){
20054 this.fireEvent('select', this, o, v);
20059 getValue: function()
20064 onClick: function(e)
20066 e.stopPropagation();
20067 e.preventDefault();
20069 var target = e.getTarget();
20071 if(target.nodeName.toLowerCase() === 'i'){
20072 target = Roo.get(target).dom.parentNode;
20075 var nodeName = target.nodeName;
20076 var className = target.className;
20077 var html = target.innerHTML;
20079 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20083 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20085 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20091 picker : function()
20093 return this.pickerEl;
20096 fillMonths: function()
20099 var months = this.picker().select('>.datepicker-months td', true).first();
20101 months.dom.innerHTML = '';
20107 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20110 months.createChild(month);
20119 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20120 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20123 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20124 e.removeClass('active');
20126 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20127 e.addClass('active');
20134 if(this.isInline) {
20138 this.picker().removeClass(['bottom', 'top']);
20140 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20142 * place to the top of element!
20146 this.picker().addClass('top');
20147 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20152 this.picker().addClass('bottom');
20154 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20157 onFocus : function()
20159 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20163 onBlur : function()
20165 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20167 var d = this.inputEl().getValue();
20176 this.picker().show();
20177 this.picker().select('>.datepicker-months', true).first().show();
20181 this.fireEvent('show', this, this.date);
20186 if(this.isInline) {
20189 this.picker().hide();
20190 this.fireEvent('hide', this, this.date);
20194 onMousedown: function(e)
20196 e.stopPropagation();
20197 e.preventDefault();
20202 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20206 fireKey: function(e)
20208 if (!this.picker().isVisible()){
20209 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20220 e.preventDefault();
20224 dir = e.keyCode == 37 ? -1 : 1;
20226 this.vIndex = this.vIndex + dir;
20228 if(this.vIndex < 0){
20232 if(this.vIndex > 11){
20236 if(isNaN(this.vIndex)){
20240 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20246 dir = e.keyCode == 38 ? -1 : 1;
20248 this.vIndex = this.vIndex + dir * 4;
20250 if(this.vIndex < 0){
20254 if(this.vIndex > 11){
20258 if(isNaN(this.vIndex)){
20262 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20267 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20268 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20272 e.preventDefault();
20275 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20276 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20292 this.picker().remove();
20297 Roo.apply(Roo.bootstrap.MonthField, {
20316 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20317 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20322 Roo.apply(Roo.bootstrap.MonthField, {
20326 cls: 'datepicker dropdown-menu roo-dynamic',
20330 cls: 'datepicker-months',
20334 cls: 'table-condensed',
20336 Roo.bootstrap.DateField.content
20356 * @class Roo.bootstrap.CheckBox
20357 * @extends Roo.bootstrap.Input
20358 * Bootstrap CheckBox class
20360 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20361 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20362 * @cfg {String} boxLabel The text that appears beside the checkbox
20363 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20364 * @cfg {Boolean} checked initnal the element
20365 * @cfg {Boolean} inline inline the element (default false)
20366 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20367 * @cfg {String} tooltip label tooltip
20370 * Create a new CheckBox
20371 * @param {Object} config The config object
20374 Roo.bootstrap.CheckBox = function(config){
20375 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20380 * Fires when the element is checked or unchecked.
20381 * @param {Roo.bootstrap.CheckBox} this This input
20382 * @param {Boolean} checked The new checked value
20387 * Fires when the element is click.
20388 * @param {Roo.bootstrap.CheckBox} this This input
20395 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20397 inputType: 'checkbox',
20406 getAutoCreate : function()
20408 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20414 cfg.cls = 'form-group ' + this.inputType; //input-group
20417 cfg.cls += ' ' + this.inputType + '-inline';
20423 type : this.inputType,
20424 value : this.inputValue,
20425 cls : 'roo-' + this.inputType, //'form-box',
20426 placeholder : this.placeholder || ''
20430 if(this.inputType != 'radio'){
20434 cls : 'roo-hidden-value',
20435 value : this.checked ? this.inputValue : this.valueOff
20440 if (this.weight) { // Validity check?
20441 cfg.cls += " " + this.inputType + "-" + this.weight;
20444 if (this.disabled) {
20445 input.disabled=true;
20449 input.checked = this.checked;
20454 input.name = this.name;
20456 if(this.inputType != 'radio'){
20457 hidden.name = this.name;
20458 input.name = '_hidden_' + this.name;
20463 input.cls += ' input-' + this.size;
20468 ['xs','sm','md','lg'].map(function(size){
20469 if (settings[size]) {
20470 cfg.cls += ' col-' + size + '-' + settings[size];
20474 var inputblock = input;
20476 if (this.before || this.after) {
20479 cls : 'input-group',
20484 inputblock.cn.push({
20486 cls : 'input-group-addon',
20491 inputblock.cn.push(input);
20493 if(this.inputType != 'radio'){
20494 inputblock.cn.push(hidden);
20498 inputblock.cn.push({
20500 cls : 'input-group-addon',
20507 if (align ==='left' && this.fieldLabel.length) {
20508 // Roo.log("left and has label");
20513 cls : 'control-label',
20514 html : this.fieldLabel
20524 if(this.labelWidth > 12){
20525 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20528 if(this.labelWidth < 13 && this.labelmd == 0){
20529 this.labelmd = this.labelWidth;
20532 if(this.labellg > 0){
20533 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20534 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20537 if(this.labelmd > 0){
20538 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20539 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20542 if(this.labelsm > 0){
20543 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20544 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20547 if(this.labelxs > 0){
20548 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20549 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20552 } else if ( this.fieldLabel.length) {
20553 // Roo.log(" label");
20557 tag: this.boxLabel ? 'span' : 'label',
20559 cls: 'control-label box-input-label',
20560 //cls : 'input-group-addon',
20561 html : this.fieldLabel
20570 // Roo.log(" no label && no align");
20571 cfg.cn = [ inputblock ] ;
20577 var boxLabelCfg = {
20579 //'for': id, // box label is handled by onclick - so no for...
20581 html: this.boxLabel
20585 boxLabelCfg.tooltip = this.tooltip;
20588 cfg.cn.push(boxLabelCfg);
20591 if(this.inputType != 'radio'){
20592 cfg.cn.push(hidden);
20600 * return the real input element.
20602 inputEl: function ()
20604 return this.el.select('input.roo-' + this.inputType,true).first();
20606 hiddenEl: function ()
20608 return this.el.select('input.roo-hidden-value',true).first();
20611 labelEl: function()
20613 return this.el.select('label.control-label',true).first();
20615 /* depricated... */
20619 return this.labelEl();
20622 boxLabelEl: function()
20624 return this.el.select('label.box-label',true).first();
20627 initEvents : function()
20629 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20631 this.inputEl().on('click', this.onClick, this);
20633 if (this.boxLabel) {
20634 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20637 this.startValue = this.getValue();
20640 Roo.bootstrap.CheckBox.register(this);
20644 onClick : function(e)
20646 if(this.fireEvent('click', this, e) !== false){
20647 this.setChecked(!this.checked);
20652 setChecked : function(state,suppressEvent)
20654 this.startValue = this.getValue();
20656 if(this.inputType == 'radio'){
20658 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20659 e.dom.checked = false;
20662 this.inputEl().dom.checked = true;
20664 this.inputEl().dom.value = this.inputValue;
20666 if(suppressEvent !== true){
20667 this.fireEvent('check', this, true);
20675 this.checked = state;
20677 this.inputEl().dom.checked = state;
20680 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20682 if(suppressEvent !== true){
20683 this.fireEvent('check', this, state);
20689 getValue : function()
20691 if(this.inputType == 'radio'){
20692 return this.getGroupValue();
20695 return this.hiddenEl().dom.value;
20699 getGroupValue : function()
20701 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20705 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20708 setValue : function(v,suppressEvent)
20710 if(this.inputType == 'radio'){
20711 this.setGroupValue(v, suppressEvent);
20715 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20720 setGroupValue : function(v, suppressEvent)
20722 this.startValue = this.getValue();
20724 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20725 e.dom.checked = false;
20727 if(e.dom.value == v){
20728 e.dom.checked = true;
20732 if(suppressEvent !== true){
20733 this.fireEvent('check', this, true);
20741 validate : function()
20743 if(this.getVisibilityEl().hasClass('hidden')){
20749 (this.inputType == 'radio' && this.validateRadio()) ||
20750 (this.inputType == 'checkbox' && this.validateCheckbox())
20756 this.markInvalid();
20760 validateRadio : function()
20762 if(this.getVisibilityEl().hasClass('hidden')){
20766 if(this.allowBlank){
20772 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20773 if(!e.dom.checked){
20785 validateCheckbox : function()
20788 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20789 //return (this.getValue() == this.inputValue) ? true : false;
20792 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20800 for(var i in group){
20801 if(group[i].el.isVisible(true)){
20809 for(var i in group){
20814 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20821 * Mark this field as valid
20823 markValid : function()
20827 this.fireEvent('valid', this);
20829 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20832 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20839 if(this.inputType == 'radio'){
20840 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20841 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20842 e.findParent('.form-group', false, true).addClass(_this.validClass);
20849 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20850 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20854 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20860 for(var i in group){
20861 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20862 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20867 * Mark this field as invalid
20868 * @param {String} msg The validation message
20870 markInvalid : function(msg)
20872 if(this.allowBlank){
20878 this.fireEvent('invalid', this, msg);
20880 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20883 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20887 label.markInvalid();
20890 if(this.inputType == 'radio'){
20891 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20892 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20893 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20900 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20901 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20905 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20911 for(var i in group){
20912 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20913 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20918 clearInvalid : function()
20920 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20922 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20924 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20926 if (label && label.iconEl) {
20927 label.iconEl.removeClass(label.validClass);
20928 label.iconEl.removeClass(label.invalidClass);
20932 disable : function()
20934 if(this.inputType != 'radio'){
20935 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20942 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20943 _this.getActionEl().addClass(this.disabledClass);
20944 e.dom.disabled = true;
20948 this.disabled = true;
20949 this.fireEvent("disable", this);
20953 enable : function()
20955 if(this.inputType != 'radio'){
20956 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20963 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20964 _this.getActionEl().removeClass(this.disabledClass);
20965 e.dom.disabled = false;
20969 this.disabled = false;
20970 this.fireEvent("enable", this);
20974 setBoxLabel : function(v)
20979 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20985 Roo.apply(Roo.bootstrap.CheckBox, {
20990 * register a CheckBox Group
20991 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20993 register : function(checkbox)
20995 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20996 this.groups[checkbox.groupId] = {};
20999 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21003 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21007 * fetch a CheckBox Group based on the group ID
21008 * @param {string} the group ID
21009 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21011 get: function(groupId) {
21012 if (typeof(this.groups[groupId]) == 'undefined') {
21016 return this.groups[groupId] ;
21029 * @class Roo.bootstrap.Radio
21030 * @extends Roo.bootstrap.Component
21031 * Bootstrap Radio class
21032 * @cfg {String} boxLabel - the label associated
21033 * @cfg {String} value - the value of radio
21036 * Create a new Radio
21037 * @param {Object} config The config object
21039 Roo.bootstrap.Radio = function(config){
21040 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21044 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21050 getAutoCreate : function()
21054 cls : 'form-group radio',
21059 html : this.boxLabel
21067 initEvents : function()
21069 this.parent().register(this);
21071 this.el.on('click', this.onClick, this);
21075 onClick : function(e)
21077 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21078 this.setChecked(true);
21082 setChecked : function(state, suppressEvent)
21084 this.parent().setValue(this.value, suppressEvent);
21088 setBoxLabel : function(v)
21093 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21108 * @class Roo.bootstrap.SecurePass
21109 * @extends Roo.bootstrap.Input
21110 * Bootstrap SecurePass class
21114 * Create a new SecurePass
21115 * @param {Object} config The config object
21118 Roo.bootstrap.SecurePass = function (config) {
21119 // these go here, so the translation tool can replace them..
21121 PwdEmpty: "Please type a password, and then retype it to confirm.",
21122 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21123 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21124 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21125 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21126 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21127 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21128 TooWeak: "Your password is Too Weak."
21130 this.meterLabel = "Password strength:";
21131 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21132 this.meterClass = [
21133 "roo-password-meter-tooweak",
21134 "roo-password-meter-weak",
21135 "roo-password-meter-medium",
21136 "roo-password-meter-strong",
21137 "roo-password-meter-grey"
21142 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21145 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21147 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21149 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21150 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21151 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21152 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21153 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21154 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21155 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21165 * @cfg {String/Object} Label for the strength meter (defaults to
21166 * 'Password strength:')
21171 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21172 * ['Weak', 'Medium', 'Strong'])
21175 pwdStrengths: false,
21188 initEvents: function ()
21190 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21192 if (this.el.is('input[type=password]') && Roo.isSafari) {
21193 this.el.on('keydown', this.SafariOnKeyDown, this);
21196 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21199 onRender: function (ct, position)
21201 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21202 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21203 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21205 this.trigger.createChild({
21210 cls: 'roo-password-meter-grey col-xs-12',
21213 //width: this.meterWidth + 'px'
21217 cls: 'roo-password-meter-text'
21223 if (this.hideTrigger) {
21224 this.trigger.setDisplayed(false);
21226 this.setSize(this.width || '', this.height || '');
21229 onDestroy: function ()
21231 if (this.trigger) {
21232 this.trigger.removeAllListeners();
21233 this.trigger.remove();
21236 this.wrap.remove();
21238 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21241 checkStrength: function ()
21243 var pwd = this.inputEl().getValue();
21244 if (pwd == this._lastPwd) {
21249 if (this.ClientSideStrongPassword(pwd)) {
21251 } else if (this.ClientSideMediumPassword(pwd)) {
21253 } else if (this.ClientSideWeakPassword(pwd)) {
21259 Roo.log('strength1: ' + strength);
21261 //var pm = this.trigger.child('div/div/div').dom;
21262 var pm = this.trigger.child('div/div');
21263 pm.removeClass(this.meterClass);
21264 pm.addClass(this.meterClass[strength]);
21267 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21269 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21271 this._lastPwd = pwd;
21275 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21277 this._lastPwd = '';
21279 var pm = this.trigger.child('div/div');
21280 pm.removeClass(this.meterClass);
21281 pm.addClass('roo-password-meter-grey');
21284 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21287 this.inputEl().dom.type='password';
21290 validateValue: function (value)
21293 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21296 if (value.length == 0) {
21297 if (this.allowBlank) {
21298 this.clearInvalid();
21302 this.markInvalid(this.errors.PwdEmpty);
21303 this.errorMsg = this.errors.PwdEmpty;
21311 if ('[\x21-\x7e]*'.match(value)) {
21312 this.markInvalid(this.errors.PwdBadChar);
21313 this.errorMsg = this.errors.PwdBadChar;
21316 if (value.length < 6) {
21317 this.markInvalid(this.errors.PwdShort);
21318 this.errorMsg = this.errors.PwdShort;
21321 if (value.length > 16) {
21322 this.markInvalid(this.errors.PwdLong);
21323 this.errorMsg = this.errors.PwdLong;
21327 if (this.ClientSideStrongPassword(value)) {
21329 } else if (this.ClientSideMediumPassword(value)) {
21331 } else if (this.ClientSideWeakPassword(value)) {
21338 if (strength < 2) {
21339 //this.markInvalid(this.errors.TooWeak);
21340 this.errorMsg = this.errors.TooWeak;
21345 console.log('strength2: ' + strength);
21347 //var pm = this.trigger.child('div/div/div').dom;
21349 var pm = this.trigger.child('div/div');
21350 pm.removeClass(this.meterClass);
21351 pm.addClass(this.meterClass[strength]);
21353 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21355 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21357 this.errorMsg = '';
21361 CharacterSetChecks: function (type)
21364 this.fResult = false;
21367 isctype: function (character, type)
21370 case this.kCapitalLetter:
21371 if (character >= 'A' && character <= 'Z') {
21376 case this.kSmallLetter:
21377 if (character >= 'a' && character <= 'z') {
21383 if (character >= '0' && character <= '9') {
21388 case this.kPunctuation:
21389 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21400 IsLongEnough: function (pwd, size)
21402 return !(pwd == null || isNaN(size) || pwd.length < size);
21405 SpansEnoughCharacterSets: function (word, nb)
21407 if (!this.IsLongEnough(word, nb))
21412 var characterSetChecks = new Array(
21413 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21414 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21417 for (var index = 0; index < word.length; ++index) {
21418 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21419 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21420 characterSetChecks[nCharSet].fResult = true;
21427 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21428 if (characterSetChecks[nCharSet].fResult) {
21433 if (nCharSets < nb) {
21439 ClientSideStrongPassword: function (pwd)
21441 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21444 ClientSideMediumPassword: function (pwd)
21446 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21449 ClientSideWeakPassword: function (pwd)
21451 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21454 })//<script type="text/javascript">
21457 * Based Ext JS Library 1.1.1
21458 * Copyright(c) 2006-2007, Ext JS, LLC.
21464 * @class Roo.HtmlEditorCore
21465 * @extends Roo.Component
21466 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21468 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21471 Roo.HtmlEditorCore = function(config){
21474 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21479 * @event initialize
21480 * Fires when the editor is fully initialized (including the iframe)
21481 * @param {Roo.HtmlEditorCore} this
21486 * Fires when the editor is first receives the focus. Any insertion must wait
21487 * until after this event.
21488 * @param {Roo.HtmlEditorCore} this
21492 * @event beforesync
21493 * Fires before the textarea is updated with content from the editor iframe. Return false
21494 * to cancel the sync.
21495 * @param {Roo.HtmlEditorCore} this
21496 * @param {String} html
21500 * @event beforepush
21501 * Fires before the iframe editor is updated with content from the textarea. Return false
21502 * to cancel the push.
21503 * @param {Roo.HtmlEditorCore} this
21504 * @param {String} html
21509 * Fires when the textarea is updated with content from the editor iframe.
21510 * @param {Roo.HtmlEditorCore} this
21511 * @param {String} html
21516 * Fires when the iframe editor is updated with content from the textarea.
21517 * @param {Roo.HtmlEditorCore} this
21518 * @param {String} html
21523 * @event editorevent
21524 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21525 * @param {Roo.HtmlEditorCore} this
21531 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21533 // defaults : white / black...
21534 this.applyBlacklists();
21541 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21545 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21551 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21556 * @cfg {Number} height (in pixels)
21560 * @cfg {Number} width (in pixels)
21565 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21568 stylesheets: false,
21573 // private properties
21574 validationEvent : false,
21576 initialized : false,
21578 sourceEditMode : false,
21579 onFocus : Roo.emptyFn,
21581 hideMode:'offsets',
21585 // blacklist + whitelisted elements..
21592 * Protected method that will not generally be called directly. It
21593 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21594 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21596 getDocMarkup : function(){
21600 // inherit styels from page...??
21601 if (this.stylesheets === false) {
21603 Roo.get(document.head).select('style').each(function(node) {
21604 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21607 Roo.get(document.head).select('link').each(function(node) {
21608 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21611 } else if (!this.stylesheets.length) {
21613 st = '<style type="text/css">' +
21614 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21617 st = '<style type="text/css">' +
21622 st += '<style type="text/css">' +
21623 'IMG { cursor: pointer } ' +
21626 var cls = 'roo-htmleditor-body';
21628 if(this.bodyCls.length){
21629 cls += ' ' + this.bodyCls;
21632 return '<html><head>' + st +
21633 //<style type="text/css">' +
21634 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21636 ' </head><body class="' + cls + '"></body></html>';
21640 onRender : function(ct, position)
21643 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21644 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21647 this.el.dom.style.border = '0 none';
21648 this.el.dom.setAttribute('tabIndex', -1);
21649 this.el.addClass('x-hidden hide');
21653 if(Roo.isIE){ // fix IE 1px bogus margin
21654 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21658 this.frameId = Roo.id();
21662 var iframe = this.owner.wrap.createChild({
21664 cls: 'form-control', // bootstrap..
21666 name: this.frameId,
21667 frameBorder : 'no',
21668 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21673 this.iframe = iframe.dom;
21675 this.assignDocWin();
21677 this.doc.designMode = 'on';
21680 this.doc.write(this.getDocMarkup());
21684 var task = { // must defer to wait for browser to be ready
21686 //console.log("run task?" + this.doc.readyState);
21687 this.assignDocWin();
21688 if(this.doc.body || this.doc.readyState == 'complete'){
21690 this.doc.designMode="on";
21694 Roo.TaskMgr.stop(task);
21695 this.initEditor.defer(10, this);
21702 Roo.TaskMgr.start(task);
21707 onResize : function(w, h)
21709 Roo.log('resize: ' +w + ',' + h );
21710 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21714 if(typeof w == 'number'){
21716 this.iframe.style.width = w + 'px';
21718 if(typeof h == 'number'){
21720 this.iframe.style.height = h + 'px';
21722 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21729 * Toggles the editor between standard and source edit mode.
21730 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21732 toggleSourceEdit : function(sourceEditMode){
21734 this.sourceEditMode = sourceEditMode === true;
21736 if(this.sourceEditMode){
21738 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21741 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21742 //this.iframe.className = '';
21745 //this.setSize(this.owner.wrap.getSize());
21746 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21753 * Protected method that will not generally be called directly. If you need/want
21754 * custom HTML cleanup, this is the method you should override.
21755 * @param {String} html The HTML to be cleaned
21756 * return {String} The cleaned HTML
21758 cleanHtml : function(html){
21759 html = String(html);
21760 if(html.length > 5){
21761 if(Roo.isSafari){ // strip safari nonsense
21762 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21765 if(html == ' '){
21772 * HTML Editor -> Textarea
21773 * Protected method that will not generally be called directly. Syncs the contents
21774 * of the editor iframe with the textarea.
21776 syncValue : function(){
21777 if(this.initialized){
21778 var bd = (this.doc.body || this.doc.documentElement);
21779 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21780 var html = bd.innerHTML;
21782 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21783 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21785 html = '<div style="'+m[0]+'">' + html + '</div>';
21788 html = this.cleanHtml(html);
21789 // fix up the special chars.. normaly like back quotes in word...
21790 // however we do not want to do this with chinese..
21791 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21792 var cc = b.charCodeAt();
21794 (cc >= 0x4E00 && cc < 0xA000 ) ||
21795 (cc >= 0x3400 && cc < 0x4E00 ) ||
21796 (cc >= 0xf900 && cc < 0xfb00 )
21802 if(this.owner.fireEvent('beforesync', this, html) !== false){
21803 this.el.dom.value = html;
21804 this.owner.fireEvent('sync', this, html);
21810 * Protected method that will not generally be called directly. Pushes the value of the textarea
21811 * into the iframe editor.
21813 pushValue : function(){
21814 if(this.initialized){
21815 var v = this.el.dom.value.trim();
21817 // if(v.length < 1){
21821 if(this.owner.fireEvent('beforepush', this, v) !== false){
21822 var d = (this.doc.body || this.doc.documentElement);
21824 this.cleanUpPaste();
21825 this.el.dom.value = d.innerHTML;
21826 this.owner.fireEvent('push', this, v);
21832 deferFocus : function(){
21833 this.focus.defer(10, this);
21837 focus : function(){
21838 if(this.win && !this.sourceEditMode){
21845 assignDocWin: function()
21847 var iframe = this.iframe;
21850 this.doc = iframe.contentWindow.document;
21851 this.win = iframe.contentWindow;
21853 // if (!Roo.get(this.frameId)) {
21856 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21857 // this.win = Roo.get(this.frameId).dom.contentWindow;
21859 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21863 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21864 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21869 initEditor : function(){
21870 //console.log("INIT EDITOR");
21871 this.assignDocWin();
21875 this.doc.designMode="on";
21877 this.doc.write(this.getDocMarkup());
21880 var dbody = (this.doc.body || this.doc.documentElement);
21881 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21882 // this copies styles from the containing element into thsi one..
21883 // not sure why we need all of this..
21884 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21886 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21887 //ss['background-attachment'] = 'fixed'; // w3c
21888 dbody.bgProperties = 'fixed'; // ie
21889 //Roo.DomHelper.applyStyles(dbody, ss);
21890 Roo.EventManager.on(this.doc, {
21891 //'mousedown': this.onEditorEvent,
21892 'mouseup': this.onEditorEvent,
21893 'dblclick': this.onEditorEvent,
21894 'click': this.onEditorEvent,
21895 'keyup': this.onEditorEvent,
21900 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21902 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21903 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21905 this.initialized = true;
21907 this.owner.fireEvent('initialize', this);
21912 onDestroy : function(){
21918 //for (var i =0; i < this.toolbars.length;i++) {
21919 // // fixme - ask toolbars for heights?
21920 // this.toolbars[i].onDestroy();
21923 //this.wrap.dom.innerHTML = '';
21924 //this.wrap.remove();
21929 onFirstFocus : function(){
21931 this.assignDocWin();
21934 this.activated = true;
21937 if(Roo.isGecko){ // prevent silly gecko errors
21939 var s = this.win.getSelection();
21940 if(!s.focusNode || s.focusNode.nodeType != 3){
21941 var r = s.getRangeAt(0);
21942 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21947 this.execCmd('useCSS', true);
21948 this.execCmd('styleWithCSS', false);
21951 this.owner.fireEvent('activate', this);
21955 adjustFont: function(btn){
21956 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21957 //if(Roo.isSafari){ // safari
21960 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21961 if(Roo.isSafari){ // safari
21962 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21963 v = (v < 10) ? 10 : v;
21964 v = (v > 48) ? 48 : v;
21965 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21970 v = Math.max(1, v+adjust);
21972 this.execCmd('FontSize', v );
21975 onEditorEvent : function(e)
21977 this.owner.fireEvent('editorevent', this, e);
21978 // this.updateToolbar();
21979 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21982 insertTag : function(tg)
21984 // could be a bit smarter... -> wrap the current selected tRoo..
21985 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21987 range = this.createRange(this.getSelection());
21988 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21989 wrappingNode.appendChild(range.extractContents());
21990 range.insertNode(wrappingNode);
21997 this.execCmd("formatblock", tg);
22001 insertText : function(txt)
22005 var range = this.createRange();
22006 range.deleteContents();
22007 //alert(Sender.getAttribute('label'));
22009 range.insertNode(this.doc.createTextNode(txt));
22015 * Executes a Midas editor command on the editor document and performs necessary focus and
22016 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22017 * @param {String} cmd The Midas command
22018 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22020 relayCmd : function(cmd, value){
22022 this.execCmd(cmd, value);
22023 this.owner.fireEvent('editorevent', this);
22024 //this.updateToolbar();
22025 this.owner.deferFocus();
22029 * Executes a Midas editor command directly on the editor document.
22030 * For visual commands, you should use {@link #relayCmd} instead.
22031 * <b>This should only be called after the editor is initialized.</b>
22032 * @param {String} cmd The Midas command
22033 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22035 execCmd : function(cmd, value){
22036 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22043 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22045 * @param {String} text | dom node..
22047 insertAtCursor : function(text)
22050 if(!this.activated){
22056 var r = this.doc.selection.createRange();
22067 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22071 // from jquery ui (MIT licenced)
22073 var win = this.win;
22075 if (win.getSelection && win.getSelection().getRangeAt) {
22076 range = win.getSelection().getRangeAt(0);
22077 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22078 range.insertNode(node);
22079 } else if (win.document.selection && win.document.selection.createRange) {
22080 // no firefox support
22081 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22082 win.document.selection.createRange().pasteHTML(txt);
22084 // no firefox support
22085 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22086 this.execCmd('InsertHTML', txt);
22095 mozKeyPress : function(e){
22097 var c = e.getCharCode(), cmd;
22100 c = String.fromCharCode(c).toLowerCase();
22114 this.cleanUpPaste.defer(100, this);
22122 e.preventDefault();
22130 fixKeys : function(){ // load time branching for fastest keydown performance
22132 return function(e){
22133 var k = e.getKey(), r;
22136 r = this.doc.selection.createRange();
22139 r.pasteHTML('    ');
22146 r = this.doc.selection.createRange();
22148 var target = r.parentElement();
22149 if(!target || target.tagName.toLowerCase() != 'li'){
22151 r.pasteHTML('<br />');
22157 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22158 this.cleanUpPaste.defer(100, this);
22164 }else if(Roo.isOpera){
22165 return function(e){
22166 var k = e.getKey();
22170 this.execCmd('InsertHTML','    ');
22173 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22174 this.cleanUpPaste.defer(100, this);
22179 }else if(Roo.isSafari){
22180 return function(e){
22181 var k = e.getKey();
22185 this.execCmd('InsertText','\t');
22189 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22190 this.cleanUpPaste.defer(100, this);
22198 getAllAncestors: function()
22200 var p = this.getSelectedNode();
22203 a.push(p); // push blank onto stack..
22204 p = this.getParentElement();
22208 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22212 a.push(this.doc.body);
22216 lastSelNode : false,
22219 getSelection : function()
22221 this.assignDocWin();
22222 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22225 getSelectedNode: function()
22227 // this may only work on Gecko!!!
22229 // should we cache this!!!!
22234 var range = this.createRange(this.getSelection()).cloneRange();
22237 var parent = range.parentElement();
22239 var testRange = range.duplicate();
22240 testRange.moveToElementText(parent);
22241 if (testRange.inRange(range)) {
22244 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22247 parent = parent.parentElement;
22252 // is ancestor a text element.
22253 var ac = range.commonAncestorContainer;
22254 if (ac.nodeType == 3) {
22255 ac = ac.parentNode;
22258 var ar = ac.childNodes;
22261 var other_nodes = [];
22262 var has_other_nodes = false;
22263 for (var i=0;i<ar.length;i++) {
22264 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22267 // fullly contained node.
22269 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22274 // probably selected..
22275 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22276 other_nodes.push(ar[i]);
22280 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22285 has_other_nodes = true;
22287 if (!nodes.length && other_nodes.length) {
22288 nodes= other_nodes;
22290 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22296 createRange: function(sel)
22298 // this has strange effects when using with
22299 // top toolbar - not sure if it's a great idea.
22300 //this.editor.contentWindow.focus();
22301 if (typeof sel != "undefined") {
22303 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22305 return this.doc.createRange();
22308 return this.doc.createRange();
22311 getParentElement: function()
22314 this.assignDocWin();
22315 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22317 var range = this.createRange(sel);
22320 var p = range.commonAncestorContainer;
22321 while (p.nodeType == 3) { // text node
22332 * Range intersection.. the hard stuff...
22336 * [ -- selected range --- ]
22340 * if end is before start or hits it. fail.
22341 * if start is after end or hits it fail.
22343 * if either hits (but other is outside. - then it's not
22349 // @see http://www.thismuchiknow.co.uk/?p=64.
22350 rangeIntersectsNode : function(range, node)
22352 var nodeRange = node.ownerDocument.createRange();
22354 nodeRange.selectNode(node);
22356 nodeRange.selectNodeContents(node);
22359 var rangeStartRange = range.cloneRange();
22360 rangeStartRange.collapse(true);
22362 var rangeEndRange = range.cloneRange();
22363 rangeEndRange.collapse(false);
22365 var nodeStartRange = nodeRange.cloneRange();
22366 nodeStartRange.collapse(true);
22368 var nodeEndRange = nodeRange.cloneRange();
22369 nodeEndRange.collapse(false);
22371 return rangeStartRange.compareBoundaryPoints(
22372 Range.START_TO_START, nodeEndRange) == -1 &&
22373 rangeEndRange.compareBoundaryPoints(
22374 Range.START_TO_START, nodeStartRange) == 1;
22378 rangeCompareNode : function(range, node)
22380 var nodeRange = node.ownerDocument.createRange();
22382 nodeRange.selectNode(node);
22384 nodeRange.selectNodeContents(node);
22388 range.collapse(true);
22390 nodeRange.collapse(true);
22392 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22393 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22395 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22397 var nodeIsBefore = ss == 1;
22398 var nodeIsAfter = ee == -1;
22400 if (nodeIsBefore && nodeIsAfter) {
22403 if (!nodeIsBefore && nodeIsAfter) {
22404 return 1; //right trailed.
22407 if (nodeIsBefore && !nodeIsAfter) {
22408 return 2; // left trailed.
22414 // private? - in a new class?
22415 cleanUpPaste : function()
22417 // cleans up the whole document..
22418 Roo.log('cleanuppaste');
22420 this.cleanUpChildren(this.doc.body);
22421 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22422 if (clean != this.doc.body.innerHTML) {
22423 this.doc.body.innerHTML = clean;
22428 cleanWordChars : function(input) {// change the chars to hex code
22429 var he = Roo.HtmlEditorCore;
22431 var output = input;
22432 Roo.each(he.swapCodes, function(sw) {
22433 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22435 output = output.replace(swapper, sw[1]);
22442 cleanUpChildren : function (n)
22444 if (!n.childNodes.length) {
22447 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22448 this.cleanUpChild(n.childNodes[i]);
22455 cleanUpChild : function (node)
22458 //console.log(node);
22459 if (node.nodeName == "#text") {
22460 // clean up silly Windows -- stuff?
22463 if (node.nodeName == "#comment") {
22464 node.parentNode.removeChild(node);
22465 // clean up silly Windows -- stuff?
22468 var lcname = node.tagName.toLowerCase();
22469 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22470 // whitelist of tags..
22472 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22474 node.parentNode.removeChild(node);
22479 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22481 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22482 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22484 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22485 // remove_keep_children = true;
22488 if (remove_keep_children) {
22489 this.cleanUpChildren(node);
22490 // inserts everything just before this node...
22491 while (node.childNodes.length) {
22492 var cn = node.childNodes[0];
22493 node.removeChild(cn);
22494 node.parentNode.insertBefore(cn, node);
22496 node.parentNode.removeChild(node);
22500 if (!node.attributes || !node.attributes.length) {
22501 this.cleanUpChildren(node);
22505 function cleanAttr(n,v)
22508 if (v.match(/^\./) || v.match(/^\//)) {
22511 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22514 if (v.match(/^#/)) {
22517 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22518 node.removeAttribute(n);
22522 var cwhite = this.cwhite;
22523 var cblack = this.cblack;
22525 function cleanStyle(n,v)
22527 if (v.match(/expression/)) { //XSS?? should we even bother..
22528 node.removeAttribute(n);
22532 var parts = v.split(/;/);
22535 Roo.each(parts, function(p) {
22536 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22540 var l = p.split(':').shift().replace(/\s+/g,'');
22541 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22543 if ( cwhite.length && cblack.indexOf(l) > -1) {
22544 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22545 //node.removeAttribute(n);
22549 // only allow 'c whitelisted system attributes'
22550 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22551 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22552 //node.removeAttribute(n);
22562 if (clean.length) {
22563 node.setAttribute(n, clean.join(';'));
22565 node.removeAttribute(n);
22571 for (var i = node.attributes.length-1; i > -1 ; i--) {
22572 var a = node.attributes[i];
22575 if (a.name.toLowerCase().substr(0,2)=='on') {
22576 node.removeAttribute(a.name);
22579 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22580 node.removeAttribute(a.name);
22583 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22584 cleanAttr(a.name,a.value); // fixme..
22587 if (a.name == 'style') {
22588 cleanStyle(a.name,a.value);
22591 /// clean up MS crap..
22592 // tecnically this should be a list of valid class'es..
22595 if (a.name == 'class') {
22596 if (a.value.match(/^Mso/)) {
22597 node.className = '';
22600 if (a.value.match(/^body$/)) {
22601 node.className = '';
22612 this.cleanUpChildren(node);
22618 * Clean up MS wordisms...
22620 cleanWord : function(node)
22625 this.cleanWord(this.doc.body);
22628 if (node.nodeName == "#text") {
22629 // clean up silly Windows -- stuff?
22632 if (node.nodeName == "#comment") {
22633 node.parentNode.removeChild(node);
22634 // clean up silly Windows -- stuff?
22638 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22639 node.parentNode.removeChild(node);
22643 // remove - but keep children..
22644 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22645 while (node.childNodes.length) {
22646 var cn = node.childNodes[0];
22647 node.removeChild(cn);
22648 node.parentNode.insertBefore(cn, node);
22650 node.parentNode.removeChild(node);
22651 this.iterateChildren(node, this.cleanWord);
22655 if (node.className.length) {
22657 var cn = node.className.split(/\W+/);
22659 Roo.each(cn, function(cls) {
22660 if (cls.match(/Mso[a-zA-Z]+/)) {
22665 node.className = cna.length ? cna.join(' ') : '';
22667 node.removeAttribute("class");
22671 if (node.hasAttribute("lang")) {
22672 node.removeAttribute("lang");
22675 if (node.hasAttribute("style")) {
22677 var styles = node.getAttribute("style").split(";");
22679 Roo.each(styles, function(s) {
22680 if (!s.match(/:/)) {
22683 var kv = s.split(":");
22684 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22687 // what ever is left... we allow.
22690 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22691 if (!nstyle.length) {
22692 node.removeAttribute('style');
22695 this.iterateChildren(node, this.cleanWord);
22701 * iterateChildren of a Node, calling fn each time, using this as the scole..
22702 * @param {DomNode} node node to iterate children of.
22703 * @param {Function} fn method of this class to call on each item.
22705 iterateChildren : function(node, fn)
22707 if (!node.childNodes.length) {
22710 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22711 fn.call(this, node.childNodes[i])
22717 * cleanTableWidths.
22719 * Quite often pasting from word etc.. results in tables with column and widths.
22720 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22723 cleanTableWidths : function(node)
22728 this.cleanTableWidths(this.doc.body);
22733 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22736 Roo.log(node.tagName);
22737 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22738 this.iterateChildren(node, this.cleanTableWidths);
22741 if (node.hasAttribute('width')) {
22742 node.removeAttribute('width');
22746 if (node.hasAttribute("style")) {
22749 var styles = node.getAttribute("style").split(";");
22751 Roo.each(styles, function(s) {
22752 if (!s.match(/:/)) {
22755 var kv = s.split(":");
22756 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22759 // what ever is left... we allow.
22762 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22763 if (!nstyle.length) {
22764 node.removeAttribute('style');
22768 this.iterateChildren(node, this.cleanTableWidths);
22776 domToHTML : function(currentElement, depth, nopadtext) {
22778 depth = depth || 0;
22779 nopadtext = nopadtext || false;
22781 if (!currentElement) {
22782 return this.domToHTML(this.doc.body);
22785 //Roo.log(currentElement);
22787 var allText = false;
22788 var nodeName = currentElement.nodeName;
22789 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22791 if (nodeName == '#text') {
22793 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22798 if (nodeName != 'BODY') {
22801 // Prints the node tagName, such as <A>, <IMG>, etc
22804 for(i = 0; i < currentElement.attributes.length;i++) {
22806 var aname = currentElement.attributes.item(i).name;
22807 if (!currentElement.attributes.item(i).value.length) {
22810 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22813 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22822 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22825 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22830 // Traverse the tree
22832 var currentElementChild = currentElement.childNodes.item(i);
22833 var allText = true;
22834 var innerHTML = '';
22836 while (currentElementChild) {
22837 // Formatting code (indent the tree so it looks nice on the screen)
22838 var nopad = nopadtext;
22839 if (lastnode == 'SPAN') {
22843 if (currentElementChild.nodeName == '#text') {
22844 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22845 toadd = nopadtext ? toadd : toadd.trim();
22846 if (!nopad && toadd.length > 80) {
22847 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22849 innerHTML += toadd;
22852 currentElementChild = currentElement.childNodes.item(i);
22858 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22860 // Recursively traverse the tree structure of the child node
22861 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22862 lastnode = currentElementChild.nodeName;
22864 currentElementChild=currentElement.childNodes.item(i);
22870 // The remaining code is mostly for formatting the tree
22871 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22876 ret+= "</"+tagName+">";
22882 applyBlacklists : function()
22884 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22885 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22889 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22890 if (b.indexOf(tag) > -1) {
22893 this.white.push(tag);
22897 Roo.each(w, function(tag) {
22898 if (b.indexOf(tag) > -1) {
22901 if (this.white.indexOf(tag) > -1) {
22904 this.white.push(tag);
22909 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22910 if (w.indexOf(tag) > -1) {
22913 this.black.push(tag);
22917 Roo.each(b, function(tag) {
22918 if (w.indexOf(tag) > -1) {
22921 if (this.black.indexOf(tag) > -1) {
22924 this.black.push(tag);
22929 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22930 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22934 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22935 if (b.indexOf(tag) > -1) {
22938 this.cwhite.push(tag);
22942 Roo.each(w, function(tag) {
22943 if (b.indexOf(tag) > -1) {
22946 if (this.cwhite.indexOf(tag) > -1) {
22949 this.cwhite.push(tag);
22954 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22955 if (w.indexOf(tag) > -1) {
22958 this.cblack.push(tag);
22962 Roo.each(b, function(tag) {
22963 if (w.indexOf(tag) > -1) {
22966 if (this.cblack.indexOf(tag) > -1) {
22969 this.cblack.push(tag);
22974 setStylesheets : function(stylesheets)
22976 if(typeof(stylesheets) == 'string'){
22977 Roo.get(this.iframe.contentDocument.head).createChild({
22979 rel : 'stylesheet',
22988 Roo.each(stylesheets, function(s) {
22993 Roo.get(_this.iframe.contentDocument.head).createChild({
22995 rel : 'stylesheet',
23004 removeStylesheets : function()
23008 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23013 setStyle : function(style)
23015 Roo.get(this.iframe.contentDocument.head).createChild({
23024 // hide stuff that is not compatible
23038 * @event specialkey
23042 * @cfg {String} fieldClass @hide
23045 * @cfg {String} focusClass @hide
23048 * @cfg {String} autoCreate @hide
23051 * @cfg {String} inputType @hide
23054 * @cfg {String} invalidClass @hide
23057 * @cfg {String} invalidText @hide
23060 * @cfg {String} msgFx @hide
23063 * @cfg {String} validateOnBlur @hide
23067 Roo.HtmlEditorCore.white = [
23068 'area', 'br', 'img', 'input', 'hr', 'wbr',
23070 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23071 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23072 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23073 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23074 'table', 'ul', 'xmp',
23076 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23079 'dir', 'menu', 'ol', 'ul', 'dl',
23085 Roo.HtmlEditorCore.black = [
23086 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23088 'base', 'basefont', 'bgsound', 'blink', 'body',
23089 'frame', 'frameset', 'head', 'html', 'ilayer',
23090 'iframe', 'layer', 'link', 'meta', 'object',
23091 'script', 'style' ,'title', 'xml' // clean later..
23093 Roo.HtmlEditorCore.clean = [
23094 'script', 'style', 'title', 'xml'
23096 Roo.HtmlEditorCore.remove = [
23101 Roo.HtmlEditorCore.ablack = [
23105 Roo.HtmlEditorCore.aclean = [
23106 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23110 Roo.HtmlEditorCore.pwhite= [
23111 'http', 'https', 'mailto'
23114 // white listed style attributes.
23115 Roo.HtmlEditorCore.cwhite= [
23116 // 'text-align', /// default is to allow most things..
23122 // black listed style attributes.
23123 Roo.HtmlEditorCore.cblack= [
23124 // 'font-size' -- this can be set by the project
23128 Roo.HtmlEditorCore.swapCodes =[
23147 * @class Roo.bootstrap.HtmlEditor
23148 * @extends Roo.bootstrap.TextArea
23149 * Bootstrap HtmlEditor class
23152 * Create a new HtmlEditor
23153 * @param {Object} config The config object
23156 Roo.bootstrap.HtmlEditor = function(config){
23157 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23158 if (!this.toolbars) {
23159 this.toolbars = [];
23162 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23165 * @event initialize
23166 * Fires when the editor is fully initialized (including the iframe)
23167 * @param {HtmlEditor} this
23172 * Fires when the editor is first receives the focus. Any insertion must wait
23173 * until after this event.
23174 * @param {HtmlEditor} this
23178 * @event beforesync
23179 * Fires before the textarea is updated with content from the editor iframe. Return false
23180 * to cancel the sync.
23181 * @param {HtmlEditor} this
23182 * @param {String} html
23186 * @event beforepush
23187 * Fires before the iframe editor is updated with content from the textarea. Return false
23188 * to cancel the push.
23189 * @param {HtmlEditor} this
23190 * @param {String} html
23195 * Fires when the textarea is updated with content from the editor iframe.
23196 * @param {HtmlEditor} this
23197 * @param {String} html
23202 * Fires when the iframe editor is updated with content from the textarea.
23203 * @param {HtmlEditor} this
23204 * @param {String} html
23208 * @event editmodechange
23209 * Fires when the editor switches edit modes
23210 * @param {HtmlEditor} this
23211 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23213 editmodechange: true,
23215 * @event editorevent
23216 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23217 * @param {HtmlEditor} this
23221 * @event firstfocus
23222 * Fires when on first focus - needed by toolbars..
23223 * @param {HtmlEditor} this
23228 * Auto save the htmlEditor value as a file into Events
23229 * @param {HtmlEditor} this
23233 * @event savedpreview
23234 * preview the saved version of htmlEditor
23235 * @param {HtmlEditor} this
23242 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23246 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23251 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23256 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23261 * @cfg {Number} height (in pixels)
23265 * @cfg {Number} width (in pixels)
23270 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23273 stylesheets: false,
23278 // private properties
23279 validationEvent : false,
23281 initialized : false,
23284 onFocus : Roo.emptyFn,
23286 hideMode:'offsets',
23288 tbContainer : false,
23292 toolbarContainer :function() {
23293 return this.wrap.select('.x-html-editor-tb',true).first();
23297 * Protected method that will not generally be called directly. It
23298 * is called when the editor creates its toolbar. Override this method if you need to
23299 * add custom toolbar buttons.
23300 * @param {HtmlEditor} editor
23302 createToolbar : function(){
23303 Roo.log('renewing');
23304 Roo.log("create toolbars");
23306 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23307 this.toolbars[0].render(this.toolbarContainer());
23311 // if (!editor.toolbars || !editor.toolbars.length) {
23312 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23315 // for (var i =0 ; i < editor.toolbars.length;i++) {
23316 // editor.toolbars[i] = Roo.factory(
23317 // typeof(editor.toolbars[i]) == 'string' ?
23318 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23319 // Roo.bootstrap.HtmlEditor);
23320 // editor.toolbars[i].init(editor);
23326 onRender : function(ct, position)
23328 // Roo.log("Call onRender: " + this.xtype);
23330 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23332 this.wrap = this.inputEl().wrap({
23333 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23336 this.editorcore.onRender(ct, position);
23338 if (this.resizable) {
23339 this.resizeEl = new Roo.Resizable(this.wrap, {
23343 minHeight : this.height,
23344 height: this.height,
23345 handles : this.resizable,
23348 resize : function(r, w, h) {
23349 _t.onResize(w,h); // -something
23355 this.createToolbar(this);
23358 if(!this.width && this.resizable){
23359 this.setSize(this.wrap.getSize());
23361 if (this.resizeEl) {
23362 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23363 // should trigger onReize..
23369 onResize : function(w, h)
23371 Roo.log('resize: ' +w + ',' + h );
23372 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23376 if(this.inputEl() ){
23377 if(typeof w == 'number'){
23378 var aw = w - this.wrap.getFrameWidth('lr');
23379 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23382 if(typeof h == 'number'){
23383 var tbh = -11; // fixme it needs to tool bar size!
23384 for (var i =0; i < this.toolbars.length;i++) {
23385 // fixme - ask toolbars for heights?
23386 tbh += this.toolbars[i].el.getHeight();
23387 //if (this.toolbars[i].footer) {
23388 // tbh += this.toolbars[i].footer.el.getHeight();
23396 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23397 ah -= 5; // knock a few pixes off for look..
23398 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23402 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23403 this.editorcore.onResize(ew,eh);
23408 * Toggles the editor between standard and source edit mode.
23409 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23411 toggleSourceEdit : function(sourceEditMode)
23413 this.editorcore.toggleSourceEdit(sourceEditMode);
23415 if(this.editorcore.sourceEditMode){
23416 Roo.log('editor - showing textarea');
23419 // Roo.log(this.syncValue());
23421 this.inputEl().removeClass(['hide', 'x-hidden']);
23422 this.inputEl().dom.removeAttribute('tabIndex');
23423 this.inputEl().focus();
23425 Roo.log('editor - hiding textarea');
23427 // Roo.log(this.pushValue());
23430 this.inputEl().addClass(['hide', 'x-hidden']);
23431 this.inputEl().dom.setAttribute('tabIndex', -1);
23432 //this.deferFocus();
23435 if(this.resizable){
23436 this.setSize(this.wrap.getSize());
23439 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23442 // private (for BoxComponent)
23443 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23445 // private (for BoxComponent)
23446 getResizeEl : function(){
23450 // private (for BoxComponent)
23451 getPositionEl : function(){
23456 initEvents : function(){
23457 this.originalValue = this.getValue();
23461 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23464 // markInvalid : Roo.emptyFn,
23466 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23469 // clearInvalid : Roo.emptyFn,
23471 setValue : function(v){
23472 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23473 this.editorcore.pushValue();
23478 deferFocus : function(){
23479 this.focus.defer(10, this);
23483 focus : function(){
23484 this.editorcore.focus();
23490 onDestroy : function(){
23496 for (var i =0; i < this.toolbars.length;i++) {
23497 // fixme - ask toolbars for heights?
23498 this.toolbars[i].onDestroy();
23501 this.wrap.dom.innerHTML = '';
23502 this.wrap.remove();
23507 onFirstFocus : function(){
23508 //Roo.log("onFirstFocus");
23509 this.editorcore.onFirstFocus();
23510 for (var i =0; i < this.toolbars.length;i++) {
23511 this.toolbars[i].onFirstFocus();
23517 syncValue : function()
23519 this.editorcore.syncValue();
23522 pushValue : function()
23524 this.editorcore.pushValue();
23528 // hide stuff that is not compatible
23542 * @event specialkey
23546 * @cfg {String} fieldClass @hide
23549 * @cfg {String} focusClass @hide
23552 * @cfg {String} autoCreate @hide
23555 * @cfg {String} inputType @hide
23558 * @cfg {String} invalidClass @hide
23561 * @cfg {String} invalidText @hide
23564 * @cfg {String} msgFx @hide
23567 * @cfg {String} validateOnBlur @hide
23576 Roo.namespace('Roo.bootstrap.htmleditor');
23578 * @class Roo.bootstrap.HtmlEditorToolbar1
23583 new Roo.bootstrap.HtmlEditor({
23586 new Roo.bootstrap.HtmlEditorToolbar1({
23587 disable : { fonts: 1 , format: 1, ..., ... , ...],
23593 * @cfg {Object} disable List of elements to disable..
23594 * @cfg {Array} btns List of additional buttons.
23598 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23601 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23604 Roo.apply(this, config);
23606 // default disabled, based on 'good practice'..
23607 this.disable = this.disable || {};
23608 Roo.applyIf(this.disable, {
23611 specialElements : true
23613 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23615 this.editor = config.editor;
23616 this.editorcore = config.editor.editorcore;
23618 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23620 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23621 // dont call parent... till later.
23623 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23628 editorcore : false,
23633 "h1","h2","h3","h4","h5","h6",
23635 "abbr", "acronym", "address", "cite", "samp", "var",
23639 onRender : function(ct, position)
23641 // Roo.log("Call onRender: " + this.xtype);
23643 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23645 this.el.dom.style.marginBottom = '0';
23647 var editorcore = this.editorcore;
23648 var editor= this.editor;
23651 var btn = function(id,cmd , toggle, handler, html){
23653 var event = toggle ? 'toggle' : 'click';
23658 xns: Roo.bootstrap,
23661 enableToggle:toggle !== false,
23663 pressed : toggle ? false : null,
23666 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23667 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23673 // var cb_box = function...
23678 xns: Roo.bootstrap,
23679 glyphicon : 'font',
23683 xns: Roo.bootstrap,
23687 Roo.each(this.formats, function(f) {
23688 style.menu.items.push({
23690 xns: Roo.bootstrap,
23691 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23696 editorcore.insertTag(this.tagname);
23703 children.push(style);
23705 btn('bold',false,true);
23706 btn('italic',false,true);
23707 btn('align-left', 'justifyleft',true);
23708 btn('align-center', 'justifycenter',true);
23709 btn('align-right' , 'justifyright',true);
23710 btn('link', false, false, function(btn) {
23711 //Roo.log("create link?");
23712 var url = prompt(this.createLinkText, this.defaultLinkValue);
23713 if(url && url != 'http:/'+'/'){
23714 this.editorcore.relayCmd('createlink', url);
23717 btn('list','insertunorderedlist',true);
23718 btn('pencil', false,true, function(btn){
23720 this.toggleSourceEdit(btn.pressed);
23723 if (this.editor.btns.length > 0) {
23724 for (var i = 0; i<this.editor.btns.length; i++) {
23725 children.push(this.editor.btns[i]);
23733 xns: Roo.bootstrap,
23738 xns: Roo.bootstrap,
23743 cog.menu.items.push({
23745 xns: Roo.bootstrap,
23746 html : Clean styles,
23751 editorcore.insertTag(this.tagname);
23760 this.xtype = 'NavSimplebar';
23762 for(var i=0;i< children.length;i++) {
23764 this.buttons.add(this.addxtypeChild(children[i]));
23768 editor.on('editorevent', this.updateToolbar, this);
23770 onBtnClick : function(id)
23772 this.editorcore.relayCmd(id);
23773 this.editorcore.focus();
23777 * Protected method that will not generally be called directly. It triggers
23778 * a toolbar update by reading the markup state of the current selection in the editor.
23780 updateToolbar: function(){
23782 if(!this.editorcore.activated){
23783 this.editor.onFirstFocus(); // is this neeed?
23787 var btns = this.buttons;
23788 var doc = this.editorcore.doc;
23789 btns.get('bold').setActive(doc.queryCommandState('bold'));
23790 btns.get('italic').setActive(doc.queryCommandState('italic'));
23791 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23793 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23794 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23795 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23797 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23798 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23801 var ans = this.editorcore.getAllAncestors();
23802 if (this.formatCombo) {
23805 var store = this.formatCombo.store;
23806 this.formatCombo.setValue("");
23807 for (var i =0; i < ans.length;i++) {
23808 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23810 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23818 // hides menus... - so this cant be on a menu...
23819 Roo.bootstrap.MenuMgr.hideAll();
23821 Roo.bootstrap.MenuMgr.hideAll();
23822 //this.editorsyncValue();
23824 onFirstFocus: function() {
23825 this.buttons.each(function(item){
23829 toggleSourceEdit : function(sourceEditMode){
23832 if(sourceEditMode){
23833 Roo.log("disabling buttons");
23834 this.buttons.each( function(item){
23835 if(item.cmd != 'pencil'){
23841 Roo.log("enabling buttons");
23842 if(this.editorcore.initialized){
23843 this.buttons.each( function(item){
23849 Roo.log("calling toggole on editor");
23850 // tell the editor that it's been pressed..
23851 this.editor.toggleSourceEdit(sourceEditMode);
23861 * @class Roo.bootstrap.Table.AbstractSelectionModel
23862 * @extends Roo.util.Observable
23863 * Abstract base class for grid SelectionModels. It provides the interface that should be
23864 * implemented by descendant classes. This class should not be directly instantiated.
23867 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23868 this.locked = false;
23869 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23873 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23874 /** @ignore Called by the grid automatically. Do not call directly. */
23875 init : function(grid){
23881 * Locks the selections.
23884 this.locked = true;
23888 * Unlocks the selections.
23890 unlock : function(){
23891 this.locked = false;
23895 * Returns true if the selections are locked.
23896 * @return {Boolean}
23898 isLocked : function(){
23899 return this.locked;
23903 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23904 * @class Roo.bootstrap.Table.RowSelectionModel
23905 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23906 * It supports multiple selections and keyboard selection/navigation.
23908 * @param {Object} config
23911 Roo.bootstrap.Table.RowSelectionModel = function(config){
23912 Roo.apply(this, config);
23913 this.selections = new Roo.util.MixedCollection(false, function(o){
23918 this.lastActive = false;
23922 * @event selectionchange
23923 * Fires when the selection changes
23924 * @param {SelectionModel} this
23926 "selectionchange" : true,
23928 * @event afterselectionchange
23929 * Fires after the selection changes (eg. by key press or clicking)
23930 * @param {SelectionModel} this
23932 "afterselectionchange" : true,
23934 * @event beforerowselect
23935 * Fires when a row is selected being selected, return false to cancel.
23936 * @param {SelectionModel} this
23937 * @param {Number} rowIndex The selected index
23938 * @param {Boolean} keepExisting False if other selections will be cleared
23940 "beforerowselect" : true,
23943 * Fires when a row is selected.
23944 * @param {SelectionModel} this
23945 * @param {Number} rowIndex The selected index
23946 * @param {Roo.data.Record} r The record
23948 "rowselect" : true,
23950 * @event rowdeselect
23951 * Fires when a row is deselected.
23952 * @param {SelectionModel} this
23953 * @param {Number} rowIndex The selected index
23955 "rowdeselect" : true
23957 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23958 this.locked = false;
23961 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23963 * @cfg {Boolean} singleSelect
23964 * True to allow selection of only one row at a time (defaults to false)
23966 singleSelect : false,
23969 initEvents : function()
23972 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23973 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23974 //}else{ // allow click to work like normal
23975 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23977 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23978 this.grid.on("rowclick", this.handleMouseDown, this);
23980 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23981 "up" : function(e){
23983 this.selectPrevious(e.shiftKey);
23984 }else if(this.last !== false && this.lastActive !== false){
23985 var last = this.last;
23986 this.selectRange(this.last, this.lastActive-1);
23987 this.grid.getView().focusRow(this.lastActive);
23988 if(last !== false){
23992 this.selectFirstRow();
23994 this.fireEvent("afterselectionchange", this);
23996 "down" : function(e){
23998 this.selectNext(e.shiftKey);
23999 }else if(this.last !== false && this.lastActive !== false){
24000 var last = this.last;
24001 this.selectRange(this.last, this.lastActive+1);
24002 this.grid.getView().focusRow(this.lastActive);
24003 if(last !== false){
24007 this.selectFirstRow();
24009 this.fireEvent("afterselectionchange", this);
24013 this.grid.store.on('load', function(){
24014 this.selections.clear();
24017 var view = this.grid.view;
24018 view.on("refresh", this.onRefresh, this);
24019 view.on("rowupdated", this.onRowUpdated, this);
24020 view.on("rowremoved", this.onRemove, this);
24025 onRefresh : function()
24027 var ds = this.grid.store, i, v = this.grid.view;
24028 var s = this.selections;
24029 s.each(function(r){
24030 if((i = ds.indexOfId(r.id)) != -1){
24039 onRemove : function(v, index, r){
24040 this.selections.remove(r);
24044 onRowUpdated : function(v, index, r){
24045 if(this.isSelected(r)){
24046 v.onRowSelect(index);
24052 * @param {Array} records The records to select
24053 * @param {Boolean} keepExisting (optional) True to keep existing selections
24055 selectRecords : function(records, keepExisting)
24058 this.clearSelections();
24060 var ds = this.grid.store;
24061 for(var i = 0, len = records.length; i < len; i++){
24062 this.selectRow(ds.indexOf(records[i]), true);
24067 * Gets the number of selected rows.
24070 getCount : function(){
24071 return this.selections.length;
24075 * Selects the first row in the grid.
24077 selectFirstRow : function(){
24082 * Select the last row.
24083 * @param {Boolean} keepExisting (optional) True to keep existing selections
24085 selectLastRow : function(keepExisting){
24086 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24087 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24091 * Selects the row immediately following the last selected row.
24092 * @param {Boolean} keepExisting (optional) True to keep existing selections
24094 selectNext : function(keepExisting)
24096 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24097 this.selectRow(this.last+1, keepExisting);
24098 this.grid.getView().focusRow(this.last);
24103 * Selects the row that precedes the last selected row.
24104 * @param {Boolean} keepExisting (optional) True to keep existing selections
24106 selectPrevious : function(keepExisting){
24108 this.selectRow(this.last-1, keepExisting);
24109 this.grid.getView().focusRow(this.last);
24114 * Returns the selected records
24115 * @return {Array} Array of selected records
24117 getSelections : function(){
24118 return [].concat(this.selections.items);
24122 * Returns the first selected record.
24125 getSelected : function(){
24126 return this.selections.itemAt(0);
24131 * Clears all selections.
24133 clearSelections : function(fast)
24139 var ds = this.grid.store;
24140 var s = this.selections;
24141 s.each(function(r){
24142 this.deselectRow(ds.indexOfId(r.id));
24146 this.selections.clear();
24153 * Selects all rows.
24155 selectAll : function(){
24159 this.selections.clear();
24160 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24161 this.selectRow(i, true);
24166 * Returns True if there is a selection.
24167 * @return {Boolean}
24169 hasSelection : function(){
24170 return this.selections.length > 0;
24174 * Returns True if the specified row is selected.
24175 * @param {Number/Record} record The record or index of the record to check
24176 * @return {Boolean}
24178 isSelected : function(index){
24179 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24180 return (r && this.selections.key(r.id) ? true : false);
24184 * Returns True if the specified record id is selected.
24185 * @param {String} id The id of record to check
24186 * @return {Boolean}
24188 isIdSelected : function(id){
24189 return (this.selections.key(id) ? true : false);
24194 handleMouseDBClick : function(e, t){
24198 handleMouseDown : function(e, t)
24200 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24201 if(this.isLocked() || rowIndex < 0 ){
24204 if(e.shiftKey && this.last !== false){
24205 var last = this.last;
24206 this.selectRange(last, rowIndex, e.ctrlKey);
24207 this.last = last; // reset the last
24211 var isSelected = this.isSelected(rowIndex);
24212 //Roo.log("select row:" + rowIndex);
24214 this.deselectRow(rowIndex);
24216 this.selectRow(rowIndex, true);
24220 if(e.button !== 0 && isSelected){
24221 alert('rowIndex 2: ' + rowIndex);
24222 view.focusRow(rowIndex);
24223 }else if(e.ctrlKey && isSelected){
24224 this.deselectRow(rowIndex);
24225 }else if(!isSelected){
24226 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24227 view.focusRow(rowIndex);
24231 this.fireEvent("afterselectionchange", this);
24234 handleDragableRowClick : function(grid, rowIndex, e)
24236 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24237 this.selectRow(rowIndex, false);
24238 grid.view.focusRow(rowIndex);
24239 this.fireEvent("afterselectionchange", this);
24244 * Selects multiple rows.
24245 * @param {Array} rows Array of the indexes of the row to select
24246 * @param {Boolean} keepExisting (optional) True to keep existing selections
24248 selectRows : function(rows, keepExisting){
24250 this.clearSelections();
24252 for(var i = 0, len = rows.length; i < len; i++){
24253 this.selectRow(rows[i], true);
24258 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24259 * @param {Number} startRow The index of the first row in the range
24260 * @param {Number} endRow The index of the last row in the range
24261 * @param {Boolean} keepExisting (optional) True to retain existing selections
24263 selectRange : function(startRow, endRow, keepExisting){
24268 this.clearSelections();
24270 if(startRow <= endRow){
24271 for(var i = startRow; i <= endRow; i++){
24272 this.selectRow(i, true);
24275 for(var i = startRow; i >= endRow; i--){
24276 this.selectRow(i, true);
24282 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24283 * @param {Number} startRow The index of the first row in the range
24284 * @param {Number} endRow The index of the last row in the range
24286 deselectRange : function(startRow, endRow, preventViewNotify){
24290 for(var i = startRow; i <= endRow; i++){
24291 this.deselectRow(i, preventViewNotify);
24297 * @param {Number} row The index of the row to select
24298 * @param {Boolean} keepExisting (optional) True to keep existing selections
24300 selectRow : function(index, keepExisting, preventViewNotify)
24302 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24305 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24306 if(!keepExisting || this.singleSelect){
24307 this.clearSelections();
24310 var r = this.grid.store.getAt(index);
24311 //console.log('selectRow - record id :' + r.id);
24313 this.selections.add(r);
24314 this.last = this.lastActive = index;
24315 if(!preventViewNotify){
24316 var proxy = new Roo.Element(
24317 this.grid.getRowDom(index)
24319 proxy.addClass('bg-info info');
24321 this.fireEvent("rowselect", this, index, r);
24322 this.fireEvent("selectionchange", this);
24328 * @param {Number} row The index of the row to deselect
24330 deselectRow : function(index, preventViewNotify)
24335 if(this.last == index){
24338 if(this.lastActive == index){
24339 this.lastActive = false;
24342 var r = this.grid.store.getAt(index);
24347 this.selections.remove(r);
24348 //.console.log('deselectRow - record id :' + r.id);
24349 if(!preventViewNotify){
24351 var proxy = new Roo.Element(
24352 this.grid.getRowDom(index)
24354 proxy.removeClass('bg-info info');
24356 this.fireEvent("rowdeselect", this, index);
24357 this.fireEvent("selectionchange", this);
24361 restoreLast : function(){
24363 this.last = this._last;
24368 acceptsNav : function(row, col, cm){
24369 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24373 onEditorKey : function(field, e){
24374 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24379 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24381 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24383 }else if(k == e.ENTER && !e.ctrlKey){
24387 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24389 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24391 }else if(k == e.ESC){
24395 g.startEditing(newCell[0], newCell[1]);
24401 * Ext JS Library 1.1.1
24402 * Copyright(c) 2006-2007, Ext JS, LLC.
24404 * Originally Released Under LGPL - original licence link has changed is not relivant.
24407 * <script type="text/javascript">
24411 * @class Roo.bootstrap.PagingToolbar
24412 * @extends Roo.bootstrap.NavSimplebar
24413 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24415 * Create a new PagingToolbar
24416 * @param {Object} config The config object
24417 * @param {Roo.data.Store} store
24419 Roo.bootstrap.PagingToolbar = function(config)
24421 // old args format still supported... - xtype is prefered..
24422 // created from xtype...
24424 this.ds = config.dataSource;
24426 if (config.store && !this.ds) {
24427 this.store= Roo.factory(config.store, Roo.data);
24428 this.ds = this.store;
24429 this.ds.xmodule = this.xmodule || false;
24432 this.toolbarItems = [];
24433 if (config.items) {
24434 this.toolbarItems = config.items;
24437 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24442 this.bind(this.ds);
24445 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24449 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24451 * @cfg {Roo.data.Store} dataSource
24452 * The underlying data store providing the paged data
24455 * @cfg {String/HTMLElement/Element} container
24456 * container The id or element that will contain the toolbar
24459 * @cfg {Boolean} displayInfo
24460 * True to display the displayMsg (defaults to false)
24463 * @cfg {Number} pageSize
24464 * The number of records to display per page (defaults to 20)
24468 * @cfg {String} displayMsg
24469 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24471 displayMsg : 'Displaying {0} - {1} of {2}',
24473 * @cfg {String} emptyMsg
24474 * The message to display when no records are found (defaults to "No data to display")
24476 emptyMsg : 'No data to display',
24478 * Customizable piece of the default paging text (defaults to "Page")
24481 beforePageText : "Page",
24483 * Customizable piece of the default paging text (defaults to "of %0")
24486 afterPageText : "of {0}",
24488 * Customizable piece of the default paging text (defaults to "First Page")
24491 firstText : "First Page",
24493 * Customizable piece of the default paging text (defaults to "Previous Page")
24496 prevText : "Previous Page",
24498 * Customizable piece of the default paging text (defaults to "Next Page")
24501 nextText : "Next Page",
24503 * Customizable piece of the default paging text (defaults to "Last Page")
24506 lastText : "Last Page",
24508 * Customizable piece of the default paging text (defaults to "Refresh")
24511 refreshText : "Refresh",
24515 onRender : function(ct, position)
24517 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24518 this.navgroup.parentId = this.id;
24519 this.navgroup.onRender(this.el, null);
24520 // add the buttons to the navgroup
24522 if(this.displayInfo){
24523 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24524 this.displayEl = this.el.select('.x-paging-info', true).first();
24525 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24526 // this.displayEl = navel.el.select('span',true).first();
24532 Roo.each(_this.buttons, function(e){ // this might need to use render????
24533 Roo.factory(e).render(_this.el);
24537 Roo.each(_this.toolbarItems, function(e) {
24538 _this.navgroup.addItem(e);
24542 this.first = this.navgroup.addItem({
24543 tooltip: this.firstText,
24545 icon : 'fa fa-backward',
24547 preventDefault: true,
24548 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24551 this.prev = this.navgroup.addItem({
24552 tooltip: this.prevText,
24554 icon : 'fa fa-step-backward',
24556 preventDefault: true,
24557 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24559 //this.addSeparator();
24562 var field = this.navgroup.addItem( {
24564 cls : 'x-paging-position',
24566 html : this.beforePageText +
24567 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24568 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24571 this.field = field.el.select('input', true).first();
24572 this.field.on("keydown", this.onPagingKeydown, this);
24573 this.field.on("focus", function(){this.dom.select();});
24576 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24577 //this.field.setHeight(18);
24578 //this.addSeparator();
24579 this.next = this.navgroup.addItem({
24580 tooltip: this.nextText,
24582 html : ' <i class="fa fa-step-forward">',
24584 preventDefault: true,
24585 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24587 this.last = this.navgroup.addItem({
24588 tooltip: this.lastText,
24589 icon : 'fa fa-forward',
24592 preventDefault: true,
24593 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24595 //this.addSeparator();
24596 this.loading = this.navgroup.addItem({
24597 tooltip: this.refreshText,
24598 icon: 'fa fa-refresh',
24599 preventDefault: true,
24600 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24606 updateInfo : function(){
24607 if(this.displayEl){
24608 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24609 var msg = count == 0 ?
24613 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24615 this.displayEl.update(msg);
24620 onLoad : function(ds, r, o)
24622 this.cursor = o.params.start ? o.params.start : 0;
24624 var d = this.getPageData(),
24629 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24630 this.field.dom.value = ap;
24631 this.first.setDisabled(ap == 1);
24632 this.prev.setDisabled(ap == 1);
24633 this.next.setDisabled(ap == ps);
24634 this.last.setDisabled(ap == ps);
24635 this.loading.enable();
24640 getPageData : function(){
24641 var total = this.ds.getTotalCount();
24644 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24645 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24650 onLoadError : function(){
24651 this.loading.enable();
24655 onPagingKeydown : function(e){
24656 var k = e.getKey();
24657 var d = this.getPageData();
24659 var v = this.field.dom.value, pageNum;
24660 if(!v || isNaN(pageNum = parseInt(v, 10))){
24661 this.field.dom.value = d.activePage;
24664 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24665 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24668 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))
24670 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24671 this.field.dom.value = pageNum;
24672 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24675 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24677 var v = this.field.dom.value, pageNum;
24678 var increment = (e.shiftKey) ? 10 : 1;
24679 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24682 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24683 this.field.dom.value = d.activePage;
24686 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24688 this.field.dom.value = parseInt(v, 10) + increment;
24689 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24690 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24697 beforeLoad : function(){
24699 this.loading.disable();
24704 onClick : function(which){
24713 ds.load({params:{start: 0, limit: this.pageSize}});
24716 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24719 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24722 var total = ds.getTotalCount();
24723 var extra = total % this.pageSize;
24724 var lastStart = extra ? (total - extra) : total-this.pageSize;
24725 ds.load({params:{start: lastStart, limit: this.pageSize}});
24728 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24734 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24735 * @param {Roo.data.Store} store The data store to unbind
24737 unbind : function(ds){
24738 ds.un("beforeload", this.beforeLoad, this);
24739 ds.un("load", this.onLoad, this);
24740 ds.un("loadexception", this.onLoadError, this);
24741 ds.un("remove", this.updateInfo, this);
24742 ds.un("add", this.updateInfo, this);
24743 this.ds = undefined;
24747 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24748 * @param {Roo.data.Store} store The data store to bind
24750 bind : function(ds){
24751 ds.on("beforeload", this.beforeLoad, this);
24752 ds.on("load", this.onLoad, this);
24753 ds.on("loadexception", this.onLoadError, this);
24754 ds.on("remove", this.updateInfo, this);
24755 ds.on("add", this.updateInfo, this);
24766 * @class Roo.bootstrap.MessageBar
24767 * @extends Roo.bootstrap.Component
24768 * Bootstrap MessageBar class
24769 * @cfg {String} html contents of the MessageBar
24770 * @cfg {String} weight (info | success | warning | danger) default info
24771 * @cfg {String} beforeClass insert the bar before the given class
24772 * @cfg {Boolean} closable (true | false) default false
24773 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24776 * Create a new Element
24777 * @param {Object} config The config object
24780 Roo.bootstrap.MessageBar = function(config){
24781 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24784 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24790 beforeClass: 'bootstrap-sticky-wrap',
24792 getAutoCreate : function(){
24796 cls: 'alert alert-dismissable alert-' + this.weight,
24801 html: this.html || ''
24807 cfg.cls += ' alert-messages-fixed';
24821 onRender : function(ct, position)
24823 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24826 var cfg = Roo.apply({}, this.getAutoCreate());
24830 cfg.cls += ' ' + this.cls;
24833 cfg.style = this.style;
24835 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24837 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24840 this.el.select('>button.close').on('click', this.hide, this);
24846 if (!this.rendered) {
24852 this.fireEvent('show', this);
24858 if (!this.rendered) {
24864 this.fireEvent('hide', this);
24867 update : function()
24869 // var e = this.el.dom.firstChild;
24871 // if(this.closable){
24872 // e = e.nextSibling;
24875 // e.data = this.html || '';
24877 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24893 * @class Roo.bootstrap.Graph
24894 * @extends Roo.bootstrap.Component
24895 * Bootstrap Graph class
24899 @cfg {String} graphtype bar | vbar | pie
24900 @cfg {number} g_x coodinator | centre x (pie)
24901 @cfg {number} g_y coodinator | centre y (pie)
24902 @cfg {number} g_r radius (pie)
24903 @cfg {number} g_height height of the chart (respected by all elements in the set)
24904 @cfg {number} g_width width of the chart (respected by all elements in the set)
24905 @cfg {Object} title The title of the chart
24908 -opts (object) options for the chart
24910 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24911 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24913 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.
24914 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24916 o stretch (boolean)
24918 -opts (object) options for the pie
24921 o startAngle (number)
24922 o endAngle (number)
24926 * Create a new Input
24927 * @param {Object} config The config object
24930 Roo.bootstrap.Graph = function(config){
24931 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24937 * The img click event for the img.
24938 * @param {Roo.EventObject} e
24944 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24955 //g_colors: this.colors,
24962 getAutoCreate : function(){
24973 onRender : function(ct,position){
24976 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24978 if (typeof(Raphael) == 'undefined') {
24979 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24983 this.raphael = Raphael(this.el.dom);
24985 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24986 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24987 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24988 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24990 r.text(160, 10, "Single Series Chart").attr(txtattr);
24991 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24992 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24993 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24995 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24996 r.barchart(330, 10, 300, 220, data1);
24997 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24998 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25001 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25002 // r.barchart(30, 30, 560, 250, xdata, {
25003 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25004 // axis : "0 0 1 1",
25005 // axisxlabels : xdata
25006 // //yvalues : cols,
25009 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25011 // this.load(null,xdata,{
25012 // axis : "0 0 1 1",
25013 // axisxlabels : xdata
25018 load : function(graphtype,xdata,opts)
25020 this.raphael.clear();
25022 graphtype = this.graphtype;
25027 var r = this.raphael,
25028 fin = function () {
25029 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25031 fout = function () {
25032 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25034 pfin = function() {
25035 this.sector.stop();
25036 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25039 this.label[0].stop();
25040 this.label[0].attr({ r: 7.5 });
25041 this.label[1].attr({ "font-weight": 800 });
25044 pfout = function() {
25045 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25048 this.label[0].animate({ r: 5 }, 500, "bounce");
25049 this.label[1].attr({ "font-weight": 400 });
25055 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25058 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25061 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25062 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25064 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25071 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25076 setTitle: function(o)
25081 initEvents: function() {
25084 this.el.on('click', this.onClick, this);
25088 onClick : function(e)
25090 Roo.log('img onclick');
25091 this.fireEvent('click', this, e);
25103 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25106 * @class Roo.bootstrap.dash.NumberBox
25107 * @extends Roo.bootstrap.Component
25108 * Bootstrap NumberBox class
25109 * @cfg {String} headline Box headline
25110 * @cfg {String} content Box content
25111 * @cfg {String} icon Box icon
25112 * @cfg {String} footer Footer text
25113 * @cfg {String} fhref Footer href
25116 * Create a new NumberBox
25117 * @param {Object} config The config object
25121 Roo.bootstrap.dash.NumberBox = function(config){
25122 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25126 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25135 getAutoCreate : function(){
25139 cls : 'small-box ',
25147 cls : 'roo-headline',
25148 html : this.headline
25152 cls : 'roo-content',
25153 html : this.content
25167 cls : 'ion ' + this.icon
25176 cls : 'small-box-footer',
25177 href : this.fhref || '#',
25181 cfg.cn.push(footer);
25188 onRender : function(ct,position){
25189 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25196 setHeadline: function (value)
25198 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25201 setFooter: function (value, href)
25203 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25206 this.el.select('a.small-box-footer',true).first().attr('href', href);
25211 setContent: function (value)
25213 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25216 initEvents: function()
25230 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25233 * @class Roo.bootstrap.dash.TabBox
25234 * @extends Roo.bootstrap.Component
25235 * Bootstrap TabBox class
25236 * @cfg {String} title Title of the TabBox
25237 * @cfg {String} icon Icon of the TabBox
25238 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25239 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25242 * Create a new TabBox
25243 * @param {Object} config The config object
25247 Roo.bootstrap.dash.TabBox = function(config){
25248 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25253 * When a pane is added
25254 * @param {Roo.bootstrap.dash.TabPane} pane
25258 * @event activatepane
25259 * When a pane is activated
25260 * @param {Roo.bootstrap.dash.TabPane} pane
25262 "activatepane" : true
25270 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25275 tabScrollable : false,
25277 getChildContainer : function()
25279 return this.el.select('.tab-content', true).first();
25282 getAutoCreate : function(){
25286 cls: 'pull-left header',
25294 cls: 'fa ' + this.icon
25300 cls: 'nav nav-tabs pull-right',
25306 if(this.tabScrollable){
25313 cls: 'nav nav-tabs pull-right',
25324 cls: 'nav-tabs-custom',
25329 cls: 'tab-content no-padding',
25337 initEvents : function()
25339 //Roo.log('add add pane handler');
25340 this.on('addpane', this.onAddPane, this);
25343 * Updates the box title
25344 * @param {String} html to set the title to.
25346 setTitle : function(value)
25348 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25350 onAddPane : function(pane)
25352 this.panes.push(pane);
25353 //Roo.log('addpane');
25355 // tabs are rendere left to right..
25356 if(!this.showtabs){
25360 var ctr = this.el.select('.nav-tabs', true).first();
25363 var existing = ctr.select('.nav-tab',true);
25364 var qty = existing.getCount();;
25367 var tab = ctr.createChild({
25369 cls : 'nav-tab' + (qty ? '' : ' active'),
25377 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25380 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25382 pane.el.addClass('active');
25387 onTabClick : function(ev,un,ob,pane)
25389 //Roo.log('tab - prev default');
25390 ev.preventDefault();
25393 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25394 pane.tab.addClass('active');
25395 //Roo.log(pane.title);
25396 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25397 // technically we should have a deactivate event.. but maybe add later.
25398 // and it should not de-activate the selected tab...
25399 this.fireEvent('activatepane', pane);
25400 pane.el.addClass('active');
25401 pane.fireEvent('activate');
25406 getActivePane : function()
25409 Roo.each(this.panes, function(p) {
25410 if(p.el.hasClass('active')){
25431 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25433 * @class Roo.bootstrap.TabPane
25434 * @extends Roo.bootstrap.Component
25435 * Bootstrap TabPane class
25436 * @cfg {Boolean} active (false | true) Default false
25437 * @cfg {String} title title of panel
25441 * Create a new TabPane
25442 * @param {Object} config The config object
25445 Roo.bootstrap.dash.TabPane = function(config){
25446 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25452 * When a pane is activated
25453 * @param {Roo.bootstrap.dash.TabPane} pane
25460 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25465 // the tabBox that this is attached to.
25468 getAutoCreate : function()
25476 cfg.cls += ' active';
25481 initEvents : function()
25483 //Roo.log('trigger add pane handler');
25484 this.parent().fireEvent('addpane', this)
25488 * Updates the tab title
25489 * @param {String} html to set the title to.
25491 setTitle: function(str)
25497 this.tab.select('a', true).first().dom.innerHTML = str;
25514 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25517 * @class Roo.bootstrap.menu.Menu
25518 * @extends Roo.bootstrap.Component
25519 * Bootstrap Menu class - container for Menu
25520 * @cfg {String} html Text of the menu
25521 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25522 * @cfg {String} icon Font awesome icon
25523 * @cfg {String} pos Menu align to (top | bottom) default bottom
25527 * Create a new Menu
25528 * @param {Object} config The config object
25532 Roo.bootstrap.menu.Menu = function(config){
25533 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25537 * @event beforeshow
25538 * Fires before this menu is displayed
25539 * @param {Roo.bootstrap.menu.Menu} this
25543 * @event beforehide
25544 * Fires before this menu is hidden
25545 * @param {Roo.bootstrap.menu.Menu} this
25550 * Fires after this menu is displayed
25551 * @param {Roo.bootstrap.menu.Menu} this
25556 * Fires after this menu is hidden
25557 * @param {Roo.bootstrap.menu.Menu} this
25562 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25563 * @param {Roo.bootstrap.menu.Menu} this
25564 * @param {Roo.EventObject} e
25571 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25575 weight : 'default',
25580 getChildContainer : function() {
25581 if(this.isSubMenu){
25585 return this.el.select('ul.dropdown-menu', true).first();
25588 getAutoCreate : function()
25593 cls : 'roo-menu-text',
25601 cls : 'fa ' + this.icon
25612 cls : 'dropdown-button btn btn-' + this.weight,
25617 cls : 'dropdown-toggle btn btn-' + this.weight,
25627 cls : 'dropdown-menu'
25633 if(this.pos == 'top'){
25634 cfg.cls += ' dropup';
25637 if(this.isSubMenu){
25640 cls : 'dropdown-menu'
25647 onRender : function(ct, position)
25649 this.isSubMenu = ct.hasClass('dropdown-submenu');
25651 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25654 initEvents : function()
25656 if(this.isSubMenu){
25660 this.hidden = true;
25662 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25663 this.triggerEl.on('click', this.onTriggerPress, this);
25665 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25666 this.buttonEl.on('click', this.onClick, this);
25672 if(this.isSubMenu){
25676 return this.el.select('ul.dropdown-menu', true).first();
25679 onClick : function(e)
25681 this.fireEvent("click", this, e);
25684 onTriggerPress : function(e)
25686 if (this.isVisible()) {
25693 isVisible : function(){
25694 return !this.hidden;
25699 this.fireEvent("beforeshow", this);
25701 this.hidden = false;
25702 this.el.addClass('open');
25704 Roo.get(document).on("mouseup", this.onMouseUp, this);
25706 this.fireEvent("show", this);
25713 this.fireEvent("beforehide", this);
25715 this.hidden = true;
25716 this.el.removeClass('open');
25718 Roo.get(document).un("mouseup", this.onMouseUp);
25720 this.fireEvent("hide", this);
25723 onMouseUp : function()
25737 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25740 * @class Roo.bootstrap.menu.Item
25741 * @extends Roo.bootstrap.Component
25742 * Bootstrap MenuItem class
25743 * @cfg {Boolean} submenu (true | false) default false
25744 * @cfg {String} html text of the item
25745 * @cfg {String} href the link
25746 * @cfg {Boolean} disable (true | false) default false
25747 * @cfg {Boolean} preventDefault (true | false) default true
25748 * @cfg {String} icon Font awesome icon
25749 * @cfg {String} pos Submenu align to (left | right) default right
25753 * Create a new Item
25754 * @param {Object} config The config object
25758 Roo.bootstrap.menu.Item = function(config){
25759 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25763 * Fires when the mouse is hovering over this menu
25764 * @param {Roo.bootstrap.menu.Item} this
25765 * @param {Roo.EventObject} e
25770 * Fires when the mouse exits this menu
25771 * @param {Roo.bootstrap.menu.Item} this
25772 * @param {Roo.EventObject} e
25778 * The raw click event for the entire grid.
25779 * @param {Roo.EventObject} e
25785 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25790 preventDefault: true,
25795 getAutoCreate : function()
25800 cls : 'roo-menu-item-text',
25808 cls : 'fa ' + this.icon
25817 href : this.href || '#',
25824 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25828 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25830 if(this.pos == 'left'){
25831 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25838 initEvents : function()
25840 this.el.on('mouseover', this.onMouseOver, this);
25841 this.el.on('mouseout', this.onMouseOut, this);
25843 this.el.select('a', true).first().on('click', this.onClick, this);
25847 onClick : function(e)
25849 if(this.preventDefault){
25850 e.preventDefault();
25853 this.fireEvent("click", this, e);
25856 onMouseOver : function(e)
25858 if(this.submenu && this.pos == 'left'){
25859 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25862 this.fireEvent("mouseover", this, e);
25865 onMouseOut : function(e)
25867 this.fireEvent("mouseout", this, e);
25879 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25882 * @class Roo.bootstrap.menu.Separator
25883 * @extends Roo.bootstrap.Component
25884 * Bootstrap Separator class
25887 * Create a new Separator
25888 * @param {Object} config The config object
25892 Roo.bootstrap.menu.Separator = function(config){
25893 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25896 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25898 getAutoCreate : function(){
25919 * @class Roo.bootstrap.Tooltip
25920 * Bootstrap Tooltip class
25921 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25922 * to determine which dom element triggers the tooltip.
25924 * It needs to add support for additional attributes like tooltip-position
25927 * Create a new Toolti
25928 * @param {Object} config The config object
25931 Roo.bootstrap.Tooltip = function(config){
25932 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25934 this.alignment = Roo.bootstrap.Tooltip.alignment;
25936 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25937 this.alignment = config.alignment;
25942 Roo.apply(Roo.bootstrap.Tooltip, {
25944 * @function init initialize tooltip monitoring.
25948 currentTip : false,
25949 currentRegion : false,
25955 Roo.get(document).on('mouseover', this.enter ,this);
25956 Roo.get(document).on('mouseout', this.leave, this);
25959 this.currentTip = new Roo.bootstrap.Tooltip();
25962 enter : function(ev)
25964 var dom = ev.getTarget();
25966 //Roo.log(['enter',dom]);
25967 var el = Roo.fly(dom);
25968 if (this.currentEl) {
25970 //Roo.log(this.currentEl);
25971 //Roo.log(this.currentEl.contains(dom));
25972 if (this.currentEl == el) {
25975 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25981 if (this.currentTip.el) {
25982 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25986 if(!el || el.dom == document){
25992 // you can not look for children, as if el is the body.. then everythign is the child..
25993 if (!el.attr('tooltip')) { //
25994 if (!el.select("[tooltip]").elements.length) {
25997 // is the mouse over this child...?
25998 bindEl = el.select("[tooltip]").first();
25999 var xy = ev.getXY();
26000 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26001 //Roo.log("not in region.");
26004 //Roo.log("child element over..");
26007 this.currentEl = bindEl;
26008 this.currentTip.bind(bindEl);
26009 this.currentRegion = Roo.lib.Region.getRegion(dom);
26010 this.currentTip.enter();
26013 leave : function(ev)
26015 var dom = ev.getTarget();
26016 //Roo.log(['leave',dom]);
26017 if (!this.currentEl) {
26022 if (dom != this.currentEl.dom) {
26025 var xy = ev.getXY();
26026 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26029 // only activate leave if mouse cursor is outside... bounding box..
26034 if (this.currentTip) {
26035 this.currentTip.leave();
26037 //Roo.log('clear currentEl');
26038 this.currentEl = false;
26043 'left' : ['r-l', [-2,0], 'right'],
26044 'right' : ['l-r', [2,0], 'left'],
26045 'bottom' : ['t-b', [0,2], 'top'],
26046 'top' : [ 'b-t', [0,-2], 'bottom']
26052 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26057 delay : null, // can be { show : 300 , hide: 500}
26061 hoverState : null, //???
26063 placement : 'bottom',
26067 getAutoCreate : function(){
26074 cls : 'tooltip-arrow'
26077 cls : 'tooltip-inner'
26084 bind : function(el)
26090 enter : function () {
26092 if (this.timeout != null) {
26093 clearTimeout(this.timeout);
26096 this.hoverState = 'in';
26097 //Roo.log("enter - show");
26098 if (!this.delay || !this.delay.show) {
26103 this.timeout = setTimeout(function () {
26104 if (_t.hoverState == 'in') {
26107 }, this.delay.show);
26111 clearTimeout(this.timeout);
26113 this.hoverState = 'out';
26114 if (!this.delay || !this.delay.hide) {
26120 this.timeout = setTimeout(function () {
26121 //Roo.log("leave - timeout");
26123 if (_t.hoverState == 'out') {
26125 Roo.bootstrap.Tooltip.currentEl = false;
26130 show : function (msg)
26133 this.render(document.body);
26136 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26138 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26140 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26142 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26144 var placement = typeof this.placement == 'function' ?
26145 this.placement.call(this, this.el, on_el) :
26148 var autoToken = /\s?auto?\s?/i;
26149 var autoPlace = autoToken.test(placement);
26151 placement = placement.replace(autoToken, '') || 'top';
26155 //this.el.setXY([0,0]);
26157 //this.el.dom.style.display='block';
26159 //this.el.appendTo(on_el);
26161 var p = this.getPosition();
26162 var box = this.el.getBox();
26168 var align = this.alignment[placement];
26170 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26172 if(placement == 'top' || placement == 'bottom'){
26174 placement = 'right';
26177 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26178 placement = 'left';
26181 var scroll = Roo.select('body', true).first().getScroll();
26183 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26189 this.el.alignTo(this.bindEl, align[0],align[1]);
26190 //var arrow = this.el.select('.arrow',true).first();
26191 //arrow.set(align[2],
26193 this.el.addClass(placement);
26195 this.el.addClass('in fade');
26197 this.hoverState = null;
26199 if (this.el.hasClass('fade')) {
26210 //this.el.setXY([0,0]);
26211 this.el.removeClass('in');
26227 * @class Roo.bootstrap.LocationPicker
26228 * @extends Roo.bootstrap.Component
26229 * Bootstrap LocationPicker class
26230 * @cfg {Number} latitude Position when init default 0
26231 * @cfg {Number} longitude Position when init default 0
26232 * @cfg {Number} zoom default 15
26233 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26234 * @cfg {Boolean} mapTypeControl default false
26235 * @cfg {Boolean} disableDoubleClickZoom default false
26236 * @cfg {Boolean} scrollwheel default true
26237 * @cfg {Boolean} streetViewControl default false
26238 * @cfg {Number} radius default 0
26239 * @cfg {String} locationName
26240 * @cfg {Boolean} draggable default true
26241 * @cfg {Boolean} enableAutocomplete default false
26242 * @cfg {Boolean} enableReverseGeocode default true
26243 * @cfg {String} markerTitle
26246 * Create a new LocationPicker
26247 * @param {Object} config The config object
26251 Roo.bootstrap.LocationPicker = function(config){
26253 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26258 * Fires when the picker initialized.
26259 * @param {Roo.bootstrap.LocationPicker} this
26260 * @param {Google Location} location
26264 * @event positionchanged
26265 * Fires when the picker position changed.
26266 * @param {Roo.bootstrap.LocationPicker} this
26267 * @param {Google Location} location
26269 positionchanged : true,
26272 * Fires when the map resize.
26273 * @param {Roo.bootstrap.LocationPicker} this
26278 * Fires when the map show.
26279 * @param {Roo.bootstrap.LocationPicker} this
26284 * Fires when the map hide.
26285 * @param {Roo.bootstrap.LocationPicker} this
26290 * Fires when click the map.
26291 * @param {Roo.bootstrap.LocationPicker} this
26292 * @param {Map event} e
26296 * @event mapRightClick
26297 * Fires when right click the map.
26298 * @param {Roo.bootstrap.LocationPicker} this
26299 * @param {Map event} e
26301 mapRightClick : true,
26303 * @event markerClick
26304 * Fires when click the marker.
26305 * @param {Roo.bootstrap.LocationPicker} this
26306 * @param {Map event} e
26308 markerClick : true,
26310 * @event markerRightClick
26311 * Fires when right click the marker.
26312 * @param {Roo.bootstrap.LocationPicker} this
26313 * @param {Map event} e
26315 markerRightClick : true,
26317 * @event OverlayViewDraw
26318 * Fires when OverlayView Draw
26319 * @param {Roo.bootstrap.LocationPicker} this
26321 OverlayViewDraw : true,
26323 * @event OverlayViewOnAdd
26324 * Fires when OverlayView Draw
26325 * @param {Roo.bootstrap.LocationPicker} this
26327 OverlayViewOnAdd : true,
26329 * @event OverlayViewOnRemove
26330 * Fires when OverlayView Draw
26331 * @param {Roo.bootstrap.LocationPicker} this
26333 OverlayViewOnRemove : true,
26335 * @event OverlayViewShow
26336 * Fires when OverlayView Draw
26337 * @param {Roo.bootstrap.LocationPicker} this
26338 * @param {Pixel} cpx
26340 OverlayViewShow : true,
26342 * @event OverlayViewHide
26343 * Fires when OverlayView Draw
26344 * @param {Roo.bootstrap.LocationPicker} this
26346 OverlayViewHide : true,
26348 * @event loadexception
26349 * Fires when load google lib failed.
26350 * @param {Roo.bootstrap.LocationPicker} this
26352 loadexception : true
26357 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26359 gMapContext: false,
26365 mapTypeControl: false,
26366 disableDoubleClickZoom: false,
26368 streetViewControl: false,
26372 enableAutocomplete: false,
26373 enableReverseGeocode: true,
26376 getAutoCreate: function()
26381 cls: 'roo-location-picker'
26387 initEvents: function(ct, position)
26389 if(!this.el.getWidth() || this.isApplied()){
26393 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26398 initial: function()
26400 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26401 this.fireEvent('loadexception', this);
26405 if(!this.mapTypeId){
26406 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26409 this.gMapContext = this.GMapContext();
26411 this.initOverlayView();
26413 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26417 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26418 _this.setPosition(_this.gMapContext.marker.position);
26421 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26422 _this.fireEvent('mapClick', this, event);
26426 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26427 _this.fireEvent('mapRightClick', this, event);
26431 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26432 _this.fireEvent('markerClick', this, event);
26436 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26437 _this.fireEvent('markerRightClick', this, event);
26441 this.setPosition(this.gMapContext.location);
26443 this.fireEvent('initial', this, this.gMapContext.location);
26446 initOverlayView: function()
26450 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26454 _this.fireEvent('OverlayViewDraw', _this);
26459 _this.fireEvent('OverlayViewOnAdd', _this);
26462 onRemove: function()
26464 _this.fireEvent('OverlayViewOnRemove', _this);
26467 show: function(cpx)
26469 _this.fireEvent('OverlayViewShow', _this, cpx);
26474 _this.fireEvent('OverlayViewHide', _this);
26480 fromLatLngToContainerPixel: function(event)
26482 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26485 isApplied: function()
26487 return this.getGmapContext() == false ? false : true;
26490 getGmapContext: function()
26492 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26495 GMapContext: function()
26497 var position = new google.maps.LatLng(this.latitude, this.longitude);
26499 var _map = new google.maps.Map(this.el.dom, {
26502 mapTypeId: this.mapTypeId,
26503 mapTypeControl: this.mapTypeControl,
26504 disableDoubleClickZoom: this.disableDoubleClickZoom,
26505 scrollwheel: this.scrollwheel,
26506 streetViewControl: this.streetViewControl,
26507 locationName: this.locationName,
26508 draggable: this.draggable,
26509 enableAutocomplete: this.enableAutocomplete,
26510 enableReverseGeocode: this.enableReverseGeocode
26513 var _marker = new google.maps.Marker({
26514 position: position,
26516 title: this.markerTitle,
26517 draggable: this.draggable
26524 location: position,
26525 radius: this.radius,
26526 locationName: this.locationName,
26527 addressComponents: {
26528 formatted_address: null,
26529 addressLine1: null,
26530 addressLine2: null,
26532 streetNumber: null,
26536 stateOrProvince: null
26539 domContainer: this.el.dom,
26540 geodecoder: new google.maps.Geocoder()
26544 drawCircle: function(center, radius, options)
26546 if (this.gMapContext.circle != null) {
26547 this.gMapContext.circle.setMap(null);
26551 options = Roo.apply({}, options, {
26552 strokeColor: "#0000FF",
26553 strokeOpacity: .35,
26555 fillColor: "#0000FF",
26559 options.map = this.gMapContext.map;
26560 options.radius = radius;
26561 options.center = center;
26562 this.gMapContext.circle = new google.maps.Circle(options);
26563 return this.gMapContext.circle;
26569 setPosition: function(location)
26571 this.gMapContext.location = location;
26572 this.gMapContext.marker.setPosition(location);
26573 this.gMapContext.map.panTo(location);
26574 this.drawCircle(location, this.gMapContext.radius, {});
26578 if (this.gMapContext.settings.enableReverseGeocode) {
26579 this.gMapContext.geodecoder.geocode({
26580 latLng: this.gMapContext.location
26581 }, function(results, status) {
26583 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26584 _this.gMapContext.locationName = results[0].formatted_address;
26585 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26587 _this.fireEvent('positionchanged', this, location);
26594 this.fireEvent('positionchanged', this, location);
26599 google.maps.event.trigger(this.gMapContext.map, "resize");
26601 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26603 this.fireEvent('resize', this);
26606 setPositionByLatLng: function(latitude, longitude)
26608 this.setPosition(new google.maps.LatLng(latitude, longitude));
26611 getCurrentPosition: function()
26614 latitude: this.gMapContext.location.lat(),
26615 longitude: this.gMapContext.location.lng()
26619 getAddressName: function()
26621 return this.gMapContext.locationName;
26624 getAddressComponents: function()
26626 return this.gMapContext.addressComponents;
26629 address_component_from_google_geocode: function(address_components)
26633 for (var i = 0; i < address_components.length; i++) {
26634 var component = address_components[i];
26635 if (component.types.indexOf("postal_code") >= 0) {
26636 result.postalCode = component.short_name;
26637 } else if (component.types.indexOf("street_number") >= 0) {
26638 result.streetNumber = component.short_name;
26639 } else if (component.types.indexOf("route") >= 0) {
26640 result.streetName = component.short_name;
26641 } else if (component.types.indexOf("neighborhood") >= 0) {
26642 result.city = component.short_name;
26643 } else if (component.types.indexOf("locality") >= 0) {
26644 result.city = component.short_name;
26645 } else if (component.types.indexOf("sublocality") >= 0) {
26646 result.district = component.short_name;
26647 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26648 result.stateOrProvince = component.short_name;
26649 } else if (component.types.indexOf("country") >= 0) {
26650 result.country = component.short_name;
26654 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26655 result.addressLine2 = "";
26659 setZoomLevel: function(zoom)
26661 this.gMapContext.map.setZoom(zoom);
26674 this.fireEvent('show', this);
26685 this.fireEvent('hide', this);
26690 Roo.apply(Roo.bootstrap.LocationPicker, {
26692 OverlayView : function(map, options)
26694 options = options || {};
26708 * @class Roo.bootstrap.Alert
26709 * @extends Roo.bootstrap.Component
26710 * Bootstrap Alert class
26711 * @cfg {String} title The title of alert
26712 * @cfg {String} html The content of alert
26713 * @cfg {String} weight ( success | info | warning | danger )
26714 * @cfg {String} faicon font-awesomeicon
26717 * Create a new alert
26718 * @param {Object} config The config object
26722 Roo.bootstrap.Alert = function(config){
26723 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26727 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26734 getAutoCreate : function()
26743 cls : 'roo-alert-icon'
26748 cls : 'roo-alert-title',
26753 cls : 'roo-alert-text',
26760 cfg.cn[0].cls += ' fa ' + this.faicon;
26764 cfg.cls += ' alert-' + this.weight;
26770 initEvents: function()
26772 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26775 setTitle : function(str)
26777 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26780 setText : function(str)
26782 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26785 setWeight : function(weight)
26788 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26791 this.weight = weight;
26793 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26796 setIcon : function(icon)
26799 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26802 this.faicon = icon;
26804 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26825 * @class Roo.bootstrap.UploadCropbox
26826 * @extends Roo.bootstrap.Component
26827 * Bootstrap UploadCropbox class
26828 * @cfg {String} emptyText show when image has been loaded
26829 * @cfg {String} rotateNotify show when image too small to rotate
26830 * @cfg {Number} errorTimeout default 3000
26831 * @cfg {Number} minWidth default 300
26832 * @cfg {Number} minHeight default 300
26833 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26834 * @cfg {Boolean} isDocument (true|false) default false
26835 * @cfg {String} url action url
26836 * @cfg {String} paramName default 'imageUpload'
26837 * @cfg {String} method default POST
26838 * @cfg {Boolean} loadMask (true|false) default true
26839 * @cfg {Boolean} loadingText default 'Loading...'
26842 * Create a new UploadCropbox
26843 * @param {Object} config The config object
26846 Roo.bootstrap.UploadCropbox = function(config){
26847 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26851 * @event beforeselectfile
26852 * Fire before select file
26853 * @param {Roo.bootstrap.UploadCropbox} this
26855 "beforeselectfile" : true,
26858 * Fire after initEvent
26859 * @param {Roo.bootstrap.UploadCropbox} this
26864 * Fire after initEvent
26865 * @param {Roo.bootstrap.UploadCropbox} this
26866 * @param {String} data
26871 * Fire when preparing the file data
26872 * @param {Roo.bootstrap.UploadCropbox} this
26873 * @param {Object} file
26878 * Fire when get exception
26879 * @param {Roo.bootstrap.UploadCropbox} this
26880 * @param {XMLHttpRequest} xhr
26882 "exception" : true,
26884 * @event beforeloadcanvas
26885 * Fire before load the canvas
26886 * @param {Roo.bootstrap.UploadCropbox} this
26887 * @param {String} src
26889 "beforeloadcanvas" : true,
26892 * Fire when trash image
26893 * @param {Roo.bootstrap.UploadCropbox} this
26898 * Fire when download the image
26899 * @param {Roo.bootstrap.UploadCropbox} this
26903 * @event footerbuttonclick
26904 * Fire when footerbuttonclick
26905 * @param {Roo.bootstrap.UploadCropbox} this
26906 * @param {String} type
26908 "footerbuttonclick" : true,
26912 * @param {Roo.bootstrap.UploadCropbox} this
26917 * Fire when rotate the image
26918 * @param {Roo.bootstrap.UploadCropbox} this
26919 * @param {String} pos
26924 * Fire when inspect the file
26925 * @param {Roo.bootstrap.UploadCropbox} this
26926 * @param {Object} file
26931 * Fire when xhr upload the file
26932 * @param {Roo.bootstrap.UploadCropbox} this
26933 * @param {Object} data
26938 * Fire when arrange the file data
26939 * @param {Roo.bootstrap.UploadCropbox} this
26940 * @param {Object} formData
26945 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26948 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26950 emptyText : 'Click to upload image',
26951 rotateNotify : 'Image is too small to rotate',
26952 errorTimeout : 3000,
26966 cropType : 'image/jpeg',
26968 canvasLoaded : false,
26969 isDocument : false,
26971 paramName : 'imageUpload',
26973 loadingText : 'Loading...',
26976 getAutoCreate : function()
26980 cls : 'roo-upload-cropbox',
26984 cls : 'roo-upload-cropbox-selector',
26989 cls : 'roo-upload-cropbox-body',
26990 style : 'cursor:pointer',
26994 cls : 'roo-upload-cropbox-preview'
26998 cls : 'roo-upload-cropbox-thumb'
27002 cls : 'roo-upload-cropbox-empty-notify',
27003 html : this.emptyText
27007 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27008 html : this.rotateNotify
27014 cls : 'roo-upload-cropbox-footer',
27017 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27027 onRender : function(ct, position)
27029 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27031 if (this.buttons.length) {
27033 Roo.each(this.buttons, function(bb) {
27035 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27037 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27043 this.maskEl = this.el;
27047 initEvents : function()
27049 this.urlAPI = (window.createObjectURL && window) ||
27050 (window.URL && URL.revokeObjectURL && URL) ||
27051 (window.webkitURL && webkitURL);
27053 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27054 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27056 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27057 this.selectorEl.hide();
27059 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27060 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27062 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27063 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27064 this.thumbEl.hide();
27066 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27067 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27069 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27070 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27071 this.errorEl.hide();
27073 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27074 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27075 this.footerEl.hide();
27077 this.setThumbBoxSize();
27083 this.fireEvent('initial', this);
27090 window.addEventListener("resize", function() { _this.resize(); } );
27092 this.bodyEl.on('click', this.beforeSelectFile, this);
27095 this.bodyEl.on('touchstart', this.onTouchStart, this);
27096 this.bodyEl.on('touchmove', this.onTouchMove, this);
27097 this.bodyEl.on('touchend', this.onTouchEnd, this);
27101 this.bodyEl.on('mousedown', this.onMouseDown, this);
27102 this.bodyEl.on('mousemove', this.onMouseMove, this);
27103 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27104 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27105 Roo.get(document).on('mouseup', this.onMouseUp, this);
27108 this.selectorEl.on('change', this.onFileSelected, this);
27114 this.baseScale = 1;
27116 this.baseRotate = 1;
27117 this.dragable = false;
27118 this.pinching = false;
27121 this.cropData = false;
27122 this.notifyEl.dom.innerHTML = this.emptyText;
27124 this.selectorEl.dom.value = '';
27128 resize : function()
27130 if(this.fireEvent('resize', this) != false){
27131 this.setThumbBoxPosition();
27132 this.setCanvasPosition();
27136 onFooterButtonClick : function(e, el, o, type)
27139 case 'rotate-left' :
27140 this.onRotateLeft(e);
27142 case 'rotate-right' :
27143 this.onRotateRight(e);
27146 this.beforeSelectFile(e);
27161 this.fireEvent('footerbuttonclick', this, type);
27164 beforeSelectFile : function(e)
27166 e.preventDefault();
27168 if(this.fireEvent('beforeselectfile', this) != false){
27169 this.selectorEl.dom.click();
27173 onFileSelected : function(e)
27175 e.preventDefault();
27177 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27181 var file = this.selectorEl.dom.files[0];
27183 if(this.fireEvent('inspect', this, file) != false){
27184 this.prepare(file);
27189 trash : function(e)
27191 this.fireEvent('trash', this);
27194 download : function(e)
27196 this.fireEvent('download', this);
27199 loadCanvas : function(src)
27201 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27205 this.imageEl = document.createElement('img');
27209 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27211 this.imageEl.src = src;
27215 onLoadCanvas : function()
27217 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27218 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27220 this.bodyEl.un('click', this.beforeSelectFile, this);
27222 this.notifyEl.hide();
27223 this.thumbEl.show();
27224 this.footerEl.show();
27226 this.baseRotateLevel();
27228 if(this.isDocument){
27229 this.setThumbBoxSize();
27232 this.setThumbBoxPosition();
27234 this.baseScaleLevel();
27240 this.canvasLoaded = true;
27243 this.maskEl.unmask();
27248 setCanvasPosition : function()
27250 if(!this.canvasEl){
27254 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27255 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27257 this.previewEl.setLeft(pw);
27258 this.previewEl.setTop(ph);
27262 onMouseDown : function(e)
27266 this.dragable = true;
27267 this.pinching = false;
27269 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27270 this.dragable = false;
27274 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27275 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27279 onMouseMove : function(e)
27283 if(!this.canvasLoaded){
27287 if (!this.dragable){
27291 var minX = Math.ceil(this.thumbEl.getLeft(true));
27292 var minY = Math.ceil(this.thumbEl.getTop(true));
27294 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27295 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27297 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27298 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27300 x = x - this.mouseX;
27301 y = y - this.mouseY;
27303 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27304 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27306 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27307 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27309 this.previewEl.setLeft(bgX);
27310 this.previewEl.setTop(bgY);
27312 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27313 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27316 onMouseUp : function(e)
27320 this.dragable = false;
27323 onMouseWheel : function(e)
27327 this.startScale = this.scale;
27329 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27331 if(!this.zoomable()){
27332 this.scale = this.startScale;
27341 zoomable : function()
27343 var minScale = this.thumbEl.getWidth() / this.minWidth;
27345 if(this.minWidth < this.minHeight){
27346 minScale = this.thumbEl.getHeight() / this.minHeight;
27349 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27350 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27354 (this.rotate == 0 || this.rotate == 180) &&
27356 width > this.imageEl.OriginWidth ||
27357 height > this.imageEl.OriginHeight ||
27358 (width < this.minWidth && height < this.minHeight)
27366 (this.rotate == 90 || this.rotate == 270) &&
27368 width > this.imageEl.OriginWidth ||
27369 height > this.imageEl.OriginHeight ||
27370 (width < this.minHeight && height < this.minWidth)
27377 !this.isDocument &&
27378 (this.rotate == 0 || this.rotate == 180) &&
27380 width < this.minWidth ||
27381 width > this.imageEl.OriginWidth ||
27382 height < this.minHeight ||
27383 height > this.imageEl.OriginHeight
27390 !this.isDocument &&
27391 (this.rotate == 90 || this.rotate == 270) &&
27393 width < this.minHeight ||
27394 width > this.imageEl.OriginWidth ||
27395 height < this.minWidth ||
27396 height > this.imageEl.OriginHeight
27406 onRotateLeft : function(e)
27408 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27410 var minScale = this.thumbEl.getWidth() / this.minWidth;
27412 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27413 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27415 this.startScale = this.scale;
27417 while (this.getScaleLevel() < minScale){
27419 this.scale = this.scale + 1;
27421 if(!this.zoomable()){
27426 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27427 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27432 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27439 this.scale = this.startScale;
27441 this.onRotateFail();
27446 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27448 if(this.isDocument){
27449 this.setThumbBoxSize();
27450 this.setThumbBoxPosition();
27451 this.setCanvasPosition();
27456 this.fireEvent('rotate', this, 'left');
27460 onRotateRight : function(e)
27462 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27464 var minScale = this.thumbEl.getWidth() / this.minWidth;
27466 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27467 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27469 this.startScale = this.scale;
27471 while (this.getScaleLevel() < minScale){
27473 this.scale = this.scale + 1;
27475 if(!this.zoomable()){
27480 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27481 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27486 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27493 this.scale = this.startScale;
27495 this.onRotateFail();
27500 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27502 if(this.isDocument){
27503 this.setThumbBoxSize();
27504 this.setThumbBoxPosition();
27505 this.setCanvasPosition();
27510 this.fireEvent('rotate', this, 'right');
27513 onRotateFail : function()
27515 this.errorEl.show(true);
27519 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27524 this.previewEl.dom.innerHTML = '';
27526 var canvasEl = document.createElement("canvas");
27528 var contextEl = canvasEl.getContext("2d");
27530 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27531 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27532 var center = this.imageEl.OriginWidth / 2;
27534 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27535 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27536 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27537 center = this.imageEl.OriginHeight / 2;
27540 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27542 contextEl.translate(center, center);
27543 contextEl.rotate(this.rotate * Math.PI / 180);
27545 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27547 this.canvasEl = document.createElement("canvas");
27549 this.contextEl = this.canvasEl.getContext("2d");
27551 switch (this.rotate) {
27554 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27555 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27557 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27562 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27563 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27565 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27566 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);
27570 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27575 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27576 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27578 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27579 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);
27583 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);
27588 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27589 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27591 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27592 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27596 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);
27603 this.previewEl.appendChild(this.canvasEl);
27605 this.setCanvasPosition();
27610 if(!this.canvasLoaded){
27614 var imageCanvas = document.createElement("canvas");
27616 var imageContext = imageCanvas.getContext("2d");
27618 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27619 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27621 var center = imageCanvas.width / 2;
27623 imageContext.translate(center, center);
27625 imageContext.rotate(this.rotate * Math.PI / 180);
27627 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27629 var canvas = document.createElement("canvas");
27631 var context = canvas.getContext("2d");
27633 canvas.width = this.minWidth;
27634 canvas.height = this.minHeight;
27636 switch (this.rotate) {
27639 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27640 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27642 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27643 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27645 var targetWidth = this.minWidth - 2 * x;
27646 var targetHeight = this.minHeight - 2 * y;
27650 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27651 scale = targetWidth / width;
27654 if(x > 0 && y == 0){
27655 scale = targetHeight / height;
27658 if(x > 0 && y > 0){
27659 scale = targetWidth / width;
27661 if(width < height){
27662 scale = targetHeight / height;
27666 context.scale(scale, scale);
27668 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27669 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27671 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27672 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27674 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27679 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27680 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27682 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27683 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27685 var targetWidth = this.minWidth - 2 * x;
27686 var targetHeight = this.minHeight - 2 * y;
27690 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27691 scale = targetWidth / width;
27694 if(x > 0 && y == 0){
27695 scale = targetHeight / height;
27698 if(x > 0 && y > 0){
27699 scale = targetWidth / width;
27701 if(width < height){
27702 scale = targetHeight / height;
27706 context.scale(scale, scale);
27708 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27709 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27711 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27712 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27714 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27716 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27721 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27722 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27724 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27725 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27727 var targetWidth = this.minWidth - 2 * x;
27728 var targetHeight = this.minHeight - 2 * y;
27732 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27733 scale = targetWidth / width;
27736 if(x > 0 && y == 0){
27737 scale = targetHeight / height;
27740 if(x > 0 && y > 0){
27741 scale = targetWidth / width;
27743 if(width < height){
27744 scale = targetHeight / height;
27748 context.scale(scale, scale);
27750 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27751 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27753 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27754 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27756 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27757 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27759 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27764 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27765 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27767 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27768 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27770 var targetWidth = this.minWidth - 2 * x;
27771 var targetHeight = this.minHeight - 2 * y;
27775 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27776 scale = targetWidth / width;
27779 if(x > 0 && y == 0){
27780 scale = targetHeight / height;
27783 if(x > 0 && y > 0){
27784 scale = targetWidth / width;
27786 if(width < height){
27787 scale = targetHeight / height;
27791 context.scale(scale, scale);
27793 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27794 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27796 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27797 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27799 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27801 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27808 this.cropData = canvas.toDataURL(this.cropType);
27810 if(this.fireEvent('crop', this, this.cropData) !== false){
27811 this.process(this.file, this.cropData);
27818 setThumbBoxSize : function()
27822 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27823 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27824 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27826 this.minWidth = width;
27827 this.minHeight = height;
27829 if(this.rotate == 90 || this.rotate == 270){
27830 this.minWidth = height;
27831 this.minHeight = width;
27836 width = Math.ceil(this.minWidth * height / this.minHeight);
27838 if(this.minWidth > this.minHeight){
27840 height = Math.ceil(this.minHeight * width / this.minWidth);
27843 this.thumbEl.setStyle({
27844 width : width + 'px',
27845 height : height + 'px'
27852 setThumbBoxPosition : function()
27854 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27855 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27857 this.thumbEl.setLeft(x);
27858 this.thumbEl.setTop(y);
27862 baseRotateLevel : function()
27864 this.baseRotate = 1;
27867 typeof(this.exif) != 'undefined' &&
27868 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27869 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27871 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27874 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27878 baseScaleLevel : function()
27882 if(this.isDocument){
27884 if(this.baseRotate == 6 || this.baseRotate == 8){
27886 height = this.thumbEl.getHeight();
27887 this.baseScale = height / this.imageEl.OriginWidth;
27889 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27890 width = this.thumbEl.getWidth();
27891 this.baseScale = width / this.imageEl.OriginHeight;
27897 height = this.thumbEl.getHeight();
27898 this.baseScale = height / this.imageEl.OriginHeight;
27900 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27901 width = this.thumbEl.getWidth();
27902 this.baseScale = width / this.imageEl.OriginWidth;
27908 if(this.baseRotate == 6 || this.baseRotate == 8){
27910 width = this.thumbEl.getHeight();
27911 this.baseScale = width / this.imageEl.OriginHeight;
27913 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27914 height = this.thumbEl.getWidth();
27915 this.baseScale = height / this.imageEl.OriginHeight;
27918 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27919 height = this.thumbEl.getWidth();
27920 this.baseScale = height / this.imageEl.OriginHeight;
27922 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27923 width = this.thumbEl.getHeight();
27924 this.baseScale = width / this.imageEl.OriginWidth;
27931 width = this.thumbEl.getWidth();
27932 this.baseScale = width / this.imageEl.OriginWidth;
27934 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27935 height = this.thumbEl.getHeight();
27936 this.baseScale = height / this.imageEl.OriginHeight;
27939 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27941 height = this.thumbEl.getHeight();
27942 this.baseScale = height / this.imageEl.OriginHeight;
27944 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27945 width = this.thumbEl.getWidth();
27946 this.baseScale = width / this.imageEl.OriginWidth;
27954 getScaleLevel : function()
27956 return this.baseScale * Math.pow(1.1, this.scale);
27959 onTouchStart : function(e)
27961 if(!this.canvasLoaded){
27962 this.beforeSelectFile(e);
27966 var touches = e.browserEvent.touches;
27972 if(touches.length == 1){
27973 this.onMouseDown(e);
27977 if(touches.length != 2){
27983 for(var i = 0, finger; finger = touches[i]; i++){
27984 coords.push(finger.pageX, finger.pageY);
27987 var x = Math.pow(coords[0] - coords[2], 2);
27988 var y = Math.pow(coords[1] - coords[3], 2);
27990 this.startDistance = Math.sqrt(x + y);
27992 this.startScale = this.scale;
27994 this.pinching = true;
27995 this.dragable = false;
27999 onTouchMove : function(e)
28001 if(!this.pinching && !this.dragable){
28005 var touches = e.browserEvent.touches;
28012 this.onMouseMove(e);
28018 for(var i = 0, finger; finger = touches[i]; i++){
28019 coords.push(finger.pageX, finger.pageY);
28022 var x = Math.pow(coords[0] - coords[2], 2);
28023 var y = Math.pow(coords[1] - coords[3], 2);
28025 this.endDistance = Math.sqrt(x + y);
28027 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28029 if(!this.zoomable()){
28030 this.scale = this.startScale;
28038 onTouchEnd : function(e)
28040 this.pinching = false;
28041 this.dragable = false;
28045 process : function(file, crop)
28048 this.maskEl.mask(this.loadingText);
28051 this.xhr = new XMLHttpRequest();
28053 file.xhr = this.xhr;
28055 this.xhr.open(this.method, this.url, true);
28058 "Accept": "application/json",
28059 "Cache-Control": "no-cache",
28060 "X-Requested-With": "XMLHttpRequest"
28063 for (var headerName in headers) {
28064 var headerValue = headers[headerName];
28066 this.xhr.setRequestHeader(headerName, headerValue);
28072 this.xhr.onload = function()
28074 _this.xhrOnLoad(_this.xhr);
28077 this.xhr.onerror = function()
28079 _this.xhrOnError(_this.xhr);
28082 var formData = new FormData();
28084 formData.append('returnHTML', 'NO');
28087 formData.append('crop', crop);
28090 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28091 formData.append(this.paramName, file, file.name);
28094 if(typeof(file.filename) != 'undefined'){
28095 formData.append('filename', file.filename);
28098 if(typeof(file.mimetype) != 'undefined'){
28099 formData.append('mimetype', file.mimetype);
28102 if(this.fireEvent('arrange', this, formData) != false){
28103 this.xhr.send(formData);
28107 xhrOnLoad : function(xhr)
28110 this.maskEl.unmask();
28113 if (xhr.readyState !== 4) {
28114 this.fireEvent('exception', this, xhr);
28118 var response = Roo.decode(xhr.responseText);
28120 if(!response.success){
28121 this.fireEvent('exception', this, xhr);
28125 var response = Roo.decode(xhr.responseText);
28127 this.fireEvent('upload', this, response);
28131 xhrOnError : function()
28134 this.maskEl.unmask();
28137 Roo.log('xhr on error');
28139 var response = Roo.decode(xhr.responseText);
28145 prepare : function(file)
28148 this.maskEl.mask(this.loadingText);
28154 if(typeof(file) === 'string'){
28155 this.loadCanvas(file);
28159 if(!file || !this.urlAPI){
28164 this.cropType = file.type;
28168 if(this.fireEvent('prepare', this, this.file) != false){
28170 var reader = new FileReader();
28172 reader.onload = function (e) {
28173 if (e.target.error) {
28174 Roo.log(e.target.error);
28178 var buffer = e.target.result,
28179 dataView = new DataView(buffer),
28181 maxOffset = dataView.byteLength - 4,
28185 if (dataView.getUint16(0) === 0xffd8) {
28186 while (offset < maxOffset) {
28187 markerBytes = dataView.getUint16(offset);
28189 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28190 markerLength = dataView.getUint16(offset + 2) + 2;
28191 if (offset + markerLength > dataView.byteLength) {
28192 Roo.log('Invalid meta data: Invalid segment size.');
28196 if(markerBytes == 0xffe1){
28197 _this.parseExifData(
28204 offset += markerLength;
28214 var url = _this.urlAPI.createObjectURL(_this.file);
28216 _this.loadCanvas(url);
28221 reader.readAsArrayBuffer(this.file);
28227 parseExifData : function(dataView, offset, length)
28229 var tiffOffset = offset + 10,
28233 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28234 // No Exif data, might be XMP data instead
28238 // Check for the ASCII code for "Exif" (0x45786966):
28239 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28240 // No Exif data, might be XMP data instead
28243 if (tiffOffset + 8 > dataView.byteLength) {
28244 Roo.log('Invalid Exif data: Invalid segment size.');
28247 // Check for the two null bytes:
28248 if (dataView.getUint16(offset + 8) !== 0x0000) {
28249 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28252 // Check the byte alignment:
28253 switch (dataView.getUint16(tiffOffset)) {
28255 littleEndian = true;
28258 littleEndian = false;
28261 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28264 // Check for the TIFF tag marker (0x002A):
28265 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28266 Roo.log('Invalid Exif data: Missing TIFF marker.');
28269 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28270 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28272 this.parseExifTags(
28275 tiffOffset + dirOffset,
28280 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28285 if (dirOffset + 6 > dataView.byteLength) {
28286 Roo.log('Invalid Exif data: Invalid directory offset.');
28289 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28290 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28291 if (dirEndOffset + 4 > dataView.byteLength) {
28292 Roo.log('Invalid Exif data: Invalid directory size.');
28295 for (i = 0; i < tagsNumber; i += 1) {
28299 dirOffset + 2 + 12 * i, // tag offset
28303 // Return the offset to the next directory:
28304 return dataView.getUint32(dirEndOffset, littleEndian);
28307 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28309 var tag = dataView.getUint16(offset, littleEndian);
28311 this.exif[tag] = this.getExifValue(
28315 dataView.getUint16(offset + 2, littleEndian), // tag type
28316 dataView.getUint32(offset + 4, littleEndian), // tag length
28321 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28323 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28332 Roo.log('Invalid Exif data: Invalid tag type.');
28336 tagSize = tagType.size * length;
28337 // Determine if the value is contained in the dataOffset bytes,
28338 // or if the value at the dataOffset is a pointer to the actual data:
28339 dataOffset = tagSize > 4 ?
28340 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28341 if (dataOffset + tagSize > dataView.byteLength) {
28342 Roo.log('Invalid Exif data: Invalid data offset.');
28345 if (length === 1) {
28346 return tagType.getValue(dataView, dataOffset, littleEndian);
28349 for (i = 0; i < length; i += 1) {
28350 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28353 if (tagType.ascii) {
28355 // Concatenate the chars:
28356 for (i = 0; i < values.length; i += 1) {
28358 // Ignore the terminating NULL byte(s):
28359 if (c === '\u0000') {
28371 Roo.apply(Roo.bootstrap.UploadCropbox, {
28373 'Orientation': 0x0112
28377 1: 0, //'top-left',
28379 3: 180, //'bottom-right',
28380 // 4: 'bottom-left',
28382 6: 90, //'right-top',
28383 // 7: 'right-bottom',
28384 8: 270 //'left-bottom'
28388 // byte, 8-bit unsigned int:
28390 getValue: function (dataView, dataOffset) {
28391 return dataView.getUint8(dataOffset);
28395 // ascii, 8-bit byte:
28397 getValue: function (dataView, dataOffset) {
28398 return String.fromCharCode(dataView.getUint8(dataOffset));
28403 // short, 16 bit int:
28405 getValue: function (dataView, dataOffset, littleEndian) {
28406 return dataView.getUint16(dataOffset, littleEndian);
28410 // long, 32 bit int:
28412 getValue: function (dataView, dataOffset, littleEndian) {
28413 return dataView.getUint32(dataOffset, littleEndian);
28417 // rational = two long values, first is numerator, second is denominator:
28419 getValue: function (dataView, dataOffset, littleEndian) {
28420 return dataView.getUint32(dataOffset, littleEndian) /
28421 dataView.getUint32(dataOffset + 4, littleEndian);
28425 // slong, 32 bit signed int:
28427 getValue: function (dataView, dataOffset, littleEndian) {
28428 return dataView.getInt32(dataOffset, littleEndian);
28432 // srational, two slongs, first is numerator, second is denominator:
28434 getValue: function (dataView, dataOffset, littleEndian) {
28435 return dataView.getInt32(dataOffset, littleEndian) /
28436 dataView.getInt32(dataOffset + 4, littleEndian);
28446 cls : 'btn-group roo-upload-cropbox-rotate-left',
28447 action : 'rotate-left',
28451 cls : 'btn btn-default',
28452 html : '<i class="fa fa-undo"></i>'
28458 cls : 'btn-group roo-upload-cropbox-picture',
28459 action : 'picture',
28463 cls : 'btn btn-default',
28464 html : '<i class="fa fa-picture-o"></i>'
28470 cls : 'btn-group roo-upload-cropbox-rotate-right',
28471 action : 'rotate-right',
28475 cls : 'btn btn-default',
28476 html : '<i class="fa fa-repeat"></i>'
28484 cls : 'btn-group roo-upload-cropbox-rotate-left',
28485 action : 'rotate-left',
28489 cls : 'btn btn-default',
28490 html : '<i class="fa fa-undo"></i>'
28496 cls : 'btn-group roo-upload-cropbox-download',
28497 action : 'download',
28501 cls : 'btn btn-default',
28502 html : '<i class="fa fa-download"></i>'
28508 cls : 'btn-group roo-upload-cropbox-crop',
28513 cls : 'btn btn-default',
28514 html : '<i class="fa fa-crop"></i>'
28520 cls : 'btn-group roo-upload-cropbox-trash',
28525 cls : 'btn btn-default',
28526 html : '<i class="fa fa-trash"></i>'
28532 cls : 'btn-group roo-upload-cropbox-rotate-right',
28533 action : 'rotate-right',
28537 cls : 'btn btn-default',
28538 html : '<i class="fa fa-repeat"></i>'
28546 cls : 'btn-group roo-upload-cropbox-rotate-left',
28547 action : 'rotate-left',
28551 cls : 'btn btn-default',
28552 html : '<i class="fa fa-undo"></i>'
28558 cls : 'btn-group roo-upload-cropbox-rotate-right',
28559 action : 'rotate-right',
28563 cls : 'btn btn-default',
28564 html : '<i class="fa fa-repeat"></i>'
28577 * @class Roo.bootstrap.DocumentManager
28578 * @extends Roo.bootstrap.Component
28579 * Bootstrap DocumentManager class
28580 * @cfg {String} paramName default 'imageUpload'
28581 * @cfg {String} toolTipName default 'filename'
28582 * @cfg {String} method default POST
28583 * @cfg {String} url action url
28584 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28585 * @cfg {Boolean} multiple multiple upload default true
28586 * @cfg {Number} thumbSize default 300
28587 * @cfg {String} fieldLabel
28588 * @cfg {Number} labelWidth default 4
28589 * @cfg {String} labelAlign (left|top) default left
28590 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28591 * @cfg {Number} labellg set the width of label (1-12)
28592 * @cfg {Number} labelmd set the width of label (1-12)
28593 * @cfg {Number} labelsm set the width of label (1-12)
28594 * @cfg {Number} labelxs set the width of label (1-12)
28597 * Create a new DocumentManager
28598 * @param {Object} config The config object
28601 Roo.bootstrap.DocumentManager = function(config){
28602 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28605 this.delegates = [];
28610 * Fire when initial the DocumentManager
28611 * @param {Roo.bootstrap.DocumentManager} this
28616 * inspect selected file
28617 * @param {Roo.bootstrap.DocumentManager} this
28618 * @param {File} file
28623 * Fire when xhr load exception
28624 * @param {Roo.bootstrap.DocumentManager} this
28625 * @param {XMLHttpRequest} xhr
28627 "exception" : true,
28629 * @event afterupload
28630 * Fire when xhr load exception
28631 * @param {Roo.bootstrap.DocumentManager} this
28632 * @param {XMLHttpRequest} xhr
28634 "afterupload" : true,
28637 * prepare the form data
28638 * @param {Roo.bootstrap.DocumentManager} this
28639 * @param {Object} formData
28644 * Fire when remove the file
28645 * @param {Roo.bootstrap.DocumentManager} this
28646 * @param {Object} file
28651 * Fire after refresh the file
28652 * @param {Roo.bootstrap.DocumentManager} this
28657 * Fire after click the image
28658 * @param {Roo.bootstrap.DocumentManager} this
28659 * @param {Object} file
28664 * Fire when upload a image and editable set to true
28665 * @param {Roo.bootstrap.DocumentManager} this
28666 * @param {Object} file
28670 * @event beforeselectfile
28671 * Fire before select file
28672 * @param {Roo.bootstrap.DocumentManager} this
28674 "beforeselectfile" : true,
28677 * Fire before process file
28678 * @param {Roo.bootstrap.DocumentManager} this
28679 * @param {Object} file
28683 * @event previewrendered
28684 * Fire when preview rendered
28685 * @param {Roo.bootstrap.DocumentManager} this
28686 * @param {Object} file
28688 "previewrendered" : true
28693 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28702 paramName : 'imageUpload',
28703 toolTipName : 'filename',
28706 labelAlign : 'left',
28716 getAutoCreate : function()
28718 var managerWidget = {
28720 cls : 'roo-document-manager',
28724 cls : 'roo-document-manager-selector',
28729 cls : 'roo-document-manager-uploader',
28733 cls : 'roo-document-manager-upload-btn',
28734 html : '<i class="fa fa-plus"></i>'
28745 cls : 'column col-md-12',
28750 if(this.fieldLabel.length){
28755 cls : 'column col-md-12',
28756 html : this.fieldLabel
28760 cls : 'column col-md-12',
28765 if(this.labelAlign == 'left'){
28770 html : this.fieldLabel
28779 if(this.labelWidth > 12){
28780 content[0].style = "width: " + this.labelWidth + 'px';
28783 if(this.labelWidth < 13 && this.labelmd == 0){
28784 this.labelmd = this.labelWidth;
28787 if(this.labellg > 0){
28788 content[0].cls += ' col-lg-' + this.labellg;
28789 content[1].cls += ' col-lg-' + (12 - this.labellg);
28792 if(this.labelmd > 0){
28793 content[0].cls += ' col-md-' + this.labelmd;
28794 content[1].cls += ' col-md-' + (12 - this.labelmd);
28797 if(this.labelsm > 0){
28798 content[0].cls += ' col-sm-' + this.labelsm;
28799 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28802 if(this.labelxs > 0){
28803 content[0].cls += ' col-xs-' + this.labelxs;
28804 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28812 cls : 'row clearfix',
28820 initEvents : function()
28822 this.managerEl = this.el.select('.roo-document-manager', true).first();
28823 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28825 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28826 this.selectorEl.hide();
28829 this.selectorEl.attr('multiple', 'multiple');
28832 this.selectorEl.on('change', this.onFileSelected, this);
28834 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28835 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28837 this.uploader.on('click', this.onUploaderClick, this);
28839 this.renderProgressDialog();
28843 window.addEventListener("resize", function() { _this.refresh(); } );
28845 this.fireEvent('initial', this);
28848 renderProgressDialog : function()
28852 this.progressDialog = new Roo.bootstrap.Modal({
28853 cls : 'roo-document-manager-progress-dialog',
28854 allow_close : false,
28864 btnclick : function() {
28865 _this.uploadCancel();
28871 this.progressDialog.render(Roo.get(document.body));
28873 this.progress = new Roo.bootstrap.Progress({
28874 cls : 'roo-document-manager-progress',
28879 this.progress.render(this.progressDialog.getChildContainer());
28881 this.progressBar = new Roo.bootstrap.ProgressBar({
28882 cls : 'roo-document-manager-progress-bar',
28885 aria_valuemax : 12,
28889 this.progressBar.render(this.progress.getChildContainer());
28892 onUploaderClick : function(e)
28894 e.preventDefault();
28896 if(this.fireEvent('beforeselectfile', this) != false){
28897 this.selectorEl.dom.click();
28902 onFileSelected : function(e)
28904 e.preventDefault();
28906 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28910 Roo.each(this.selectorEl.dom.files, function(file){
28911 if(this.fireEvent('inspect', this, file) != false){
28912 this.files.push(file);
28922 this.selectorEl.dom.value = '';
28924 if(!this.files || !this.files.length){
28928 if(this.boxes > 0 && this.files.length > this.boxes){
28929 this.files = this.files.slice(0, this.boxes);
28932 this.uploader.show();
28934 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28935 this.uploader.hide();
28944 Roo.each(this.files, function(file){
28946 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28947 var f = this.renderPreview(file);
28952 if(file.type.indexOf('image') != -1){
28953 this.delegates.push(
28955 _this.process(file);
28956 }).createDelegate(this)
28964 _this.process(file);
28965 }).createDelegate(this)
28970 this.files = files;
28972 this.delegates = this.delegates.concat(docs);
28974 if(!this.delegates.length){
28979 this.progressBar.aria_valuemax = this.delegates.length;
28986 arrange : function()
28988 if(!this.delegates.length){
28989 this.progressDialog.hide();
28994 var delegate = this.delegates.shift();
28996 this.progressDialog.show();
28998 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29000 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29005 refresh : function()
29007 this.uploader.show();
29009 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29010 this.uploader.hide();
29013 Roo.isTouch ? this.closable(false) : this.closable(true);
29015 this.fireEvent('refresh', this);
29018 onRemove : function(e, el, o)
29020 e.preventDefault();
29022 this.fireEvent('remove', this, o);
29026 remove : function(o)
29030 Roo.each(this.files, function(file){
29031 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29040 this.files = files;
29047 Roo.each(this.files, function(file){
29052 file.target.remove();
29061 onClick : function(e, el, o)
29063 e.preventDefault();
29065 this.fireEvent('click', this, o);
29069 closable : function(closable)
29071 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29073 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29085 xhrOnLoad : function(xhr)
29087 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29091 if (xhr.readyState !== 4) {
29093 this.fireEvent('exception', this, xhr);
29097 var response = Roo.decode(xhr.responseText);
29099 if(!response.success){
29101 this.fireEvent('exception', this, xhr);
29105 var file = this.renderPreview(response.data);
29107 this.files.push(file);
29111 this.fireEvent('afterupload', this, xhr);
29115 xhrOnError : function(xhr)
29117 Roo.log('xhr on error');
29119 var response = Roo.decode(xhr.responseText);
29126 process : function(file)
29128 if(this.fireEvent('process', this, file) !== false){
29129 if(this.editable && file.type.indexOf('image') != -1){
29130 this.fireEvent('edit', this, file);
29134 this.uploadStart(file, false);
29141 uploadStart : function(file, crop)
29143 this.xhr = new XMLHttpRequest();
29145 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29150 file.xhr = this.xhr;
29152 this.managerEl.createChild({
29154 cls : 'roo-document-manager-loading',
29158 tooltip : file.name,
29159 cls : 'roo-document-manager-thumb',
29160 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29166 this.xhr.open(this.method, this.url, true);
29169 "Accept": "application/json",
29170 "Cache-Control": "no-cache",
29171 "X-Requested-With": "XMLHttpRequest"
29174 for (var headerName in headers) {
29175 var headerValue = headers[headerName];
29177 this.xhr.setRequestHeader(headerName, headerValue);
29183 this.xhr.onload = function()
29185 _this.xhrOnLoad(_this.xhr);
29188 this.xhr.onerror = function()
29190 _this.xhrOnError(_this.xhr);
29193 var formData = new FormData();
29195 formData.append('returnHTML', 'NO');
29198 formData.append('crop', crop);
29201 formData.append(this.paramName, file, file.name);
29208 if(this.fireEvent('prepare', this, formData, options) != false){
29210 if(options.manually){
29214 this.xhr.send(formData);
29218 this.uploadCancel();
29221 uploadCancel : function()
29227 this.delegates = [];
29229 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29236 renderPreview : function(file)
29238 if(typeof(file.target) != 'undefined' && file.target){
29242 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29244 var previewEl = this.managerEl.createChild({
29246 cls : 'roo-document-manager-preview',
29250 tooltip : file[this.toolTipName],
29251 cls : 'roo-document-manager-thumb',
29252 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29257 html : '<i class="fa fa-times-circle"></i>'
29262 var close = previewEl.select('button.close', true).first();
29264 close.on('click', this.onRemove, this, file);
29266 file.target = previewEl;
29268 var image = previewEl.select('img', true).first();
29272 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29274 image.on('click', this.onClick, this, file);
29276 this.fireEvent('previewrendered', this, file);
29282 onPreviewLoad : function(file, image)
29284 if(typeof(file.target) == 'undefined' || !file.target){
29288 var width = image.dom.naturalWidth || image.dom.width;
29289 var height = image.dom.naturalHeight || image.dom.height;
29291 if(width > height){
29292 file.target.addClass('wide');
29296 file.target.addClass('tall');
29301 uploadFromSource : function(file, crop)
29303 this.xhr = new XMLHttpRequest();
29305 this.managerEl.createChild({
29307 cls : 'roo-document-manager-loading',
29311 tooltip : file.name,
29312 cls : 'roo-document-manager-thumb',
29313 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29319 this.xhr.open(this.method, this.url, true);
29322 "Accept": "application/json",
29323 "Cache-Control": "no-cache",
29324 "X-Requested-With": "XMLHttpRequest"
29327 for (var headerName in headers) {
29328 var headerValue = headers[headerName];
29330 this.xhr.setRequestHeader(headerName, headerValue);
29336 this.xhr.onload = function()
29338 _this.xhrOnLoad(_this.xhr);
29341 this.xhr.onerror = function()
29343 _this.xhrOnError(_this.xhr);
29346 var formData = new FormData();
29348 formData.append('returnHTML', 'NO');
29350 formData.append('crop', crop);
29352 if(typeof(file.filename) != 'undefined'){
29353 formData.append('filename', file.filename);
29356 if(typeof(file.mimetype) != 'undefined'){
29357 formData.append('mimetype', file.mimetype);
29362 if(this.fireEvent('prepare', this, formData) != false){
29363 this.xhr.send(formData);
29373 * @class Roo.bootstrap.DocumentViewer
29374 * @extends Roo.bootstrap.Component
29375 * Bootstrap DocumentViewer class
29376 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29377 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29380 * Create a new DocumentViewer
29381 * @param {Object} config The config object
29384 Roo.bootstrap.DocumentViewer = function(config){
29385 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29390 * Fire after initEvent
29391 * @param {Roo.bootstrap.DocumentViewer} this
29397 * @param {Roo.bootstrap.DocumentViewer} this
29402 * Fire after download button
29403 * @param {Roo.bootstrap.DocumentViewer} this
29408 * Fire after trash button
29409 * @param {Roo.bootstrap.DocumentViewer} this
29416 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29418 showDownload : true,
29422 getAutoCreate : function()
29426 cls : 'roo-document-viewer',
29430 cls : 'roo-document-viewer-body',
29434 cls : 'roo-document-viewer-thumb',
29438 cls : 'roo-document-viewer-image'
29446 cls : 'roo-document-viewer-footer',
29449 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29453 cls : 'btn-group roo-document-viewer-download',
29457 cls : 'btn btn-default',
29458 html : '<i class="fa fa-download"></i>'
29464 cls : 'btn-group roo-document-viewer-trash',
29468 cls : 'btn btn-default',
29469 html : '<i class="fa fa-trash"></i>'
29482 initEvents : function()
29484 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29485 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29487 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29488 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29490 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29491 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29493 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29494 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29496 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29497 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29499 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29500 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29502 this.bodyEl.on('click', this.onClick, this);
29503 this.downloadBtn.on('click', this.onDownload, this);
29504 this.trashBtn.on('click', this.onTrash, this);
29506 this.downloadBtn.hide();
29507 this.trashBtn.hide();
29509 if(this.showDownload){
29510 this.downloadBtn.show();
29513 if(this.showTrash){
29514 this.trashBtn.show();
29517 if(!this.showDownload && !this.showTrash) {
29518 this.footerEl.hide();
29523 initial : function()
29525 this.fireEvent('initial', this);
29529 onClick : function(e)
29531 e.preventDefault();
29533 this.fireEvent('click', this);
29536 onDownload : function(e)
29538 e.preventDefault();
29540 this.fireEvent('download', this);
29543 onTrash : function(e)
29545 e.preventDefault();
29547 this.fireEvent('trash', this);
29559 * @class Roo.bootstrap.NavProgressBar
29560 * @extends Roo.bootstrap.Component
29561 * Bootstrap NavProgressBar class
29564 * Create a new nav progress bar
29565 * @param {Object} config The config object
29568 Roo.bootstrap.NavProgressBar = function(config){
29569 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29571 this.bullets = this.bullets || [];
29573 // Roo.bootstrap.NavProgressBar.register(this);
29577 * Fires when the active item changes
29578 * @param {Roo.bootstrap.NavProgressBar} this
29579 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29580 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29587 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29592 getAutoCreate : function()
29594 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29598 cls : 'roo-navigation-bar-group',
29602 cls : 'roo-navigation-top-bar'
29606 cls : 'roo-navigation-bullets-bar',
29610 cls : 'roo-navigation-bar'
29617 cls : 'roo-navigation-bottom-bar'
29627 initEvents: function()
29632 onRender : function(ct, position)
29634 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29636 if(this.bullets.length){
29637 Roo.each(this.bullets, function(b){
29646 addItem : function(cfg)
29648 var item = new Roo.bootstrap.NavProgressItem(cfg);
29650 item.parentId = this.id;
29651 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29654 var top = new Roo.bootstrap.Element({
29656 cls : 'roo-navigation-bar-text'
29659 var bottom = new Roo.bootstrap.Element({
29661 cls : 'roo-navigation-bar-text'
29664 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29665 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29667 var topText = new Roo.bootstrap.Element({
29669 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29672 var bottomText = new Roo.bootstrap.Element({
29674 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29677 topText.onRender(top.el, null);
29678 bottomText.onRender(bottom.el, null);
29681 item.bottomEl = bottom;
29684 this.barItems.push(item);
29689 getActive : function()
29691 var active = false;
29693 Roo.each(this.barItems, function(v){
29695 if (!v.isActive()) {
29707 setActiveItem : function(item)
29711 Roo.each(this.barItems, function(v){
29712 if (v.rid == item.rid) {
29716 if (v.isActive()) {
29717 v.setActive(false);
29722 item.setActive(true);
29724 this.fireEvent('changed', this, item, prev);
29727 getBarItem: function(rid)
29731 Roo.each(this.barItems, function(e) {
29732 if (e.rid != rid) {
29743 indexOfItem : function(item)
29747 Roo.each(this.barItems, function(v, i){
29749 if (v.rid != item.rid) {
29760 setActiveNext : function()
29762 var i = this.indexOfItem(this.getActive());
29764 if (i > this.barItems.length) {
29768 this.setActiveItem(this.barItems[i+1]);
29771 setActivePrev : function()
29773 var i = this.indexOfItem(this.getActive());
29779 this.setActiveItem(this.barItems[i-1]);
29782 format : function()
29784 if(!this.barItems.length){
29788 var width = 100 / this.barItems.length;
29790 Roo.each(this.barItems, function(i){
29791 i.el.setStyle('width', width + '%');
29792 i.topEl.el.setStyle('width', width + '%');
29793 i.bottomEl.el.setStyle('width', width + '%');
29802 * Nav Progress Item
29807 * @class Roo.bootstrap.NavProgressItem
29808 * @extends Roo.bootstrap.Component
29809 * Bootstrap NavProgressItem class
29810 * @cfg {String} rid the reference id
29811 * @cfg {Boolean} active (true|false) Is item active default false
29812 * @cfg {Boolean} disabled (true|false) Is item active default false
29813 * @cfg {String} html
29814 * @cfg {String} position (top|bottom) text position default bottom
29815 * @cfg {String} icon show icon instead of number
29818 * Create a new NavProgressItem
29819 * @param {Object} config The config object
29821 Roo.bootstrap.NavProgressItem = function(config){
29822 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29827 * The raw click event for the entire grid.
29828 * @param {Roo.bootstrap.NavProgressItem} this
29829 * @param {Roo.EventObject} e
29836 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29842 position : 'bottom',
29845 getAutoCreate : function()
29847 var iconCls = 'roo-navigation-bar-item-icon';
29849 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29853 cls: 'roo-navigation-bar-item',
29863 cfg.cls += ' active';
29866 cfg.cls += ' disabled';
29872 disable : function()
29874 this.setDisabled(true);
29877 enable : function()
29879 this.setDisabled(false);
29882 initEvents: function()
29884 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29886 this.iconEl.on('click', this.onClick, this);
29889 onClick : function(e)
29891 e.preventDefault();
29897 if(this.fireEvent('click', this, e) === false){
29901 this.parent().setActiveItem(this);
29904 isActive: function ()
29906 return this.active;
29909 setActive : function(state)
29911 if(this.active == state){
29915 this.active = state;
29918 this.el.addClass('active');
29922 this.el.removeClass('active');
29927 setDisabled : function(state)
29929 if(this.disabled == state){
29933 this.disabled = state;
29936 this.el.addClass('disabled');
29940 this.el.removeClass('disabled');
29943 tooltipEl : function()
29945 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29958 * @class Roo.bootstrap.FieldLabel
29959 * @extends Roo.bootstrap.Component
29960 * Bootstrap FieldLabel class
29961 * @cfg {String} html contents of the element
29962 * @cfg {String} tag tag of the element default label
29963 * @cfg {String} cls class of the element
29964 * @cfg {String} target label target
29965 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29966 * @cfg {String} invalidClass default "text-warning"
29967 * @cfg {String} validClass default "text-success"
29968 * @cfg {String} iconTooltip default "This field is required"
29969 * @cfg {String} indicatorpos (left|right) default left
29972 * Create a new FieldLabel
29973 * @param {Object} config The config object
29976 Roo.bootstrap.FieldLabel = function(config){
29977 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29982 * Fires after the field has been marked as invalid.
29983 * @param {Roo.form.FieldLabel} this
29984 * @param {String} msg The validation message
29989 * Fires after the field has been validated with no errors.
29990 * @param {Roo.form.FieldLabel} this
29996 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30003 invalidClass : 'has-warning',
30004 validClass : 'has-success',
30005 iconTooltip : 'This field is required',
30006 indicatorpos : 'left',
30008 getAutoCreate : function(){
30011 if (!this.allowBlank) {
30017 cls : 'roo-bootstrap-field-label ' + this.cls,
30022 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30023 tooltip : this.iconTooltip
30032 if(this.indicatorpos == 'right'){
30035 cls : 'roo-bootstrap-field-label ' + this.cls,
30044 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30045 tooltip : this.iconTooltip
30054 initEvents: function()
30056 Roo.bootstrap.Element.superclass.initEvents.call(this);
30058 this.indicator = this.indicatorEl();
30060 if(this.indicator){
30061 this.indicator.removeClass('visible');
30062 this.indicator.addClass('invisible');
30065 Roo.bootstrap.FieldLabel.register(this);
30068 indicatorEl : function()
30070 var indicator = this.el.select('i.roo-required-indicator',true).first();
30081 * Mark this field as valid
30083 markValid : function()
30085 if(this.indicator){
30086 this.indicator.removeClass('visible');
30087 this.indicator.addClass('invisible');
30090 this.el.removeClass(this.invalidClass);
30092 this.el.addClass(this.validClass);
30094 this.fireEvent('valid', this);
30098 * Mark this field as invalid
30099 * @param {String} msg The validation message
30101 markInvalid : function(msg)
30103 if(this.indicator){
30104 this.indicator.removeClass('invisible');
30105 this.indicator.addClass('visible');
30108 this.el.removeClass(this.validClass);
30110 this.el.addClass(this.invalidClass);
30112 this.fireEvent('invalid', this, msg);
30118 Roo.apply(Roo.bootstrap.FieldLabel, {
30123 * register a FieldLabel Group
30124 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30126 register : function(label)
30128 if(this.groups.hasOwnProperty(label.target)){
30132 this.groups[label.target] = label;
30136 * fetch a FieldLabel Group based on the target
30137 * @param {string} target
30138 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30140 get: function(target) {
30141 if (typeof(this.groups[target]) == 'undefined') {
30145 return this.groups[target] ;
30154 * page DateSplitField.
30160 * @class Roo.bootstrap.DateSplitField
30161 * @extends Roo.bootstrap.Component
30162 * Bootstrap DateSplitField class
30163 * @cfg {string} fieldLabel - the label associated
30164 * @cfg {Number} labelWidth set the width of label (0-12)
30165 * @cfg {String} labelAlign (top|left)
30166 * @cfg {Boolean} dayAllowBlank (true|false) default false
30167 * @cfg {Boolean} monthAllowBlank (true|false) default false
30168 * @cfg {Boolean} yearAllowBlank (true|false) default false
30169 * @cfg {string} dayPlaceholder
30170 * @cfg {string} monthPlaceholder
30171 * @cfg {string} yearPlaceholder
30172 * @cfg {string} dayFormat default 'd'
30173 * @cfg {string} monthFormat default 'm'
30174 * @cfg {string} yearFormat default 'Y'
30175 * @cfg {Number} labellg set the width of label (1-12)
30176 * @cfg {Number} labelmd set the width of label (1-12)
30177 * @cfg {Number} labelsm set the width of label (1-12)
30178 * @cfg {Number} labelxs set the width of label (1-12)
30182 * Create a new DateSplitField
30183 * @param {Object} config The config object
30186 Roo.bootstrap.DateSplitField = function(config){
30187 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30193 * getting the data of years
30194 * @param {Roo.bootstrap.DateSplitField} this
30195 * @param {Object} years
30200 * getting the data of days
30201 * @param {Roo.bootstrap.DateSplitField} this
30202 * @param {Object} days
30207 * Fires after the field has been marked as invalid.
30208 * @param {Roo.form.Field} this
30209 * @param {String} msg The validation message
30214 * Fires after the field has been validated with no errors.
30215 * @param {Roo.form.Field} this
30221 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30224 labelAlign : 'top',
30226 dayAllowBlank : false,
30227 monthAllowBlank : false,
30228 yearAllowBlank : false,
30229 dayPlaceholder : '',
30230 monthPlaceholder : '',
30231 yearPlaceholder : '',
30235 isFormField : true,
30241 getAutoCreate : function()
30245 cls : 'row roo-date-split-field-group',
30250 cls : 'form-hidden-field roo-date-split-field-group-value',
30256 var labelCls = 'col-md-12';
30257 var contentCls = 'col-md-4';
30259 if(this.fieldLabel){
30263 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30267 html : this.fieldLabel
30272 if(this.labelAlign == 'left'){
30274 if(this.labelWidth > 12){
30275 label.style = "width: " + this.labelWidth + 'px';
30278 if(this.labelWidth < 13 && this.labelmd == 0){
30279 this.labelmd = this.labelWidth;
30282 if(this.labellg > 0){
30283 labelCls = ' col-lg-' + this.labellg;
30284 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30287 if(this.labelmd > 0){
30288 labelCls = ' col-md-' + this.labelmd;
30289 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30292 if(this.labelsm > 0){
30293 labelCls = ' col-sm-' + this.labelsm;
30294 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30297 if(this.labelxs > 0){
30298 labelCls = ' col-xs-' + this.labelxs;
30299 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30303 label.cls += ' ' + labelCls;
30305 cfg.cn.push(label);
30308 Roo.each(['day', 'month', 'year'], function(t){
30311 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30318 inputEl: function ()
30320 return this.el.select('.roo-date-split-field-group-value', true).first();
30323 onRender : function(ct, position)
30327 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30329 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30331 this.dayField = new Roo.bootstrap.ComboBox({
30332 allowBlank : this.dayAllowBlank,
30333 alwaysQuery : true,
30334 displayField : 'value',
30337 forceSelection : true,
30339 placeholder : this.dayPlaceholder,
30340 selectOnFocus : true,
30341 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30342 triggerAction : 'all',
30344 valueField : 'value',
30345 store : new Roo.data.SimpleStore({
30346 data : (function() {
30348 _this.fireEvent('days', _this, days);
30351 fields : [ 'value' ]
30354 select : function (_self, record, index)
30356 _this.setValue(_this.getValue());
30361 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30363 this.monthField = new Roo.bootstrap.MonthField({
30364 after : '<i class=\"fa fa-calendar\"></i>',
30365 allowBlank : this.monthAllowBlank,
30366 placeholder : this.monthPlaceholder,
30369 render : function (_self)
30371 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30372 e.preventDefault();
30376 select : function (_self, oldvalue, newvalue)
30378 _this.setValue(_this.getValue());
30383 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30385 this.yearField = new Roo.bootstrap.ComboBox({
30386 allowBlank : this.yearAllowBlank,
30387 alwaysQuery : true,
30388 displayField : 'value',
30391 forceSelection : true,
30393 placeholder : this.yearPlaceholder,
30394 selectOnFocus : true,
30395 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30396 triggerAction : 'all',
30398 valueField : 'value',
30399 store : new Roo.data.SimpleStore({
30400 data : (function() {
30402 _this.fireEvent('years', _this, years);
30405 fields : [ 'value' ]
30408 select : function (_self, record, index)
30410 _this.setValue(_this.getValue());
30415 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30418 setValue : function(v, format)
30420 this.inputEl.dom.value = v;
30422 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30424 var d = Date.parseDate(v, f);
30431 this.setDay(d.format(this.dayFormat));
30432 this.setMonth(d.format(this.monthFormat));
30433 this.setYear(d.format(this.yearFormat));
30440 setDay : function(v)
30442 this.dayField.setValue(v);
30443 this.inputEl.dom.value = this.getValue();
30448 setMonth : function(v)
30450 this.monthField.setValue(v, true);
30451 this.inputEl.dom.value = this.getValue();
30456 setYear : function(v)
30458 this.yearField.setValue(v);
30459 this.inputEl.dom.value = this.getValue();
30464 getDay : function()
30466 return this.dayField.getValue();
30469 getMonth : function()
30471 return this.monthField.getValue();
30474 getYear : function()
30476 return this.yearField.getValue();
30479 getValue : function()
30481 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30483 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30493 this.inputEl.dom.value = '';
30498 validate : function()
30500 var d = this.dayField.validate();
30501 var m = this.monthField.validate();
30502 var y = this.yearField.validate();
30507 (!this.dayAllowBlank && !d) ||
30508 (!this.monthAllowBlank && !m) ||
30509 (!this.yearAllowBlank && !y)
30514 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30523 this.markInvalid();
30528 markValid : function()
30531 var label = this.el.select('label', true).first();
30532 var icon = this.el.select('i.fa-star', true).first();
30538 this.fireEvent('valid', this);
30542 * Mark this field as invalid
30543 * @param {String} msg The validation message
30545 markInvalid : function(msg)
30548 var label = this.el.select('label', true).first();
30549 var icon = this.el.select('i.fa-star', true).first();
30551 if(label && !icon){
30552 this.el.select('.roo-date-split-field-label', true).createChild({
30554 cls : 'text-danger fa fa-lg fa-star',
30555 tooltip : 'This field is required',
30556 style : 'margin-right:5px;'
30560 this.fireEvent('invalid', this, msg);
30563 clearInvalid : function()
30565 var label = this.el.select('label', true).first();
30566 var icon = this.el.select('i.fa-star', true).first();
30572 this.fireEvent('valid', this);
30575 getName: function()
30585 * http://masonry.desandro.com
30587 * The idea is to render all the bricks based on vertical width...
30589 * The original code extends 'outlayer' - we might need to use that....
30595 * @class Roo.bootstrap.LayoutMasonry
30596 * @extends Roo.bootstrap.Component
30597 * Bootstrap Layout Masonry class
30600 * Create a new Element
30601 * @param {Object} config The config object
30604 Roo.bootstrap.LayoutMasonry = function(config){
30606 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30610 Roo.bootstrap.LayoutMasonry.register(this);
30616 * Fire after layout the items
30617 * @param {Roo.bootstrap.LayoutMasonry} this
30618 * @param {Roo.EventObject} e
30625 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30628 * @cfg {Boolean} isLayoutInstant = no animation?
30630 isLayoutInstant : false, // needed?
30633 * @cfg {Number} boxWidth width of the columns
30638 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30643 * @cfg {Number} padWidth padding below box..
30648 * @cfg {Number} gutter gutter width..
30653 * @cfg {Number} maxCols maximum number of columns
30659 * @cfg {Boolean} isAutoInitial defalut true
30661 isAutoInitial : true,
30666 * @cfg {Boolean} isHorizontal defalut false
30668 isHorizontal : false,
30670 currentSize : null,
30676 bricks: null, //CompositeElement
30680 _isLayoutInited : false,
30682 // isAlternative : false, // only use for vertical layout...
30685 * @cfg {Number} alternativePadWidth padding below box..
30687 alternativePadWidth : 50,
30689 selectedBrick : [],
30691 getAutoCreate : function(){
30693 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30697 cls: 'blog-masonary-wrapper ' + this.cls,
30699 cls : 'mas-boxes masonary'
30706 getChildContainer: function( )
30708 if (this.boxesEl) {
30709 return this.boxesEl;
30712 this.boxesEl = this.el.select('.mas-boxes').first();
30714 return this.boxesEl;
30718 initEvents : function()
30722 if(this.isAutoInitial){
30723 Roo.log('hook children rendered');
30724 this.on('childrenrendered', function() {
30725 Roo.log('children rendered');
30731 initial : function()
30733 this.selectedBrick = [];
30735 this.currentSize = this.el.getBox(true);
30737 Roo.EventManager.onWindowResize(this.resize, this);
30739 if(!this.isAutoInitial){
30747 //this.layout.defer(500,this);
30751 resize : function()
30753 var cs = this.el.getBox(true);
30756 this.currentSize.width == cs.width &&
30757 this.currentSize.x == cs.x &&
30758 this.currentSize.height == cs.height &&
30759 this.currentSize.y == cs.y
30761 Roo.log("no change in with or X or Y");
30765 this.currentSize = cs;
30771 layout : function()
30773 this._resetLayout();
30775 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30777 this.layoutItems( isInstant );
30779 this._isLayoutInited = true;
30781 this.fireEvent('layout', this);
30785 _resetLayout : function()
30787 if(this.isHorizontal){
30788 this.horizontalMeasureColumns();
30792 this.verticalMeasureColumns();
30796 verticalMeasureColumns : function()
30798 this.getContainerWidth();
30800 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30801 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30805 var boxWidth = this.boxWidth + this.padWidth;
30807 if(this.containerWidth < this.boxWidth){
30808 boxWidth = this.containerWidth
30811 var containerWidth = this.containerWidth;
30813 var cols = Math.floor(containerWidth / boxWidth);
30815 this.cols = Math.max( cols, 1 );
30817 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30819 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30821 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30823 this.colWidth = boxWidth + avail - this.padWidth;
30825 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30826 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30829 horizontalMeasureColumns : function()
30831 this.getContainerWidth();
30833 var boxWidth = this.boxWidth;
30835 if(this.containerWidth < boxWidth){
30836 boxWidth = this.containerWidth;
30839 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30841 this.el.setHeight(boxWidth);
30845 getContainerWidth : function()
30847 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30850 layoutItems : function( isInstant )
30852 Roo.log(this.bricks);
30854 var items = Roo.apply([], this.bricks);
30856 if(this.isHorizontal){
30857 this._horizontalLayoutItems( items , isInstant );
30861 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30862 // this._verticalAlternativeLayoutItems( items , isInstant );
30866 this._verticalLayoutItems( items , isInstant );
30870 _verticalLayoutItems : function ( items , isInstant)
30872 if ( !items || !items.length ) {
30877 ['xs', 'xs', 'xs', 'tall'],
30878 ['xs', 'xs', 'tall'],
30879 ['xs', 'xs', 'sm'],
30880 ['xs', 'xs', 'xs'],
30886 ['sm', 'xs', 'xs'],
30890 ['tall', 'xs', 'xs', 'xs'],
30891 ['tall', 'xs', 'xs'],
30903 Roo.each(items, function(item, k){
30905 switch (item.size) {
30906 // these layouts take up a full box,
30917 boxes.push([item]);
30940 var filterPattern = function(box, length)
30948 var pattern = box.slice(0, length);
30952 Roo.each(pattern, function(i){
30953 format.push(i.size);
30956 Roo.each(standard, function(s){
30958 if(String(s) != String(format)){
30967 if(!match && length == 1){
30972 filterPattern(box, length - 1);
30976 queue.push(pattern);
30978 box = box.slice(length, box.length);
30980 filterPattern(box, 4);
30986 Roo.each(boxes, function(box, k){
30992 if(box.length == 1){
30997 filterPattern(box, 4);
31001 this._processVerticalLayoutQueue( queue, isInstant );
31005 // _verticalAlternativeLayoutItems : function( items , isInstant )
31007 // if ( !items || !items.length ) {
31011 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31015 _horizontalLayoutItems : function ( items , isInstant)
31017 if ( !items || !items.length || items.length < 3) {
31023 var eItems = items.slice(0, 3);
31025 items = items.slice(3, items.length);
31028 ['xs', 'xs', 'xs', 'wide'],
31029 ['xs', 'xs', 'wide'],
31030 ['xs', 'xs', 'sm'],
31031 ['xs', 'xs', 'xs'],
31037 ['sm', 'xs', 'xs'],
31041 ['wide', 'xs', 'xs', 'xs'],
31042 ['wide', 'xs', 'xs'],
31055 Roo.each(items, function(item, k){
31057 switch (item.size) {
31068 boxes.push([item]);
31092 var filterPattern = function(box, length)
31100 var pattern = box.slice(0, length);
31104 Roo.each(pattern, function(i){
31105 format.push(i.size);
31108 Roo.each(standard, function(s){
31110 if(String(s) != String(format)){
31119 if(!match && length == 1){
31124 filterPattern(box, length - 1);
31128 queue.push(pattern);
31130 box = box.slice(length, box.length);
31132 filterPattern(box, 4);
31138 Roo.each(boxes, function(box, k){
31144 if(box.length == 1){
31149 filterPattern(box, 4);
31156 var pos = this.el.getBox(true);
31160 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31162 var hit_end = false;
31164 Roo.each(queue, function(box){
31168 Roo.each(box, function(b){
31170 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31180 Roo.each(box, function(b){
31182 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31185 mx = Math.max(mx, b.x);
31189 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31193 Roo.each(box, function(b){
31195 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31209 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31212 /** Sets position of item in DOM
31213 * @param {Element} item
31214 * @param {Number} x - horizontal position
31215 * @param {Number} y - vertical position
31216 * @param {Boolean} isInstant - disables transitions
31218 _processVerticalLayoutQueue : function( queue, isInstant )
31220 var pos = this.el.getBox(true);
31225 for (var i = 0; i < this.cols; i++){
31229 Roo.each(queue, function(box, k){
31231 var col = k % this.cols;
31233 Roo.each(box, function(b,kk){
31235 b.el.position('absolute');
31237 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31238 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31240 if(b.size == 'md-left' || b.size == 'md-right'){
31241 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31242 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31245 b.el.setWidth(width);
31246 b.el.setHeight(height);
31248 b.el.select('iframe',true).setSize(width,height);
31252 for (var i = 0; i < this.cols; i++){
31254 if(maxY[i] < maxY[col]){
31259 col = Math.min(col, i);
31263 x = pos.x + col * (this.colWidth + this.padWidth);
31267 var positions = [];
31269 switch (box.length){
31271 positions = this.getVerticalOneBoxColPositions(x, y, box);
31274 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31277 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31280 positions = this.getVerticalFourBoxColPositions(x, y, box);
31286 Roo.each(box, function(b,kk){
31288 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31290 var sz = b.el.getSize();
31292 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31300 for (var i = 0; i < this.cols; i++){
31301 mY = Math.max(mY, maxY[i]);
31304 this.el.setHeight(mY - pos.y);
31308 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31310 // var pos = this.el.getBox(true);
31313 // var maxX = pos.right;
31315 // var maxHeight = 0;
31317 // Roo.each(items, function(item, k){
31321 // item.el.position('absolute');
31323 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31325 // item.el.setWidth(width);
31327 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31329 // item.el.setHeight(height);
31332 // item.el.setXY([x, y], isInstant ? false : true);
31334 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31337 // y = y + height + this.alternativePadWidth;
31339 // maxHeight = maxHeight + height + this.alternativePadWidth;
31343 // this.el.setHeight(maxHeight);
31347 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31349 var pos = this.el.getBox(true);
31354 var maxX = pos.right;
31356 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31358 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31360 Roo.each(queue, function(box, k){
31362 Roo.each(box, function(b, kk){
31364 b.el.position('absolute');
31366 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31367 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31369 if(b.size == 'md-left' || b.size == 'md-right'){
31370 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31371 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31374 b.el.setWidth(width);
31375 b.el.setHeight(height);
31383 var positions = [];
31385 switch (box.length){
31387 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31390 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31393 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31396 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31402 Roo.each(box, function(b,kk){
31404 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31406 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31414 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31416 Roo.each(eItems, function(b,k){
31418 b.size = (k == 0) ? 'sm' : 'xs';
31419 b.x = (k == 0) ? 2 : 1;
31420 b.y = (k == 0) ? 2 : 1;
31422 b.el.position('absolute');
31424 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31426 b.el.setWidth(width);
31428 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31430 b.el.setHeight(height);
31434 var positions = [];
31437 x : maxX - this.unitWidth * 2 - this.gutter,
31442 x : maxX - this.unitWidth,
31443 y : minY + (this.unitWidth + this.gutter) * 2
31447 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31451 Roo.each(eItems, function(b,k){
31453 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31459 getVerticalOneBoxColPositions : function(x, y, box)
31463 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31465 if(box[0].size == 'md-left'){
31469 if(box[0].size == 'md-right'){
31474 x : x + (this.unitWidth + this.gutter) * rand,
31481 getVerticalTwoBoxColPositions : function(x, y, box)
31485 if(box[0].size == 'xs'){
31489 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31493 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31507 x : x + (this.unitWidth + this.gutter) * 2,
31508 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31515 getVerticalThreeBoxColPositions : function(x, y, box)
31519 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31527 x : x + (this.unitWidth + this.gutter) * 1,
31532 x : x + (this.unitWidth + this.gutter) * 2,
31540 if(box[0].size == 'xs' && box[1].size == 'xs'){
31549 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31553 x : x + (this.unitWidth + this.gutter) * 1,
31567 x : x + (this.unitWidth + this.gutter) * 2,
31572 x : x + (this.unitWidth + this.gutter) * 2,
31573 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31580 getVerticalFourBoxColPositions : function(x, y, box)
31584 if(box[0].size == 'xs'){
31593 y : y + (this.unitHeight + this.gutter) * 1
31598 y : y + (this.unitHeight + this.gutter) * 2
31602 x : x + (this.unitWidth + this.gutter) * 1,
31616 x : x + (this.unitWidth + this.gutter) * 2,
31621 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31622 y : y + (this.unitHeight + this.gutter) * 1
31626 x : x + (this.unitWidth + this.gutter) * 2,
31627 y : y + (this.unitWidth + this.gutter) * 2
31634 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31638 if(box[0].size == 'md-left'){
31640 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31647 if(box[0].size == 'md-right'){
31649 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31650 y : minY + (this.unitWidth + this.gutter) * 1
31656 var rand = Math.floor(Math.random() * (4 - box[0].y));
31659 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31660 y : minY + (this.unitWidth + this.gutter) * rand
31667 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31671 if(box[0].size == 'xs'){
31674 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31679 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31680 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31688 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31693 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31694 y : minY + (this.unitWidth + this.gutter) * 2
31701 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31705 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31708 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31713 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31714 y : minY + (this.unitWidth + this.gutter) * 1
31718 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31719 y : minY + (this.unitWidth + this.gutter) * 2
31726 if(box[0].size == 'xs' && box[1].size == 'xs'){
31729 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31734 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31739 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31740 y : minY + (this.unitWidth + this.gutter) * 1
31748 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31753 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31754 y : minY + (this.unitWidth + this.gutter) * 2
31758 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31759 y : minY + (this.unitWidth + this.gutter) * 2
31766 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31770 if(box[0].size == 'xs'){
31773 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31778 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31783 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),
31788 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31789 y : minY + (this.unitWidth + this.gutter) * 1
31797 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31802 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31803 y : minY + (this.unitWidth + this.gutter) * 2
31807 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31808 y : minY + (this.unitWidth + this.gutter) * 2
31812 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),
31813 y : minY + (this.unitWidth + this.gutter) * 2
31821 * remove a Masonry Brick
31822 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31824 removeBrick : function(brick_id)
31830 for (var i = 0; i<this.bricks.length; i++) {
31831 if (this.bricks[i].id == brick_id) {
31832 this.bricks.splice(i,1);
31833 this.el.dom.removeChild(Roo.get(brick_id).dom);
31840 * adds a Masonry Brick
31841 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31843 addBrick : function(cfg)
31845 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31846 //this.register(cn);
31847 cn.parentId = this.id;
31848 cn.onRender(this.el, null);
31853 * register a Masonry Brick
31854 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31857 register : function(brick)
31859 this.bricks.push(brick);
31860 brick.masonryId = this.id;
31864 * clear all the Masonry Brick
31866 clearAll : function()
31869 //this.getChildContainer().dom.innerHTML = "";
31870 this.el.dom.innerHTML = '';
31873 getSelected : function()
31875 if (!this.selectedBrick) {
31879 return this.selectedBrick;
31883 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31887 * register a Masonry Layout
31888 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31891 register : function(layout)
31893 this.groups[layout.id] = layout;
31896 * fetch a Masonry Layout based on the masonry layout ID
31897 * @param {string} the masonry layout to add
31898 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31901 get: function(layout_id) {
31902 if (typeof(this.groups[layout_id]) == 'undefined') {
31905 return this.groups[layout_id] ;
31917 * http://masonry.desandro.com
31919 * The idea is to render all the bricks based on vertical width...
31921 * The original code extends 'outlayer' - we might need to use that....
31927 * @class Roo.bootstrap.LayoutMasonryAuto
31928 * @extends Roo.bootstrap.Component
31929 * Bootstrap Layout Masonry class
31932 * Create a new Element
31933 * @param {Object} config The config object
31936 Roo.bootstrap.LayoutMasonryAuto = function(config){
31937 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31940 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31943 * @cfg {Boolean} isFitWidth - resize the width..
31945 isFitWidth : false, // options..
31947 * @cfg {Boolean} isOriginLeft = left align?
31949 isOriginLeft : true,
31951 * @cfg {Boolean} isOriginTop = top align?
31953 isOriginTop : false,
31955 * @cfg {Boolean} isLayoutInstant = no animation?
31957 isLayoutInstant : false, // needed?
31959 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31961 isResizingContainer : true,
31963 * @cfg {Number} columnWidth width of the columns
31969 * @cfg {Number} maxCols maximum number of columns
31974 * @cfg {Number} padHeight padding below box..
31980 * @cfg {Boolean} isAutoInitial defalut true
31983 isAutoInitial : true,
31989 initialColumnWidth : 0,
31990 currentSize : null,
31992 colYs : null, // array.
31999 bricks: null, //CompositeElement
32000 cols : 0, // array?
32001 // element : null, // wrapped now this.el
32002 _isLayoutInited : null,
32005 getAutoCreate : function(){
32009 cls: 'blog-masonary-wrapper ' + this.cls,
32011 cls : 'mas-boxes masonary'
32018 getChildContainer: function( )
32020 if (this.boxesEl) {
32021 return this.boxesEl;
32024 this.boxesEl = this.el.select('.mas-boxes').first();
32026 return this.boxesEl;
32030 initEvents : function()
32034 if(this.isAutoInitial){
32035 Roo.log('hook children rendered');
32036 this.on('childrenrendered', function() {
32037 Roo.log('children rendered');
32044 initial : function()
32046 this.reloadItems();
32048 this.currentSize = this.el.getBox(true);
32050 /// was window resize... - let's see if this works..
32051 Roo.EventManager.onWindowResize(this.resize, this);
32053 if(!this.isAutoInitial){
32058 this.layout.defer(500,this);
32061 reloadItems: function()
32063 this.bricks = this.el.select('.masonry-brick', true);
32065 this.bricks.each(function(b) {
32066 //Roo.log(b.getSize());
32067 if (!b.attr('originalwidth')) {
32068 b.attr('originalwidth', b.getSize().width);
32073 Roo.log(this.bricks.elements.length);
32076 resize : function()
32079 var cs = this.el.getBox(true);
32081 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32082 Roo.log("no change in with or X");
32085 this.currentSize = cs;
32089 layout : function()
32092 this._resetLayout();
32093 //this._manageStamps();
32095 // don't animate first layout
32096 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32097 this.layoutItems( isInstant );
32099 // flag for initalized
32100 this._isLayoutInited = true;
32103 layoutItems : function( isInstant )
32105 //var items = this._getItemsForLayout( this.items );
32106 // original code supports filtering layout items.. we just ignore it..
32108 this._layoutItems( this.bricks , isInstant );
32110 this._postLayout();
32112 _layoutItems : function ( items , isInstant)
32114 //this.fireEvent( 'layout', this, items );
32117 if ( !items || !items.elements.length ) {
32118 // no items, emit event with empty array
32123 items.each(function(item) {
32124 Roo.log("layout item");
32126 // get x/y object from method
32127 var position = this._getItemLayoutPosition( item );
32129 position.item = item;
32130 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32131 queue.push( position );
32134 this._processLayoutQueue( queue );
32136 /** Sets position of item in DOM
32137 * @param {Element} item
32138 * @param {Number} x - horizontal position
32139 * @param {Number} y - vertical position
32140 * @param {Boolean} isInstant - disables transitions
32142 _processLayoutQueue : function( queue )
32144 for ( var i=0, len = queue.length; i < len; i++ ) {
32145 var obj = queue[i];
32146 obj.item.position('absolute');
32147 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32153 * Any logic you want to do after each layout,
32154 * i.e. size the container
32156 _postLayout : function()
32158 this.resizeContainer();
32161 resizeContainer : function()
32163 if ( !this.isResizingContainer ) {
32166 var size = this._getContainerSize();
32168 this.el.setSize(size.width,size.height);
32169 this.boxesEl.setSize(size.width,size.height);
32175 _resetLayout : function()
32177 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32178 this.colWidth = this.el.getWidth();
32179 //this.gutter = this.el.getWidth();
32181 this.measureColumns();
32187 this.colYs.push( 0 );
32193 measureColumns : function()
32195 this.getContainerWidth();
32196 // if columnWidth is 0, default to outerWidth of first item
32197 if ( !this.columnWidth ) {
32198 var firstItem = this.bricks.first();
32199 Roo.log(firstItem);
32200 this.columnWidth = this.containerWidth;
32201 if (firstItem && firstItem.attr('originalwidth') ) {
32202 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32204 // columnWidth fall back to item of first element
32205 Roo.log("set column width?");
32206 this.initialColumnWidth = this.columnWidth ;
32208 // if first elem has no width, default to size of container
32213 if (this.initialColumnWidth) {
32214 this.columnWidth = this.initialColumnWidth;
32219 // column width is fixed at the top - however if container width get's smaller we should
32222 // this bit calcs how man columns..
32224 var columnWidth = this.columnWidth += this.gutter;
32226 // calculate columns
32227 var containerWidth = this.containerWidth + this.gutter;
32229 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32230 // fix rounding errors, typically with gutters
32231 var excess = columnWidth - containerWidth % columnWidth;
32234 // if overshoot is less than a pixel, round up, otherwise floor it
32235 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32236 cols = Math[ mathMethod ]( cols );
32237 this.cols = Math.max( cols, 1 );
32238 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32240 // padding positioning..
32241 var totalColWidth = this.cols * this.columnWidth;
32242 var padavail = this.containerWidth - totalColWidth;
32243 // so for 2 columns - we need 3 'pads'
32245 var padNeeded = (1+this.cols) * this.padWidth;
32247 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32249 this.columnWidth += padExtra
32250 //this.padWidth = Math.floor(padavail / ( this.cols));
32252 // adjust colum width so that padding is fixed??
32254 // we have 3 columns ... total = width * 3
32255 // we have X left over... that should be used by
32257 //if (this.expandC) {
32265 getContainerWidth : function()
32267 /* // container is parent if fit width
32268 var container = this.isFitWidth ? this.element.parentNode : this.element;
32269 // check that this.size and size are there
32270 // IE8 triggers resize on body size change, so they might not be
32272 var size = getSize( container ); //FIXME
32273 this.containerWidth = size && size.innerWidth; //FIXME
32276 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32280 _getItemLayoutPosition : function( item ) // what is item?
32282 // we resize the item to our columnWidth..
32284 item.setWidth(this.columnWidth);
32285 item.autoBoxAdjust = false;
32287 var sz = item.getSize();
32289 // how many columns does this brick span
32290 var remainder = this.containerWidth % this.columnWidth;
32292 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32293 // round if off by 1 pixel, otherwise use ceil
32294 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32295 colSpan = Math.min( colSpan, this.cols );
32297 // normally this should be '1' as we dont' currently allow multi width columns..
32299 var colGroup = this._getColGroup( colSpan );
32300 // get the minimum Y value from the columns
32301 var minimumY = Math.min.apply( Math, colGroup );
32302 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32304 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32306 // position the brick
32308 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32309 y: this.currentSize.y + minimumY + this.padHeight
32313 // apply setHeight to necessary columns
32314 var setHeight = minimumY + sz.height + this.padHeight;
32315 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32317 var setSpan = this.cols + 1 - colGroup.length;
32318 for ( var i = 0; i < setSpan; i++ ) {
32319 this.colYs[ shortColIndex + i ] = setHeight ;
32326 * @param {Number} colSpan - number of columns the element spans
32327 * @returns {Array} colGroup
32329 _getColGroup : function( colSpan )
32331 if ( colSpan < 2 ) {
32332 // if brick spans only one column, use all the column Ys
32337 // how many different places could this brick fit horizontally
32338 var groupCount = this.cols + 1 - colSpan;
32339 // for each group potential horizontal position
32340 for ( var i = 0; i < groupCount; i++ ) {
32341 // make an array of colY values for that one group
32342 var groupColYs = this.colYs.slice( i, i + colSpan );
32343 // and get the max value of the array
32344 colGroup[i] = Math.max.apply( Math, groupColYs );
32349 _manageStamp : function( stamp )
32351 var stampSize = stamp.getSize();
32352 var offset = stamp.getBox();
32353 // get the columns that this stamp affects
32354 var firstX = this.isOriginLeft ? offset.x : offset.right;
32355 var lastX = firstX + stampSize.width;
32356 var firstCol = Math.floor( firstX / this.columnWidth );
32357 firstCol = Math.max( 0, firstCol );
32359 var lastCol = Math.floor( lastX / this.columnWidth );
32360 // lastCol should not go over if multiple of columnWidth #425
32361 lastCol -= lastX % this.columnWidth ? 0 : 1;
32362 lastCol = Math.min( this.cols - 1, lastCol );
32364 // set colYs to bottom of the stamp
32365 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32368 for ( var i = firstCol; i <= lastCol; i++ ) {
32369 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32374 _getContainerSize : function()
32376 this.maxY = Math.max.apply( Math, this.colYs );
32381 if ( this.isFitWidth ) {
32382 size.width = this._getContainerFitWidth();
32388 _getContainerFitWidth : function()
32390 var unusedCols = 0;
32391 // count unused columns
32394 if ( this.colYs[i] !== 0 ) {
32399 // fit container to columns that have been used
32400 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32403 needsResizeLayout : function()
32405 var previousWidth = this.containerWidth;
32406 this.getContainerWidth();
32407 return previousWidth !== this.containerWidth;
32422 * @class Roo.bootstrap.MasonryBrick
32423 * @extends Roo.bootstrap.Component
32424 * Bootstrap MasonryBrick class
32427 * Create a new MasonryBrick
32428 * @param {Object} config The config object
32431 Roo.bootstrap.MasonryBrick = function(config){
32433 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32435 Roo.bootstrap.MasonryBrick.register(this);
32441 * When a MasonryBrick is clcik
32442 * @param {Roo.bootstrap.MasonryBrick} this
32443 * @param {Roo.EventObject} e
32449 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32452 * @cfg {String} title
32456 * @cfg {String} html
32460 * @cfg {String} bgimage
32464 * @cfg {String} videourl
32468 * @cfg {String} cls
32472 * @cfg {String} href
32476 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32481 * @cfg {String} placetitle (center|bottom)
32486 * @cfg {Boolean} isFitContainer defalut true
32488 isFitContainer : true,
32491 * @cfg {Boolean} preventDefault defalut false
32493 preventDefault : false,
32496 * @cfg {Boolean} inverse defalut false
32498 maskInverse : false,
32500 getAutoCreate : function()
32502 if(!this.isFitContainer){
32503 return this.getSplitAutoCreate();
32506 var cls = 'masonry-brick masonry-brick-full';
32508 if(this.href.length){
32509 cls += ' masonry-brick-link';
32512 if(this.bgimage.length){
32513 cls += ' masonry-brick-image';
32516 if(this.maskInverse){
32517 cls += ' mask-inverse';
32520 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32521 cls += ' enable-mask';
32525 cls += ' masonry-' + this.size + '-brick';
32528 if(this.placetitle.length){
32530 switch (this.placetitle) {
32532 cls += ' masonry-center-title';
32535 cls += ' masonry-bottom-title';
32542 if(!this.html.length && !this.bgimage.length){
32543 cls += ' masonry-center-title';
32546 if(!this.html.length && this.bgimage.length){
32547 cls += ' masonry-bottom-title';
32552 cls += ' ' + this.cls;
32556 tag: (this.href.length) ? 'a' : 'div',
32561 cls: 'masonry-brick-mask'
32565 cls: 'masonry-brick-paragraph',
32571 if(this.href.length){
32572 cfg.href = this.href;
32575 var cn = cfg.cn[1].cn;
32577 if(this.title.length){
32580 cls: 'masonry-brick-title',
32585 if(this.html.length){
32588 cls: 'masonry-brick-text',
32593 if (!this.title.length && !this.html.length) {
32594 cfg.cn[1].cls += ' hide';
32597 if(this.bgimage.length){
32600 cls: 'masonry-brick-image-view',
32605 if(this.videourl.length){
32606 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32607 // youtube support only?
32610 cls: 'masonry-brick-image-view',
32613 allowfullscreen : true
32621 getSplitAutoCreate : function()
32623 var cls = 'masonry-brick masonry-brick-split';
32625 if(this.href.length){
32626 cls += ' masonry-brick-link';
32629 if(this.bgimage.length){
32630 cls += ' masonry-brick-image';
32634 cls += ' masonry-' + this.size + '-brick';
32637 switch (this.placetitle) {
32639 cls += ' masonry-center-title';
32642 cls += ' masonry-bottom-title';
32645 if(!this.bgimage.length){
32646 cls += ' masonry-center-title';
32649 if(this.bgimage.length){
32650 cls += ' masonry-bottom-title';
32656 cls += ' ' + this.cls;
32660 tag: (this.href.length) ? 'a' : 'div',
32665 cls: 'masonry-brick-split-head',
32669 cls: 'masonry-brick-paragraph',
32676 cls: 'masonry-brick-split-body',
32682 if(this.href.length){
32683 cfg.href = this.href;
32686 if(this.title.length){
32687 cfg.cn[0].cn[0].cn.push({
32689 cls: 'masonry-brick-title',
32694 if(this.html.length){
32695 cfg.cn[1].cn.push({
32697 cls: 'masonry-brick-text',
32702 if(this.bgimage.length){
32703 cfg.cn[0].cn.push({
32705 cls: 'masonry-brick-image-view',
32710 if(this.videourl.length){
32711 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32712 // youtube support only?
32713 cfg.cn[0].cn.cn.push({
32715 cls: 'masonry-brick-image-view',
32718 allowfullscreen : true
32725 initEvents: function()
32727 switch (this.size) {
32760 this.el.on('touchstart', this.onTouchStart, this);
32761 this.el.on('touchmove', this.onTouchMove, this);
32762 this.el.on('touchend', this.onTouchEnd, this);
32763 this.el.on('contextmenu', this.onContextMenu, this);
32765 this.el.on('mouseenter' ,this.enter, this);
32766 this.el.on('mouseleave', this.leave, this);
32767 this.el.on('click', this.onClick, this);
32770 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32771 this.parent().bricks.push(this);
32776 onClick: function(e, el)
32778 var time = this.endTimer - this.startTimer;
32779 // Roo.log(e.preventDefault());
32782 e.preventDefault();
32787 if(!this.preventDefault){
32791 e.preventDefault();
32793 if (this.activeClass != '') {
32794 this.selectBrick();
32797 this.fireEvent('click', this, e);
32800 enter: function(e, el)
32802 e.preventDefault();
32804 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32808 if(this.bgimage.length && this.html.length){
32809 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32813 leave: function(e, el)
32815 e.preventDefault();
32817 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32821 if(this.bgimage.length && this.html.length){
32822 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32826 onTouchStart: function(e, el)
32828 // e.preventDefault();
32830 this.touchmoved = false;
32832 if(!this.isFitContainer){
32836 if(!this.bgimage.length || !this.html.length){
32840 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32842 this.timer = new Date().getTime();
32846 onTouchMove: function(e, el)
32848 this.touchmoved = true;
32851 onContextMenu : function(e,el)
32853 e.preventDefault();
32854 e.stopPropagation();
32858 onTouchEnd: function(e, el)
32860 // e.preventDefault();
32862 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32869 if(!this.bgimage.length || !this.html.length){
32871 if(this.href.length){
32872 window.location.href = this.href;
32878 if(!this.isFitContainer){
32882 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32884 window.location.href = this.href;
32887 //selection on single brick only
32888 selectBrick : function() {
32890 if (!this.parentId) {
32894 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32895 var index = m.selectedBrick.indexOf(this.id);
32898 m.selectedBrick.splice(index,1);
32899 this.el.removeClass(this.activeClass);
32903 for(var i = 0; i < m.selectedBrick.length; i++) {
32904 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32905 b.el.removeClass(b.activeClass);
32908 m.selectedBrick = [];
32910 m.selectedBrick.push(this.id);
32911 this.el.addClass(this.activeClass);
32915 isSelected : function(){
32916 return this.el.hasClass(this.activeClass);
32921 Roo.apply(Roo.bootstrap.MasonryBrick, {
32924 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32926 * register a Masonry Brick
32927 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32930 register : function(brick)
32932 //this.groups[brick.id] = brick;
32933 this.groups.add(brick.id, brick);
32936 * fetch a masonry brick based on the masonry brick ID
32937 * @param {string} the masonry brick to add
32938 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32941 get: function(brick_id)
32943 // if (typeof(this.groups[brick_id]) == 'undefined') {
32946 // return this.groups[brick_id] ;
32948 if(this.groups.key(brick_id)) {
32949 return this.groups.key(brick_id);
32967 * @class Roo.bootstrap.Brick
32968 * @extends Roo.bootstrap.Component
32969 * Bootstrap Brick class
32972 * Create a new Brick
32973 * @param {Object} config The config object
32976 Roo.bootstrap.Brick = function(config){
32977 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32983 * When a Brick is click
32984 * @param {Roo.bootstrap.Brick} this
32985 * @param {Roo.EventObject} e
32991 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32994 * @cfg {String} title
32998 * @cfg {String} html
33002 * @cfg {String} bgimage
33006 * @cfg {String} cls
33010 * @cfg {String} href
33014 * @cfg {String} video
33018 * @cfg {Boolean} square
33022 getAutoCreate : function()
33024 var cls = 'roo-brick';
33026 if(this.href.length){
33027 cls += ' roo-brick-link';
33030 if(this.bgimage.length){
33031 cls += ' roo-brick-image';
33034 if(!this.html.length && !this.bgimage.length){
33035 cls += ' roo-brick-center-title';
33038 if(!this.html.length && this.bgimage.length){
33039 cls += ' roo-brick-bottom-title';
33043 cls += ' ' + this.cls;
33047 tag: (this.href.length) ? 'a' : 'div',
33052 cls: 'roo-brick-paragraph',
33058 if(this.href.length){
33059 cfg.href = this.href;
33062 var cn = cfg.cn[0].cn;
33064 if(this.title.length){
33067 cls: 'roo-brick-title',
33072 if(this.html.length){
33075 cls: 'roo-brick-text',
33082 if(this.bgimage.length){
33085 cls: 'roo-brick-image-view',
33093 initEvents: function()
33095 if(this.title.length || this.html.length){
33096 this.el.on('mouseenter' ,this.enter, this);
33097 this.el.on('mouseleave', this.leave, this);
33100 Roo.EventManager.onWindowResize(this.resize, this);
33102 if(this.bgimage.length){
33103 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33104 this.imageEl.on('load', this.onImageLoad, this);
33111 onImageLoad : function()
33116 resize : function()
33118 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33120 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33122 if(this.bgimage.length){
33123 var image = this.el.select('.roo-brick-image-view', true).first();
33125 image.setWidth(paragraph.getWidth());
33128 image.setHeight(paragraph.getWidth());
33131 this.el.setHeight(image.getHeight());
33132 paragraph.setHeight(image.getHeight());
33138 enter: function(e, el)
33140 e.preventDefault();
33142 if(this.bgimage.length){
33143 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33144 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33148 leave: function(e, el)
33150 e.preventDefault();
33152 if(this.bgimage.length){
33153 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33154 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33169 * @class Roo.bootstrap.NumberField
33170 * @extends Roo.bootstrap.Input
33171 * Bootstrap NumberField class
33177 * Create a new NumberField
33178 * @param {Object} config The config object
33181 Roo.bootstrap.NumberField = function(config){
33182 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33185 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33188 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33190 allowDecimals : true,
33192 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33194 decimalSeparator : ".",
33196 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33198 decimalPrecision : 2,
33200 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33202 allowNegative : true,
33205 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33209 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33211 minValue : Number.NEGATIVE_INFINITY,
33213 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33215 maxValue : Number.MAX_VALUE,
33217 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33219 minText : "The minimum value for this field is {0}",
33221 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33223 maxText : "The maximum value for this field is {0}",
33225 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33226 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33228 nanText : "{0} is not a valid number",
33230 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33232 thousandsDelimiter : false,
33234 * @cfg {String} valueAlign alignment of value
33236 valueAlign : "left",
33238 getAutoCreate : function()
33240 var hiddenInput = {
33244 cls: 'hidden-number-input'
33248 hiddenInput.name = this.name;
33253 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33255 this.name = hiddenInput.name;
33257 if(cfg.cn.length > 0) {
33258 cfg.cn.push(hiddenInput);
33265 initEvents : function()
33267 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33269 var allowed = "0123456789";
33271 if(this.allowDecimals){
33272 allowed += this.decimalSeparator;
33275 if(this.allowNegative){
33279 if(this.thousandsDelimiter) {
33283 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33285 var keyPress = function(e){
33287 var k = e.getKey();
33289 var c = e.getCharCode();
33292 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33293 allowed.indexOf(String.fromCharCode(c)) === -1
33299 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33303 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33308 this.el.on("keypress", keyPress, this);
33311 validateValue : function(value)
33314 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33318 var num = this.parseValue(value);
33321 this.markInvalid(String.format(this.nanText, value));
33325 if(num < this.minValue){
33326 this.markInvalid(String.format(this.minText, this.minValue));
33330 if(num > this.maxValue){
33331 this.markInvalid(String.format(this.maxText, this.maxValue));
33338 getValue : function()
33340 var v = this.hiddenEl().getValue();
33342 return this.fixPrecision(this.parseValue(v));
33345 parseValue : function(value)
33347 if(this.thousandsDelimiter) {
33349 r = new RegExp(",", "g");
33350 value = value.replace(r, "");
33353 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33354 return isNaN(value) ? '' : value;
33357 fixPrecision : function(value)
33359 if(this.thousandsDelimiter) {
33361 r = new RegExp(",", "g");
33362 value = value.replace(r, "");
33365 var nan = isNaN(value);
33367 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33368 return nan ? '' : value;
33370 return parseFloat(value).toFixed(this.decimalPrecision);
33373 setValue : function(v)
33375 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33381 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33383 this.inputEl().dom.value = (v == '') ? '' :
33384 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33386 if(!this.allowZero && v === '0') {
33387 this.hiddenEl().dom.value = '';
33388 this.inputEl().dom.value = '';
33395 decimalPrecisionFcn : function(v)
33397 return Math.floor(v);
33400 beforeBlur : function()
33402 var v = this.parseValue(this.getRawValue());
33404 if(v || v === 0 || v === ''){
33409 hiddenEl : function()
33411 return this.el.select('input.hidden-number-input',true).first();
33423 * @class Roo.bootstrap.DocumentSlider
33424 * @extends Roo.bootstrap.Component
33425 * Bootstrap DocumentSlider class
33428 * Create a new DocumentViewer
33429 * @param {Object} config The config object
33432 Roo.bootstrap.DocumentSlider = function(config){
33433 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33440 * Fire after initEvent
33441 * @param {Roo.bootstrap.DocumentSlider} this
33446 * Fire after update
33447 * @param {Roo.bootstrap.DocumentSlider} this
33453 * @param {Roo.bootstrap.DocumentSlider} this
33459 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33465 getAutoCreate : function()
33469 cls : 'roo-document-slider',
33473 cls : 'roo-document-slider-header',
33477 cls : 'roo-document-slider-header-title'
33483 cls : 'roo-document-slider-body',
33487 cls : 'roo-document-slider-prev',
33491 cls : 'fa fa-chevron-left'
33497 cls : 'roo-document-slider-thumb',
33501 cls : 'roo-document-slider-image'
33507 cls : 'roo-document-slider-next',
33511 cls : 'fa fa-chevron-right'
33523 initEvents : function()
33525 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33526 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33528 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33529 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33531 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33532 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33534 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33535 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33537 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33538 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33540 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33541 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33543 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33544 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33546 this.thumbEl.on('click', this.onClick, this);
33548 this.prevIndicator.on('click', this.prev, this);
33550 this.nextIndicator.on('click', this.next, this);
33554 initial : function()
33556 if(this.files.length){
33557 this.indicator = 1;
33561 this.fireEvent('initial', this);
33564 update : function()
33566 this.imageEl.attr('src', this.files[this.indicator - 1]);
33568 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33570 this.prevIndicator.show();
33572 if(this.indicator == 1){
33573 this.prevIndicator.hide();
33576 this.nextIndicator.show();
33578 if(this.indicator == this.files.length){
33579 this.nextIndicator.hide();
33582 this.thumbEl.scrollTo('top');
33584 this.fireEvent('update', this);
33587 onClick : function(e)
33589 e.preventDefault();
33591 this.fireEvent('click', this);
33596 e.preventDefault();
33598 this.indicator = Math.max(1, this.indicator - 1);
33605 e.preventDefault();
33607 this.indicator = Math.min(this.files.length, this.indicator + 1);
33621 * @class Roo.bootstrap.RadioSet
33622 * @extends Roo.bootstrap.Input
33623 * Bootstrap RadioSet class
33624 * @cfg {String} indicatorpos (left|right) default left
33625 * @cfg {Boolean} inline (true|false) inline the element (default true)
33626 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33628 * Create a new RadioSet
33629 * @param {Object} config The config object
33632 Roo.bootstrap.RadioSet = function(config){
33634 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33638 Roo.bootstrap.RadioSet.register(this);
33643 * Fires when the element is checked or unchecked.
33644 * @param {Roo.bootstrap.RadioSet} this This radio
33645 * @param {Roo.bootstrap.Radio} item The checked item
33650 * Fires when the element is click.
33651 * @param {Roo.bootstrap.RadioSet} this This radio set
33652 * @param {Roo.bootstrap.Radio} item The checked item
33653 * @param {Roo.EventObject} e The event object
33660 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33668 indicatorpos : 'left',
33670 getAutoCreate : function()
33674 cls : 'roo-radio-set-label',
33678 html : this.fieldLabel
33683 if(this.indicatorpos == 'left'){
33686 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33687 tooltip : 'This field is required'
33692 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33693 tooltip : 'This field is required'
33699 cls : 'roo-radio-set-items'
33702 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33704 if (align === 'left' && this.fieldLabel.length) {
33707 cls : "roo-radio-set-right",
33713 if(this.labelWidth > 12){
33714 label.style = "width: " + this.labelWidth + 'px';
33717 if(this.labelWidth < 13 && this.labelmd == 0){
33718 this.labelmd = this.labelWidth;
33721 if(this.labellg > 0){
33722 label.cls += ' col-lg-' + this.labellg;
33723 items.cls += ' col-lg-' + (12 - this.labellg);
33726 if(this.labelmd > 0){
33727 label.cls += ' col-md-' + this.labelmd;
33728 items.cls += ' col-md-' + (12 - this.labelmd);
33731 if(this.labelsm > 0){
33732 label.cls += ' col-sm-' + this.labelsm;
33733 items.cls += ' col-sm-' + (12 - this.labelsm);
33736 if(this.labelxs > 0){
33737 label.cls += ' col-xs-' + this.labelxs;
33738 items.cls += ' col-xs-' + (12 - this.labelxs);
33744 cls : 'roo-radio-set',
33748 cls : 'roo-radio-set-input',
33751 value : this.value ? this.value : ''
33758 if(this.weight.length){
33759 cfg.cls += ' roo-radio-' + this.weight;
33763 cfg.cls += ' roo-radio-set-inline';
33767 ['xs','sm','md','lg'].map(function(size){
33768 if (settings[size]) {
33769 cfg.cls += ' col-' + size + '-' + settings[size];
33777 initEvents : function()
33779 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33780 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33782 if(!this.fieldLabel.length){
33783 this.labelEl.hide();
33786 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33787 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33789 this.indicator = this.indicatorEl();
33791 if(this.indicator){
33792 this.indicator.addClass('invisible');
33795 this.originalValue = this.getValue();
33799 inputEl: function ()
33801 return this.el.select('.roo-radio-set-input', true).first();
33804 getChildContainer : function()
33806 return this.itemsEl;
33809 register : function(item)
33811 this.radioes.push(item);
33815 validate : function()
33817 if(this.getVisibilityEl().hasClass('hidden')){
33823 Roo.each(this.radioes, function(i){
33832 if(this.allowBlank) {
33836 if(this.disabled || valid){
33841 this.markInvalid();
33846 markValid : function()
33848 if(this.labelEl.isVisible(true)){
33849 this.indicatorEl().removeClass('visible');
33850 this.indicatorEl().addClass('invisible');
33853 this.el.removeClass([this.invalidClass, this.validClass]);
33854 this.el.addClass(this.validClass);
33856 this.fireEvent('valid', this);
33859 markInvalid : function(msg)
33861 if(this.allowBlank || this.disabled){
33865 if(this.labelEl.isVisible(true)){
33866 this.indicatorEl().removeClass('invisible');
33867 this.indicatorEl().addClass('visible');
33870 this.el.removeClass([this.invalidClass, this.validClass]);
33871 this.el.addClass(this.invalidClass);
33873 this.fireEvent('invalid', this, msg);
33877 setValue : function(v, suppressEvent)
33879 if(this.value === v){
33886 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33889 Roo.each(this.radioes, function(i){
33891 i.el.removeClass('checked');
33894 Roo.each(this.radioes, function(i){
33896 if(i.value === v || i.value.toString() === v.toString()){
33898 i.el.addClass('checked');
33900 if(suppressEvent !== true){
33901 this.fireEvent('check', this, i);
33912 clearInvalid : function(){
33914 if(!this.el || this.preventMark){
33918 this.el.removeClass([this.invalidClass]);
33920 this.fireEvent('valid', this);
33925 Roo.apply(Roo.bootstrap.RadioSet, {
33929 register : function(set)
33931 this.groups[set.name] = set;
33934 get: function(name)
33936 if (typeof(this.groups[name]) == 'undefined') {
33940 return this.groups[name] ;
33946 * Ext JS Library 1.1.1
33947 * Copyright(c) 2006-2007, Ext JS, LLC.
33949 * Originally Released Under LGPL - original licence link has changed is not relivant.
33952 * <script type="text/javascript">
33957 * @class Roo.bootstrap.SplitBar
33958 * @extends Roo.util.Observable
33959 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33963 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33964 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33965 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33966 split.minSize = 100;
33967 split.maxSize = 600;
33968 split.animate = true;
33969 split.on('moved', splitterMoved);
33972 * Create a new SplitBar
33973 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33974 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33975 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33976 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33977 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33978 position of the SplitBar).
33980 Roo.bootstrap.SplitBar = function(cfg){
33985 // dragElement : elm
33986 // resizingElement: el,
33988 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33989 // placement : Roo.bootstrap.SplitBar.LEFT ,
33990 // existingProxy ???
33993 this.el = Roo.get(cfg.dragElement, true);
33994 this.el.dom.unselectable = "on";
33996 this.resizingEl = Roo.get(cfg.resizingElement, true);
34000 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34001 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34004 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34007 * The minimum size of the resizing element. (Defaults to 0)
34013 * The maximum size of the resizing element. (Defaults to 2000)
34016 this.maxSize = 2000;
34019 * Whether to animate the transition to the new size
34022 this.animate = false;
34025 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34028 this.useShim = false;
34033 if(!cfg.existingProxy){
34035 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34037 this.proxy = Roo.get(cfg.existingProxy).dom;
34040 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34043 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34046 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34049 this.dragSpecs = {};
34052 * @private The adapter to use to positon and resize elements
34054 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34055 this.adapter.init(this);
34057 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34059 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34060 this.el.addClass("roo-splitbar-h");
34063 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34064 this.el.addClass("roo-splitbar-v");
34070 * Fires when the splitter is moved (alias for {@link #event-moved})
34071 * @param {Roo.bootstrap.SplitBar} this
34072 * @param {Number} newSize the new width or height
34077 * Fires when the splitter is moved
34078 * @param {Roo.bootstrap.SplitBar} this
34079 * @param {Number} newSize the new width or height
34083 * @event beforeresize
34084 * Fires before the splitter is dragged
34085 * @param {Roo.bootstrap.SplitBar} this
34087 "beforeresize" : true,
34089 "beforeapply" : true
34092 Roo.util.Observable.call(this);
34095 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34096 onStartProxyDrag : function(x, y){
34097 this.fireEvent("beforeresize", this);
34099 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34101 o.enableDisplayMode("block");
34102 // all splitbars share the same overlay
34103 Roo.bootstrap.SplitBar.prototype.overlay = o;
34105 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34106 this.overlay.show();
34107 Roo.get(this.proxy).setDisplayed("block");
34108 var size = this.adapter.getElementSize(this);
34109 this.activeMinSize = this.getMinimumSize();;
34110 this.activeMaxSize = this.getMaximumSize();;
34111 var c1 = size - this.activeMinSize;
34112 var c2 = Math.max(this.activeMaxSize - size, 0);
34113 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34114 this.dd.resetConstraints();
34115 this.dd.setXConstraint(
34116 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34117 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34119 this.dd.setYConstraint(0, 0);
34121 this.dd.resetConstraints();
34122 this.dd.setXConstraint(0, 0);
34123 this.dd.setYConstraint(
34124 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34125 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34128 this.dragSpecs.startSize = size;
34129 this.dragSpecs.startPoint = [x, y];
34130 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34134 * @private Called after the drag operation by the DDProxy
34136 onEndProxyDrag : function(e){
34137 Roo.get(this.proxy).setDisplayed(false);
34138 var endPoint = Roo.lib.Event.getXY(e);
34140 this.overlay.hide();
34143 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34144 newSize = this.dragSpecs.startSize +
34145 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34146 endPoint[0] - this.dragSpecs.startPoint[0] :
34147 this.dragSpecs.startPoint[0] - endPoint[0]
34150 newSize = this.dragSpecs.startSize +
34151 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34152 endPoint[1] - this.dragSpecs.startPoint[1] :
34153 this.dragSpecs.startPoint[1] - endPoint[1]
34156 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34157 if(newSize != this.dragSpecs.startSize){
34158 if(this.fireEvent('beforeapply', this, newSize) !== false){
34159 this.adapter.setElementSize(this, newSize);
34160 this.fireEvent("moved", this, newSize);
34161 this.fireEvent("resize", this, newSize);
34167 * Get the adapter this SplitBar uses
34168 * @return The adapter object
34170 getAdapter : function(){
34171 return this.adapter;
34175 * Set the adapter this SplitBar uses
34176 * @param {Object} adapter A SplitBar adapter object
34178 setAdapter : function(adapter){
34179 this.adapter = adapter;
34180 this.adapter.init(this);
34184 * Gets the minimum size for the resizing element
34185 * @return {Number} The minimum size
34187 getMinimumSize : function(){
34188 return this.minSize;
34192 * Sets the minimum size for the resizing element
34193 * @param {Number} minSize The minimum size
34195 setMinimumSize : function(minSize){
34196 this.minSize = minSize;
34200 * Gets the maximum size for the resizing element
34201 * @return {Number} The maximum size
34203 getMaximumSize : function(){
34204 return this.maxSize;
34208 * Sets the maximum size for the resizing element
34209 * @param {Number} maxSize The maximum size
34211 setMaximumSize : function(maxSize){
34212 this.maxSize = maxSize;
34216 * Sets the initialize size for the resizing element
34217 * @param {Number} size The initial size
34219 setCurrentSize : function(size){
34220 var oldAnimate = this.animate;
34221 this.animate = false;
34222 this.adapter.setElementSize(this, size);
34223 this.animate = oldAnimate;
34227 * Destroy this splitbar.
34228 * @param {Boolean} removeEl True to remove the element
34230 destroy : function(removeEl){
34232 this.shim.remove();
34235 this.proxy.parentNode.removeChild(this.proxy);
34243 * @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.
34245 Roo.bootstrap.SplitBar.createProxy = function(dir){
34246 var proxy = new Roo.Element(document.createElement("div"));
34247 proxy.unselectable();
34248 var cls = 'roo-splitbar-proxy';
34249 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34250 document.body.appendChild(proxy.dom);
34255 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34256 * Default Adapter. It assumes the splitter and resizing element are not positioned
34257 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34259 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34262 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34263 // do nothing for now
34264 init : function(s){
34268 * Called before drag operations to get the current size of the resizing element.
34269 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34271 getElementSize : function(s){
34272 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34273 return s.resizingEl.getWidth();
34275 return s.resizingEl.getHeight();
34280 * Called after drag operations to set the size of the resizing element.
34281 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34282 * @param {Number} newSize The new size to set
34283 * @param {Function} onComplete A function to be invoked when resizing is complete
34285 setElementSize : function(s, newSize, onComplete){
34286 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34288 s.resizingEl.setWidth(newSize);
34290 onComplete(s, newSize);
34293 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34298 s.resizingEl.setHeight(newSize);
34300 onComplete(s, newSize);
34303 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34310 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34311 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34312 * Adapter that moves the splitter element to align with the resized sizing element.
34313 * Used with an absolute positioned SplitBar.
34314 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34315 * document.body, make sure you assign an id to the body element.
34317 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34318 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34319 this.container = Roo.get(container);
34322 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34323 init : function(s){
34324 this.basic.init(s);
34327 getElementSize : function(s){
34328 return this.basic.getElementSize(s);
34331 setElementSize : function(s, newSize, onComplete){
34332 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34335 moveSplitter : function(s){
34336 var yes = Roo.bootstrap.SplitBar;
34337 switch(s.placement){
34339 s.el.setX(s.resizingEl.getRight());
34342 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34345 s.el.setY(s.resizingEl.getBottom());
34348 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34355 * Orientation constant - Create a vertical SplitBar
34359 Roo.bootstrap.SplitBar.VERTICAL = 1;
34362 * Orientation constant - Create a horizontal SplitBar
34366 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34369 * Placement constant - The resizing element is to the left of the splitter element
34373 Roo.bootstrap.SplitBar.LEFT = 1;
34376 * Placement constant - The resizing element is to the right of the splitter element
34380 Roo.bootstrap.SplitBar.RIGHT = 2;
34383 * Placement constant - The resizing element is positioned above the splitter element
34387 Roo.bootstrap.SplitBar.TOP = 3;
34390 * Placement constant - The resizing element is positioned under splitter element
34394 Roo.bootstrap.SplitBar.BOTTOM = 4;
34395 Roo.namespace("Roo.bootstrap.layout");/*
34397 * Ext JS Library 1.1.1
34398 * Copyright(c) 2006-2007, Ext JS, LLC.
34400 * Originally Released Under LGPL - original licence link has changed is not relivant.
34403 * <script type="text/javascript">
34407 * @class Roo.bootstrap.layout.Manager
34408 * @extends Roo.bootstrap.Component
34409 * Base class for layout managers.
34411 Roo.bootstrap.layout.Manager = function(config)
34413 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34419 /** false to disable window resize monitoring @type Boolean */
34420 this.monitorWindowResize = true;
34425 * Fires when a layout is performed.
34426 * @param {Roo.LayoutManager} this
34430 * @event regionresized
34431 * Fires when the user resizes a region.
34432 * @param {Roo.LayoutRegion} region The resized region
34433 * @param {Number} newSize The new size (width for east/west, height for north/south)
34435 "regionresized" : true,
34437 * @event regioncollapsed
34438 * Fires when a region is collapsed.
34439 * @param {Roo.LayoutRegion} region The collapsed region
34441 "regioncollapsed" : true,
34443 * @event regionexpanded
34444 * Fires when a region is expanded.
34445 * @param {Roo.LayoutRegion} region The expanded region
34447 "regionexpanded" : true
34449 this.updating = false;
34452 this.el = Roo.get(config.el);
34458 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34463 monitorWindowResize : true,
34469 onRender : function(ct, position)
34472 this.el = Roo.get(ct);
34475 //this.fireEvent('render',this);
34479 initEvents: function()
34483 // ie scrollbar fix
34484 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34485 document.body.scroll = "no";
34486 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34487 this.el.position('relative');
34489 this.id = this.el.id;
34490 this.el.addClass("roo-layout-container");
34491 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34492 if(this.el.dom != document.body ) {
34493 this.el.on('resize', this.layout,this);
34494 this.el.on('show', this.layout,this);
34500 * Returns true if this layout is currently being updated
34501 * @return {Boolean}
34503 isUpdating : function(){
34504 return this.updating;
34508 * Suspend the LayoutManager from doing auto-layouts while
34509 * making multiple add or remove calls
34511 beginUpdate : function(){
34512 this.updating = true;
34516 * Restore auto-layouts and optionally disable the manager from performing a layout
34517 * @param {Boolean} noLayout true to disable a layout update
34519 endUpdate : function(noLayout){
34520 this.updating = false;
34526 layout: function(){
34530 onRegionResized : function(region, newSize){
34531 this.fireEvent("regionresized", region, newSize);
34535 onRegionCollapsed : function(region){
34536 this.fireEvent("regioncollapsed", region);
34539 onRegionExpanded : function(region){
34540 this.fireEvent("regionexpanded", region);
34544 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34545 * performs box-model adjustments.
34546 * @return {Object} The size as an object {width: (the width), height: (the height)}
34548 getViewSize : function()
34551 if(this.el.dom != document.body){
34552 size = this.el.getSize();
34554 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34556 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34557 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34562 * Returns the Element this layout is bound to.
34563 * @return {Roo.Element}
34565 getEl : function(){
34570 * Returns the specified region.
34571 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34572 * @return {Roo.LayoutRegion}
34574 getRegion : function(target){
34575 return this.regions[target.toLowerCase()];
34578 onWindowResize : function(){
34579 if(this.monitorWindowResize){
34586 * Ext JS Library 1.1.1
34587 * Copyright(c) 2006-2007, Ext JS, LLC.
34589 * Originally Released Under LGPL - original licence link has changed is not relivant.
34592 * <script type="text/javascript">
34595 * @class Roo.bootstrap.layout.Border
34596 * @extends Roo.bootstrap.layout.Manager
34597 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34598 * please see: examples/bootstrap/nested.html<br><br>
34600 <b>The container the layout is rendered into can be either the body element or any other element.
34601 If it is not the body element, the container needs to either be an absolute positioned element,
34602 or you will need to add "position:relative" to the css of the container. You will also need to specify
34603 the container size if it is not the body element.</b>
34606 * Create a new Border
34607 * @param {Object} config Configuration options
34609 Roo.bootstrap.layout.Border = function(config){
34610 config = config || {};
34611 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34615 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34616 if(config[region]){
34617 config[region].region = region;
34618 this.addRegion(config[region]);
34624 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34626 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34628 * Creates and adds a new region if it doesn't already exist.
34629 * @param {String} target The target region key (north, south, east, west or center).
34630 * @param {Object} config The regions config object
34631 * @return {BorderLayoutRegion} The new region
34633 addRegion : function(config)
34635 if(!this.regions[config.region]){
34636 var r = this.factory(config);
34637 this.bindRegion(r);
34639 return this.regions[config.region];
34643 bindRegion : function(r){
34644 this.regions[r.config.region] = r;
34646 r.on("visibilitychange", this.layout, this);
34647 r.on("paneladded", this.layout, this);
34648 r.on("panelremoved", this.layout, this);
34649 r.on("invalidated", this.layout, this);
34650 r.on("resized", this.onRegionResized, this);
34651 r.on("collapsed", this.onRegionCollapsed, this);
34652 r.on("expanded", this.onRegionExpanded, this);
34656 * Performs a layout update.
34658 layout : function()
34660 if(this.updating) {
34664 // render all the rebions if they have not been done alreayd?
34665 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34666 if(this.regions[region] && !this.regions[region].bodyEl){
34667 this.regions[region].onRender(this.el)
34671 var size = this.getViewSize();
34672 var w = size.width;
34673 var h = size.height;
34678 //var x = 0, y = 0;
34680 var rs = this.regions;
34681 var north = rs["north"];
34682 var south = rs["south"];
34683 var west = rs["west"];
34684 var east = rs["east"];
34685 var center = rs["center"];
34686 //if(this.hideOnLayout){ // not supported anymore
34687 //c.el.setStyle("display", "none");
34689 if(north && north.isVisible()){
34690 var b = north.getBox();
34691 var m = north.getMargins();
34692 b.width = w - (m.left+m.right);
34695 centerY = b.height + b.y + m.bottom;
34696 centerH -= centerY;
34697 north.updateBox(this.safeBox(b));
34699 if(south && south.isVisible()){
34700 var b = south.getBox();
34701 var m = south.getMargins();
34702 b.width = w - (m.left+m.right);
34704 var totalHeight = (b.height + m.top + m.bottom);
34705 b.y = h - totalHeight + m.top;
34706 centerH -= totalHeight;
34707 south.updateBox(this.safeBox(b));
34709 if(west && west.isVisible()){
34710 var b = west.getBox();
34711 var m = west.getMargins();
34712 b.height = centerH - (m.top+m.bottom);
34714 b.y = centerY + m.top;
34715 var totalWidth = (b.width + m.left + m.right);
34716 centerX += totalWidth;
34717 centerW -= totalWidth;
34718 west.updateBox(this.safeBox(b));
34720 if(east && east.isVisible()){
34721 var b = east.getBox();
34722 var m = east.getMargins();
34723 b.height = centerH - (m.top+m.bottom);
34724 var totalWidth = (b.width + m.left + m.right);
34725 b.x = w - totalWidth + m.left;
34726 b.y = centerY + m.top;
34727 centerW -= totalWidth;
34728 east.updateBox(this.safeBox(b));
34731 var m = center.getMargins();
34733 x: centerX + m.left,
34734 y: centerY + m.top,
34735 width: centerW - (m.left+m.right),
34736 height: centerH - (m.top+m.bottom)
34738 //if(this.hideOnLayout){
34739 //center.el.setStyle("display", "block");
34741 center.updateBox(this.safeBox(centerBox));
34744 this.fireEvent("layout", this);
34748 safeBox : function(box){
34749 box.width = Math.max(0, box.width);
34750 box.height = Math.max(0, box.height);
34755 * Adds a ContentPanel (or subclass) to this layout.
34756 * @param {String} target The target region key (north, south, east, west or center).
34757 * @param {Roo.ContentPanel} panel The panel to add
34758 * @return {Roo.ContentPanel} The added panel
34760 add : function(target, panel){
34762 target = target.toLowerCase();
34763 return this.regions[target].add(panel);
34767 * Remove a ContentPanel (or subclass) to this layout.
34768 * @param {String} target The target region key (north, south, east, west or center).
34769 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34770 * @return {Roo.ContentPanel} The removed panel
34772 remove : function(target, panel){
34773 target = target.toLowerCase();
34774 return this.regions[target].remove(panel);
34778 * Searches all regions for a panel with the specified id
34779 * @param {String} panelId
34780 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34782 findPanel : function(panelId){
34783 var rs = this.regions;
34784 for(var target in rs){
34785 if(typeof rs[target] != "function"){
34786 var p = rs[target].getPanel(panelId);
34796 * Searches all regions for a panel with the specified id and activates (shows) it.
34797 * @param {String/ContentPanel} panelId The panels id or the panel itself
34798 * @return {Roo.ContentPanel} The shown panel or null
34800 showPanel : function(panelId) {
34801 var rs = this.regions;
34802 for(var target in rs){
34803 var r = rs[target];
34804 if(typeof r != "function"){
34805 if(r.hasPanel(panelId)){
34806 return r.showPanel(panelId);
34814 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34815 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34818 restoreState : function(provider){
34820 provider = Roo.state.Manager;
34822 var sm = new Roo.LayoutStateManager();
34823 sm.init(this, provider);
34829 * Adds a xtype elements to the layout.
34833 xtype : 'ContentPanel',
34840 xtype : 'NestedLayoutPanel',
34846 items : [ ... list of content panels or nested layout panels.. ]
34850 * @param {Object} cfg Xtype definition of item to add.
34852 addxtype : function(cfg)
34854 // basically accepts a pannel...
34855 // can accept a layout region..!?!?
34856 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34859 // theory? children can only be panels??
34861 //if (!cfg.xtype.match(/Panel$/)) {
34866 if (typeof(cfg.region) == 'undefined') {
34867 Roo.log("Failed to add Panel, region was not set");
34871 var region = cfg.region;
34877 xitems = cfg.items;
34884 case 'Content': // ContentPanel (el, cfg)
34885 case 'Scroll': // ContentPanel (el, cfg)
34887 cfg.autoCreate = true;
34888 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34890 // var el = this.el.createChild();
34891 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34894 this.add(region, ret);
34898 case 'TreePanel': // our new panel!
34899 cfg.el = this.el.createChild();
34900 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34901 this.add(region, ret);
34906 // create a new Layout (which is a Border Layout...
34908 var clayout = cfg.layout;
34909 clayout.el = this.el.createChild();
34910 clayout.items = clayout.items || [];
34914 // replace this exitems with the clayout ones..
34915 xitems = clayout.items;
34917 // force background off if it's in center...
34918 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34919 cfg.background = false;
34921 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34924 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34925 //console.log('adding nested layout panel ' + cfg.toSource());
34926 this.add(region, ret);
34927 nb = {}; /// find first...
34932 // needs grid and region
34934 //var el = this.getRegion(region).el.createChild();
34936 *var el = this.el.createChild();
34937 // create the grid first...
34938 cfg.grid.container = el;
34939 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34942 if (region == 'center' && this.active ) {
34943 cfg.background = false;
34946 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34948 this.add(region, ret);
34950 if (cfg.background) {
34951 // render grid on panel activation (if panel background)
34952 ret.on('activate', function(gp) {
34953 if (!gp.grid.rendered) {
34954 // gp.grid.render(el);
34958 // cfg.grid.render(el);
34964 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34965 // it was the old xcomponent building that caused this before.
34966 // espeically if border is the top element in the tree.
34976 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34978 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34979 this.add(region, ret);
34983 throw "Can not add '" + cfg.xtype + "' to Border";
34989 this.beginUpdate();
34993 Roo.each(xitems, function(i) {
34994 region = nb && i.region ? i.region : false;
34996 var add = ret.addxtype(i);
34999 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35000 if (!i.background) {
35001 abn[region] = nb[region] ;
35008 // make the last non-background panel active..
35009 //if (nb) { Roo.log(abn); }
35012 for(var r in abn) {
35013 region = this.getRegion(r);
35015 // tried using nb[r], but it does not work..
35017 region.showPanel(abn[r]);
35028 factory : function(cfg)
35031 var validRegions = Roo.bootstrap.layout.Border.regions;
35033 var target = cfg.region;
35036 var r = Roo.bootstrap.layout;
35040 return new r.North(cfg);
35042 return new r.South(cfg);
35044 return new r.East(cfg);
35046 return new r.West(cfg);
35048 return new r.Center(cfg);
35050 throw 'Layout region "'+target+'" not supported.';
35057 * Ext JS Library 1.1.1
35058 * Copyright(c) 2006-2007, Ext JS, LLC.
35060 * Originally Released Under LGPL - original licence link has changed is not relivant.
35063 * <script type="text/javascript">
35067 * @class Roo.bootstrap.layout.Basic
35068 * @extends Roo.util.Observable
35069 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35070 * and does not have a titlebar, tabs or any other features. All it does is size and position
35071 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35072 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35073 * @cfg {string} region the region that it inhabits..
35074 * @cfg {bool} skipConfig skip config?
35078 Roo.bootstrap.layout.Basic = function(config){
35080 this.mgr = config.mgr;
35082 this.position = config.region;
35084 var skipConfig = config.skipConfig;
35088 * @scope Roo.BasicLayoutRegion
35092 * @event beforeremove
35093 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35094 * @param {Roo.LayoutRegion} this
35095 * @param {Roo.ContentPanel} panel The panel
35096 * @param {Object} e The cancel event object
35098 "beforeremove" : true,
35100 * @event invalidated
35101 * Fires when the layout for this region is changed.
35102 * @param {Roo.LayoutRegion} this
35104 "invalidated" : true,
35106 * @event visibilitychange
35107 * Fires when this region is shown or hidden
35108 * @param {Roo.LayoutRegion} this
35109 * @param {Boolean} visibility true or false
35111 "visibilitychange" : true,
35113 * @event paneladded
35114 * Fires when a panel is added.
35115 * @param {Roo.LayoutRegion} this
35116 * @param {Roo.ContentPanel} panel The panel
35118 "paneladded" : true,
35120 * @event panelremoved
35121 * Fires when a panel is removed.
35122 * @param {Roo.LayoutRegion} this
35123 * @param {Roo.ContentPanel} panel The panel
35125 "panelremoved" : true,
35127 * @event beforecollapse
35128 * Fires when this region before collapse.
35129 * @param {Roo.LayoutRegion} this
35131 "beforecollapse" : true,
35134 * Fires when this region is collapsed.
35135 * @param {Roo.LayoutRegion} this
35137 "collapsed" : true,
35140 * Fires when this region is expanded.
35141 * @param {Roo.LayoutRegion} this
35146 * Fires when this region is slid into view.
35147 * @param {Roo.LayoutRegion} this
35149 "slideshow" : true,
35152 * Fires when this region slides out of view.
35153 * @param {Roo.LayoutRegion} this
35155 "slidehide" : true,
35157 * @event panelactivated
35158 * Fires when a panel is activated.
35159 * @param {Roo.LayoutRegion} this
35160 * @param {Roo.ContentPanel} panel The activated panel
35162 "panelactivated" : true,
35165 * Fires when the user resizes this region.
35166 * @param {Roo.LayoutRegion} this
35167 * @param {Number} newSize The new size (width for east/west, height for north/south)
35171 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35172 this.panels = new Roo.util.MixedCollection();
35173 this.panels.getKey = this.getPanelId.createDelegate(this);
35175 this.activePanel = null;
35176 // ensure listeners are added...
35178 if (config.listeners || config.events) {
35179 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35180 listeners : config.listeners || {},
35181 events : config.events || {}
35185 if(skipConfig !== true){
35186 this.applyConfig(config);
35190 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35192 getPanelId : function(p){
35196 applyConfig : function(config){
35197 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35198 this.config = config;
35203 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35204 * the width, for horizontal (north, south) the height.
35205 * @param {Number} newSize The new width or height
35207 resizeTo : function(newSize){
35208 var el = this.el ? this.el :
35209 (this.activePanel ? this.activePanel.getEl() : null);
35211 switch(this.position){
35214 el.setWidth(newSize);
35215 this.fireEvent("resized", this, newSize);
35219 el.setHeight(newSize);
35220 this.fireEvent("resized", this, newSize);
35226 getBox : function(){
35227 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35230 getMargins : function(){
35231 return this.margins;
35234 updateBox : function(box){
35236 var el = this.activePanel.getEl();
35237 el.dom.style.left = box.x + "px";
35238 el.dom.style.top = box.y + "px";
35239 this.activePanel.setSize(box.width, box.height);
35243 * Returns the container element for this region.
35244 * @return {Roo.Element}
35246 getEl : function(){
35247 return this.activePanel;
35251 * Returns true if this region is currently visible.
35252 * @return {Boolean}
35254 isVisible : function(){
35255 return this.activePanel ? true : false;
35258 setActivePanel : function(panel){
35259 panel = this.getPanel(panel);
35260 if(this.activePanel && this.activePanel != panel){
35261 this.activePanel.setActiveState(false);
35262 this.activePanel.getEl().setLeftTop(-10000,-10000);
35264 this.activePanel = panel;
35265 panel.setActiveState(true);
35267 panel.setSize(this.box.width, this.box.height);
35269 this.fireEvent("panelactivated", this, panel);
35270 this.fireEvent("invalidated");
35274 * Show the specified panel.
35275 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35276 * @return {Roo.ContentPanel} The shown panel or null
35278 showPanel : function(panel){
35279 panel = this.getPanel(panel);
35281 this.setActivePanel(panel);
35287 * Get the active panel for this region.
35288 * @return {Roo.ContentPanel} The active panel or null
35290 getActivePanel : function(){
35291 return this.activePanel;
35295 * Add the passed ContentPanel(s)
35296 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35297 * @return {Roo.ContentPanel} The panel added (if only one was added)
35299 add : function(panel){
35300 if(arguments.length > 1){
35301 for(var i = 0, len = arguments.length; i < len; i++) {
35302 this.add(arguments[i]);
35306 if(this.hasPanel(panel)){
35307 this.showPanel(panel);
35310 var el = panel.getEl();
35311 if(el.dom.parentNode != this.mgr.el.dom){
35312 this.mgr.el.dom.appendChild(el.dom);
35314 if(panel.setRegion){
35315 panel.setRegion(this);
35317 this.panels.add(panel);
35318 el.setStyle("position", "absolute");
35319 if(!panel.background){
35320 this.setActivePanel(panel);
35321 if(this.config.initialSize && this.panels.getCount()==1){
35322 this.resizeTo(this.config.initialSize);
35325 this.fireEvent("paneladded", this, panel);
35330 * Returns true if the panel is in this region.
35331 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35332 * @return {Boolean}
35334 hasPanel : function(panel){
35335 if(typeof panel == "object"){ // must be panel obj
35336 panel = panel.getId();
35338 return this.getPanel(panel) ? true : false;
35342 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35343 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35344 * @param {Boolean} preservePanel Overrides the config preservePanel option
35345 * @return {Roo.ContentPanel} The panel that was removed
35347 remove : function(panel, preservePanel){
35348 panel = this.getPanel(panel);
35353 this.fireEvent("beforeremove", this, panel, e);
35354 if(e.cancel === true){
35357 var panelId = panel.getId();
35358 this.panels.removeKey(panelId);
35363 * Returns the panel specified or null if it's not in this region.
35364 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35365 * @return {Roo.ContentPanel}
35367 getPanel : function(id){
35368 if(typeof id == "object"){ // must be panel obj
35371 return this.panels.get(id);
35375 * Returns this regions position (north/south/east/west/center).
35378 getPosition: function(){
35379 return this.position;
35383 * Ext JS Library 1.1.1
35384 * Copyright(c) 2006-2007, Ext JS, LLC.
35386 * Originally Released Under LGPL - original licence link has changed is not relivant.
35389 * <script type="text/javascript">
35393 * @class Roo.bootstrap.layout.Region
35394 * @extends Roo.bootstrap.layout.Basic
35395 * This class represents a region in a layout manager.
35397 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35398 * @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})
35399 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35400 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35401 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35402 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35403 * @cfg {String} title The title for the region (overrides panel titles)
35404 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35405 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35406 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35407 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35408 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35409 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35410 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35411 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35412 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35413 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35415 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35416 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35417 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35418 * @cfg {Number} width For East/West panels
35419 * @cfg {Number} height For North/South panels
35420 * @cfg {Boolean} split To show the splitter
35421 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35423 * @cfg {string} cls Extra CSS classes to add to region
35425 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35426 * @cfg {string} region the region that it inhabits..
35429 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35430 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35432 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35433 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35434 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35436 Roo.bootstrap.layout.Region = function(config)
35438 this.applyConfig(config);
35440 var mgr = config.mgr;
35441 var pos = config.region;
35442 config.skipConfig = true;
35443 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35446 this.onRender(mgr.el);
35449 this.visible = true;
35450 this.collapsed = false;
35451 this.unrendered_panels = [];
35454 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35456 position: '', // set by wrapper (eg. north/south etc..)
35457 unrendered_panels : null, // unrendered panels.
35458 createBody : function(){
35459 /** This region's body element
35460 * @type Roo.Element */
35461 this.bodyEl = this.el.createChild({
35463 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35467 onRender: function(ctr, pos)
35469 var dh = Roo.DomHelper;
35470 /** This region's container element
35471 * @type Roo.Element */
35472 this.el = dh.append(ctr.dom, {
35474 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35476 /** This region's title element
35477 * @type Roo.Element */
35479 this.titleEl = dh.append(this.el.dom,
35482 unselectable: "on",
35483 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35485 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35486 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35489 this.titleEl.enableDisplayMode();
35490 /** This region's title text element
35491 * @type HTMLElement */
35492 this.titleTextEl = this.titleEl.dom.firstChild;
35493 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35495 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35496 this.closeBtn.enableDisplayMode();
35497 this.closeBtn.on("click", this.closeClicked, this);
35498 this.closeBtn.hide();
35500 this.createBody(this.config);
35501 if(this.config.hideWhenEmpty){
35503 this.on("paneladded", this.validateVisibility, this);
35504 this.on("panelremoved", this.validateVisibility, this);
35506 if(this.autoScroll){
35507 this.bodyEl.setStyle("overflow", "auto");
35509 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35511 //if(c.titlebar !== false){
35512 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35513 this.titleEl.hide();
35515 this.titleEl.show();
35516 if(this.config.title){
35517 this.titleTextEl.innerHTML = this.config.title;
35521 if(this.config.collapsed){
35522 this.collapse(true);
35524 if(this.config.hidden){
35528 if (this.unrendered_panels && this.unrendered_panels.length) {
35529 for (var i =0;i< this.unrendered_panels.length; i++) {
35530 this.add(this.unrendered_panels[i]);
35532 this.unrendered_panels = null;
35538 applyConfig : function(c)
35541 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35542 var dh = Roo.DomHelper;
35543 if(c.titlebar !== false){
35544 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35545 this.collapseBtn.on("click", this.collapse, this);
35546 this.collapseBtn.enableDisplayMode();
35548 if(c.showPin === true || this.showPin){
35549 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35550 this.stickBtn.enableDisplayMode();
35551 this.stickBtn.on("click", this.expand, this);
35552 this.stickBtn.hide();
35557 /** This region's collapsed element
35558 * @type Roo.Element */
35561 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35562 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35565 if(c.floatable !== false){
35566 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35567 this.collapsedEl.on("click", this.collapseClick, this);
35570 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35571 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35572 id: "message", unselectable: "on", style:{"float":"left"}});
35573 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35575 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35576 this.expandBtn.on("click", this.expand, this);
35580 if(this.collapseBtn){
35581 this.collapseBtn.setVisible(c.collapsible == true);
35584 this.cmargins = c.cmargins || this.cmargins ||
35585 (this.position == "west" || this.position == "east" ?
35586 {top: 0, left: 2, right:2, bottom: 0} :
35587 {top: 2, left: 0, right:0, bottom: 2});
35589 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35592 this.bottomTabs = c.tabPosition != "top";
35594 this.autoScroll = c.autoScroll || false;
35599 this.duration = c.duration || .30;
35600 this.slideDuration = c.slideDuration || .45;
35605 * Returns true if this region is currently visible.
35606 * @return {Boolean}
35608 isVisible : function(){
35609 return this.visible;
35613 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35614 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35616 //setCollapsedTitle : function(title){
35617 // title = title || " ";
35618 // if(this.collapsedTitleTextEl){
35619 // this.collapsedTitleTextEl.innerHTML = title;
35623 getBox : function(){
35625 // if(!this.collapsed){
35626 b = this.el.getBox(false, true);
35628 // b = this.collapsedEl.getBox(false, true);
35633 getMargins : function(){
35634 return this.margins;
35635 //return this.collapsed ? this.cmargins : this.margins;
35638 highlight : function(){
35639 this.el.addClass("x-layout-panel-dragover");
35642 unhighlight : function(){
35643 this.el.removeClass("x-layout-panel-dragover");
35646 updateBox : function(box)
35648 if (!this.bodyEl) {
35649 return; // not rendered yet..
35653 if(!this.collapsed){
35654 this.el.dom.style.left = box.x + "px";
35655 this.el.dom.style.top = box.y + "px";
35656 this.updateBody(box.width, box.height);
35658 this.collapsedEl.dom.style.left = box.x + "px";
35659 this.collapsedEl.dom.style.top = box.y + "px";
35660 this.collapsedEl.setSize(box.width, box.height);
35663 this.tabs.autoSizeTabs();
35667 updateBody : function(w, h)
35670 this.el.setWidth(w);
35671 w -= this.el.getBorderWidth("rl");
35672 if(this.config.adjustments){
35673 w += this.config.adjustments[0];
35676 if(h !== null && h > 0){
35677 this.el.setHeight(h);
35678 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35679 h -= this.el.getBorderWidth("tb");
35680 if(this.config.adjustments){
35681 h += this.config.adjustments[1];
35683 this.bodyEl.setHeight(h);
35685 h = this.tabs.syncHeight(h);
35688 if(this.panelSize){
35689 w = w !== null ? w : this.panelSize.width;
35690 h = h !== null ? h : this.panelSize.height;
35692 if(this.activePanel){
35693 var el = this.activePanel.getEl();
35694 w = w !== null ? w : el.getWidth();
35695 h = h !== null ? h : el.getHeight();
35696 this.panelSize = {width: w, height: h};
35697 this.activePanel.setSize(w, h);
35699 if(Roo.isIE && this.tabs){
35700 this.tabs.el.repaint();
35705 * Returns the container element for this region.
35706 * @return {Roo.Element}
35708 getEl : function(){
35713 * Hides this region.
35716 //if(!this.collapsed){
35717 this.el.dom.style.left = "-2000px";
35720 // this.collapsedEl.dom.style.left = "-2000px";
35721 // this.collapsedEl.hide();
35723 this.visible = false;
35724 this.fireEvent("visibilitychange", this, false);
35728 * Shows this region if it was previously hidden.
35731 //if(!this.collapsed){
35734 // this.collapsedEl.show();
35736 this.visible = true;
35737 this.fireEvent("visibilitychange", this, true);
35740 closeClicked : function(){
35741 if(this.activePanel){
35742 this.remove(this.activePanel);
35746 collapseClick : function(e){
35748 e.stopPropagation();
35751 e.stopPropagation();
35757 * Collapses this region.
35758 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35761 collapse : function(skipAnim, skipCheck = false){
35762 if(this.collapsed) {
35766 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35768 this.collapsed = true;
35770 this.split.el.hide();
35772 if(this.config.animate && skipAnim !== true){
35773 this.fireEvent("invalidated", this);
35774 this.animateCollapse();
35776 this.el.setLocation(-20000,-20000);
35778 this.collapsedEl.show();
35779 this.fireEvent("collapsed", this);
35780 this.fireEvent("invalidated", this);
35786 animateCollapse : function(){
35791 * Expands this region if it was previously collapsed.
35792 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35793 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35796 expand : function(e, skipAnim){
35798 e.stopPropagation();
35800 if(!this.collapsed || this.el.hasActiveFx()) {
35804 this.afterSlideIn();
35807 this.collapsed = false;
35808 if(this.config.animate && skipAnim !== true){
35809 this.animateExpand();
35813 this.split.el.show();
35815 this.collapsedEl.setLocation(-2000,-2000);
35816 this.collapsedEl.hide();
35817 this.fireEvent("invalidated", this);
35818 this.fireEvent("expanded", this);
35822 animateExpand : function(){
35826 initTabs : function()
35828 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35830 var ts = new Roo.bootstrap.panel.Tabs({
35831 el: this.bodyEl.dom,
35832 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35833 disableTooltips: this.config.disableTabTips,
35834 toolbar : this.config.toolbar
35837 if(this.config.hideTabs){
35838 ts.stripWrap.setDisplayed(false);
35841 ts.resizeTabs = this.config.resizeTabs === true;
35842 ts.minTabWidth = this.config.minTabWidth || 40;
35843 ts.maxTabWidth = this.config.maxTabWidth || 250;
35844 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35845 ts.monitorResize = false;
35846 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35847 ts.bodyEl.addClass('roo-layout-tabs-body');
35848 this.panels.each(this.initPanelAsTab, this);
35851 initPanelAsTab : function(panel){
35852 var ti = this.tabs.addTab(
35856 this.config.closeOnTab && panel.isClosable(),
35859 if(panel.tabTip !== undefined){
35860 ti.setTooltip(panel.tabTip);
35862 ti.on("activate", function(){
35863 this.setActivePanel(panel);
35866 if(this.config.closeOnTab){
35867 ti.on("beforeclose", function(t, e){
35869 this.remove(panel);
35873 panel.tabItem = ti;
35878 updatePanelTitle : function(panel, title)
35880 if(this.activePanel == panel){
35881 this.updateTitle(title);
35884 var ti = this.tabs.getTab(panel.getEl().id);
35886 if(panel.tabTip !== undefined){
35887 ti.setTooltip(panel.tabTip);
35892 updateTitle : function(title){
35893 if(this.titleTextEl && !this.config.title){
35894 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35898 setActivePanel : function(panel)
35900 panel = this.getPanel(panel);
35901 if(this.activePanel && this.activePanel != panel){
35902 if(this.activePanel.setActiveState(false) === false){
35906 this.activePanel = panel;
35907 panel.setActiveState(true);
35908 if(this.panelSize){
35909 panel.setSize(this.panelSize.width, this.panelSize.height);
35912 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35914 this.updateTitle(panel.getTitle());
35916 this.fireEvent("invalidated", this);
35918 this.fireEvent("panelactivated", this, panel);
35922 * Shows the specified panel.
35923 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35924 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35926 showPanel : function(panel)
35928 panel = this.getPanel(panel);
35931 var tab = this.tabs.getTab(panel.getEl().id);
35932 if(tab.isHidden()){
35933 this.tabs.unhideTab(tab.id);
35937 this.setActivePanel(panel);
35944 * Get the active panel for this region.
35945 * @return {Roo.ContentPanel} The active panel or null
35947 getActivePanel : function(){
35948 return this.activePanel;
35951 validateVisibility : function(){
35952 if(this.panels.getCount() < 1){
35953 this.updateTitle(" ");
35954 this.closeBtn.hide();
35957 if(!this.isVisible()){
35964 * Adds the passed ContentPanel(s) to this region.
35965 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35966 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35968 add : function(panel)
35970 if(arguments.length > 1){
35971 for(var i = 0, len = arguments.length; i < len; i++) {
35972 this.add(arguments[i]);
35977 // if we have not been rendered yet, then we can not really do much of this..
35978 if (!this.bodyEl) {
35979 this.unrendered_panels.push(panel);
35986 if(this.hasPanel(panel)){
35987 this.showPanel(panel);
35990 panel.setRegion(this);
35991 this.panels.add(panel);
35992 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35993 // sinle panel - no tab...?? would it not be better to render it with the tabs,
35994 // and hide them... ???
35995 this.bodyEl.dom.appendChild(panel.getEl().dom);
35996 if(panel.background !== true){
35997 this.setActivePanel(panel);
35999 this.fireEvent("paneladded", this, panel);
36006 this.initPanelAsTab(panel);
36010 if(panel.background !== true){
36011 this.tabs.activate(panel.getEl().id);
36013 this.fireEvent("paneladded", this, panel);
36018 * Hides the tab for the specified panel.
36019 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36021 hidePanel : function(panel){
36022 if(this.tabs && (panel = this.getPanel(panel))){
36023 this.tabs.hideTab(panel.getEl().id);
36028 * Unhides the tab for a previously hidden panel.
36029 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36031 unhidePanel : function(panel){
36032 if(this.tabs && (panel = this.getPanel(panel))){
36033 this.tabs.unhideTab(panel.getEl().id);
36037 clearPanels : function(){
36038 while(this.panels.getCount() > 0){
36039 this.remove(this.panels.first());
36044 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36045 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36046 * @param {Boolean} preservePanel Overrides the config preservePanel option
36047 * @return {Roo.ContentPanel} The panel that was removed
36049 remove : function(panel, preservePanel)
36051 panel = this.getPanel(panel);
36056 this.fireEvent("beforeremove", this, panel, e);
36057 if(e.cancel === true){
36060 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36061 var panelId = panel.getId();
36062 this.panels.removeKey(panelId);
36064 document.body.appendChild(panel.getEl().dom);
36067 this.tabs.removeTab(panel.getEl().id);
36068 }else if (!preservePanel){
36069 this.bodyEl.dom.removeChild(panel.getEl().dom);
36071 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36072 var p = this.panels.first();
36073 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36074 tempEl.appendChild(p.getEl().dom);
36075 this.bodyEl.update("");
36076 this.bodyEl.dom.appendChild(p.getEl().dom);
36078 this.updateTitle(p.getTitle());
36080 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36081 this.setActivePanel(p);
36083 panel.setRegion(null);
36084 if(this.activePanel == panel){
36085 this.activePanel = null;
36087 if(this.config.autoDestroy !== false && preservePanel !== true){
36088 try{panel.destroy();}catch(e){}
36090 this.fireEvent("panelremoved", this, panel);
36095 * Returns the TabPanel component used by this region
36096 * @return {Roo.TabPanel}
36098 getTabs : function(){
36102 createTool : function(parentEl, className){
36103 var btn = Roo.DomHelper.append(parentEl, {
36105 cls: "x-layout-tools-button",
36108 cls: "roo-layout-tools-button-inner " + className,
36112 btn.addClassOnOver("roo-layout-tools-button-over");
36117 * Ext JS Library 1.1.1
36118 * Copyright(c) 2006-2007, Ext JS, LLC.
36120 * Originally Released Under LGPL - original licence link has changed is not relivant.
36123 * <script type="text/javascript">
36129 * @class Roo.SplitLayoutRegion
36130 * @extends Roo.LayoutRegion
36131 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36133 Roo.bootstrap.layout.Split = function(config){
36134 this.cursor = config.cursor;
36135 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36138 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36140 splitTip : "Drag to resize.",
36141 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36142 useSplitTips : false,
36144 applyConfig : function(config){
36145 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36148 onRender : function(ctr,pos) {
36150 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36151 if(!this.config.split){
36156 var splitEl = Roo.DomHelper.append(ctr.dom, {
36158 id: this.el.id + "-split",
36159 cls: "roo-layout-split roo-layout-split-"+this.position,
36162 /** The SplitBar for this region
36163 * @type Roo.SplitBar */
36164 // does not exist yet...
36165 Roo.log([this.position, this.orientation]);
36167 this.split = new Roo.bootstrap.SplitBar({
36168 dragElement : splitEl,
36169 resizingElement: this.el,
36170 orientation : this.orientation
36173 this.split.on("moved", this.onSplitMove, this);
36174 this.split.useShim = this.config.useShim === true;
36175 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36176 if(this.useSplitTips){
36177 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36179 //if(config.collapsible){
36180 // this.split.el.on("dblclick", this.collapse, this);
36183 if(typeof this.config.minSize != "undefined"){
36184 this.split.minSize = this.config.minSize;
36186 if(typeof this.config.maxSize != "undefined"){
36187 this.split.maxSize = this.config.maxSize;
36189 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36190 this.hideSplitter();
36195 getHMaxSize : function(){
36196 var cmax = this.config.maxSize || 10000;
36197 var center = this.mgr.getRegion("center");
36198 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36201 getVMaxSize : function(){
36202 var cmax = this.config.maxSize || 10000;
36203 var center = this.mgr.getRegion("center");
36204 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36207 onSplitMove : function(split, newSize){
36208 this.fireEvent("resized", this, newSize);
36212 * Returns the {@link Roo.SplitBar} for this region.
36213 * @return {Roo.SplitBar}
36215 getSplitBar : function(){
36220 this.hideSplitter();
36221 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36224 hideSplitter : function(){
36226 this.split.el.setLocation(-2000,-2000);
36227 this.split.el.hide();
36233 this.split.el.show();
36235 Roo.bootstrap.layout.Split.superclass.show.call(this);
36238 beforeSlide: function(){
36239 if(Roo.isGecko){// firefox overflow auto bug workaround
36240 this.bodyEl.clip();
36242 this.tabs.bodyEl.clip();
36244 if(this.activePanel){
36245 this.activePanel.getEl().clip();
36247 if(this.activePanel.beforeSlide){
36248 this.activePanel.beforeSlide();
36254 afterSlide : function(){
36255 if(Roo.isGecko){// firefox overflow auto bug workaround
36256 this.bodyEl.unclip();
36258 this.tabs.bodyEl.unclip();
36260 if(this.activePanel){
36261 this.activePanel.getEl().unclip();
36262 if(this.activePanel.afterSlide){
36263 this.activePanel.afterSlide();
36269 initAutoHide : function(){
36270 if(this.autoHide !== false){
36271 if(!this.autoHideHd){
36272 var st = new Roo.util.DelayedTask(this.slideIn, this);
36273 this.autoHideHd = {
36274 "mouseout": function(e){
36275 if(!e.within(this.el, true)){
36279 "mouseover" : function(e){
36285 this.el.on(this.autoHideHd);
36289 clearAutoHide : function(){
36290 if(this.autoHide !== false){
36291 this.el.un("mouseout", this.autoHideHd.mouseout);
36292 this.el.un("mouseover", this.autoHideHd.mouseover);
36296 clearMonitor : function(){
36297 Roo.get(document).un("click", this.slideInIf, this);
36300 // these names are backwards but not changed for compat
36301 slideOut : function(){
36302 if(this.isSlid || this.el.hasActiveFx()){
36305 this.isSlid = true;
36306 if(this.collapseBtn){
36307 this.collapseBtn.hide();
36309 this.closeBtnState = this.closeBtn.getStyle('display');
36310 this.closeBtn.hide();
36312 this.stickBtn.show();
36315 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36316 this.beforeSlide();
36317 this.el.setStyle("z-index", 10001);
36318 this.el.slideIn(this.getSlideAnchor(), {
36319 callback: function(){
36321 this.initAutoHide();
36322 Roo.get(document).on("click", this.slideInIf, this);
36323 this.fireEvent("slideshow", this);
36330 afterSlideIn : function(){
36331 this.clearAutoHide();
36332 this.isSlid = false;
36333 this.clearMonitor();
36334 this.el.setStyle("z-index", "");
36335 if(this.collapseBtn){
36336 this.collapseBtn.show();
36338 this.closeBtn.setStyle('display', this.closeBtnState);
36340 this.stickBtn.hide();
36342 this.fireEvent("slidehide", this);
36345 slideIn : function(cb){
36346 if(!this.isSlid || this.el.hasActiveFx()){
36350 this.isSlid = false;
36351 this.beforeSlide();
36352 this.el.slideOut(this.getSlideAnchor(), {
36353 callback: function(){
36354 this.el.setLeftTop(-10000, -10000);
36356 this.afterSlideIn();
36364 slideInIf : function(e){
36365 if(!e.within(this.el)){
36370 animateCollapse : function(){
36371 this.beforeSlide();
36372 this.el.setStyle("z-index", 20000);
36373 var anchor = this.getSlideAnchor();
36374 this.el.slideOut(anchor, {
36375 callback : function(){
36376 this.el.setStyle("z-index", "");
36377 this.collapsedEl.slideIn(anchor, {duration:.3});
36379 this.el.setLocation(-10000,-10000);
36381 this.fireEvent("collapsed", this);
36388 animateExpand : function(){
36389 this.beforeSlide();
36390 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36391 this.el.setStyle("z-index", 20000);
36392 this.collapsedEl.hide({
36395 this.el.slideIn(this.getSlideAnchor(), {
36396 callback : function(){
36397 this.el.setStyle("z-index", "");
36400 this.split.el.show();
36402 this.fireEvent("invalidated", this);
36403 this.fireEvent("expanded", this);
36431 getAnchor : function(){
36432 return this.anchors[this.position];
36435 getCollapseAnchor : function(){
36436 return this.canchors[this.position];
36439 getSlideAnchor : function(){
36440 return this.sanchors[this.position];
36443 getAlignAdj : function(){
36444 var cm = this.cmargins;
36445 switch(this.position){
36461 getExpandAdj : function(){
36462 var c = this.collapsedEl, cm = this.cmargins;
36463 switch(this.position){
36465 return [-(cm.right+c.getWidth()+cm.left), 0];
36468 return [cm.right+c.getWidth()+cm.left, 0];
36471 return [0, -(cm.top+cm.bottom+c.getHeight())];
36474 return [0, cm.top+cm.bottom+c.getHeight()];
36480 * Ext JS Library 1.1.1
36481 * Copyright(c) 2006-2007, Ext JS, LLC.
36483 * Originally Released Under LGPL - original licence link has changed is not relivant.
36486 * <script type="text/javascript">
36489 * These classes are private internal classes
36491 Roo.bootstrap.layout.Center = function(config){
36492 config.region = "center";
36493 Roo.bootstrap.layout.Region.call(this, config);
36494 this.visible = true;
36495 this.minWidth = config.minWidth || 20;
36496 this.minHeight = config.minHeight || 20;
36499 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36501 // center panel can't be hidden
36505 // center panel can't be hidden
36508 getMinWidth: function(){
36509 return this.minWidth;
36512 getMinHeight: function(){
36513 return this.minHeight;
36526 Roo.bootstrap.layout.North = function(config)
36528 config.region = 'north';
36529 config.cursor = 'n-resize';
36531 Roo.bootstrap.layout.Split.call(this, config);
36535 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36536 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36537 this.split.el.addClass("roo-layout-split-v");
36539 var size = config.initialSize || config.height;
36540 if(typeof size != "undefined"){
36541 this.el.setHeight(size);
36544 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36546 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36550 getBox : function(){
36551 if(this.collapsed){
36552 return this.collapsedEl.getBox();
36554 var box = this.el.getBox();
36556 box.height += this.split.el.getHeight();
36561 updateBox : function(box){
36562 if(this.split && !this.collapsed){
36563 box.height -= this.split.el.getHeight();
36564 this.split.el.setLeft(box.x);
36565 this.split.el.setTop(box.y+box.height);
36566 this.split.el.setWidth(box.width);
36568 if(this.collapsed){
36569 this.updateBody(box.width, null);
36571 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36579 Roo.bootstrap.layout.South = function(config){
36580 config.region = 'south';
36581 config.cursor = 's-resize';
36582 Roo.bootstrap.layout.Split.call(this, config);
36584 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36585 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36586 this.split.el.addClass("roo-layout-split-v");
36588 var size = config.initialSize || config.height;
36589 if(typeof size != "undefined"){
36590 this.el.setHeight(size);
36594 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36595 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36596 getBox : function(){
36597 if(this.collapsed){
36598 return this.collapsedEl.getBox();
36600 var box = this.el.getBox();
36602 var sh = this.split.el.getHeight();
36609 updateBox : function(box){
36610 if(this.split && !this.collapsed){
36611 var sh = this.split.el.getHeight();
36614 this.split.el.setLeft(box.x);
36615 this.split.el.setTop(box.y-sh);
36616 this.split.el.setWidth(box.width);
36618 if(this.collapsed){
36619 this.updateBody(box.width, null);
36621 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36625 Roo.bootstrap.layout.East = function(config){
36626 config.region = "east";
36627 config.cursor = "e-resize";
36628 Roo.bootstrap.layout.Split.call(this, config);
36630 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36631 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36632 this.split.el.addClass("roo-layout-split-h");
36634 var size = config.initialSize || config.width;
36635 if(typeof size != "undefined"){
36636 this.el.setWidth(size);
36639 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36640 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36641 getBox : function(){
36642 if(this.collapsed){
36643 return this.collapsedEl.getBox();
36645 var box = this.el.getBox();
36647 var sw = this.split.el.getWidth();
36654 updateBox : function(box){
36655 if(this.split && !this.collapsed){
36656 var sw = this.split.el.getWidth();
36658 this.split.el.setLeft(box.x);
36659 this.split.el.setTop(box.y);
36660 this.split.el.setHeight(box.height);
36663 if(this.collapsed){
36664 this.updateBody(null, box.height);
36666 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36670 Roo.bootstrap.layout.West = function(config){
36671 config.region = "west";
36672 config.cursor = "w-resize";
36674 Roo.bootstrap.layout.Split.call(this, config);
36676 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36677 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36678 this.split.el.addClass("roo-layout-split-h");
36682 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36683 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36685 onRender: function(ctr, pos)
36687 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36688 var size = this.config.initialSize || this.config.width;
36689 if(typeof size != "undefined"){
36690 this.el.setWidth(size);
36694 getBox : function(){
36695 if(this.collapsed){
36696 return this.collapsedEl.getBox();
36698 var box = this.el.getBox();
36700 box.width += this.split.el.getWidth();
36705 updateBox : function(box){
36706 if(this.split && !this.collapsed){
36707 var sw = this.split.el.getWidth();
36709 this.split.el.setLeft(box.x+box.width);
36710 this.split.el.setTop(box.y);
36711 this.split.el.setHeight(box.height);
36713 if(this.collapsed){
36714 this.updateBody(null, box.height);
36716 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36719 Roo.namespace("Roo.bootstrap.panel");/*
36721 * Ext JS Library 1.1.1
36722 * Copyright(c) 2006-2007, Ext JS, LLC.
36724 * Originally Released Under LGPL - original licence link has changed is not relivant.
36727 * <script type="text/javascript">
36730 * @class Roo.ContentPanel
36731 * @extends Roo.util.Observable
36732 * A basic ContentPanel element.
36733 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36734 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36735 * @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
36736 * @cfg {Boolean} closable True if the panel can be closed/removed
36737 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36738 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36739 * @cfg {Toolbar} toolbar A toolbar for this panel
36740 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36741 * @cfg {String} title The title for this panel
36742 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36743 * @cfg {String} url Calls {@link #setUrl} with this value
36744 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36745 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36746 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36747 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36748 * @cfg {Boolean} badges render the badges
36751 * Create a new ContentPanel.
36752 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36753 * @param {String/Object} config A string to set only the title or a config object
36754 * @param {String} content (optional) Set the HTML content for this panel
36755 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36757 Roo.bootstrap.panel.Content = function( config){
36759 this.tpl = config.tpl || false;
36761 var el = config.el;
36762 var content = config.content;
36764 if(config.autoCreate){ // xtype is available if this is called from factory
36767 this.el = Roo.get(el);
36768 if(!this.el && config && config.autoCreate){
36769 if(typeof config.autoCreate == "object"){
36770 if(!config.autoCreate.id){
36771 config.autoCreate.id = config.id||el;
36773 this.el = Roo.DomHelper.append(document.body,
36774 config.autoCreate, true);
36776 var elcfg = { tag: "div",
36777 cls: "roo-layout-inactive-content",
36781 elcfg.html = config.html;
36785 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36788 this.closable = false;
36789 this.loaded = false;
36790 this.active = false;
36793 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36795 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36797 this.wrapEl = this.el; //this.el.wrap();
36799 if (config.toolbar.items) {
36800 ti = config.toolbar.items ;
36801 delete config.toolbar.items ;
36805 this.toolbar.render(this.wrapEl, 'before');
36806 for(var i =0;i < ti.length;i++) {
36807 // Roo.log(['add child', items[i]]);
36808 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36810 this.toolbar.items = nitems;
36811 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36812 delete config.toolbar;
36816 // xtype created footer. - not sure if will work as we normally have to render first..
36817 if (this.footer && !this.footer.el && this.footer.xtype) {
36818 if (!this.wrapEl) {
36819 this.wrapEl = this.el.wrap();
36822 this.footer.container = this.wrapEl.createChild();
36824 this.footer = Roo.factory(this.footer, Roo);
36829 if(typeof config == "string"){
36830 this.title = config;
36832 Roo.apply(this, config);
36836 this.resizeEl = Roo.get(this.resizeEl, true);
36838 this.resizeEl = this.el;
36840 // handle view.xtype
36848 * Fires when this panel is activated.
36849 * @param {Roo.ContentPanel} this
36853 * @event deactivate
36854 * Fires when this panel is activated.
36855 * @param {Roo.ContentPanel} this
36857 "deactivate" : true,
36861 * Fires when this panel is resized if fitToFrame is true.
36862 * @param {Roo.ContentPanel} this
36863 * @param {Number} width The width after any component adjustments
36864 * @param {Number} height The height after any component adjustments
36870 * Fires when this tab is created
36871 * @param {Roo.ContentPanel} this
36882 if(this.autoScroll){
36883 this.resizeEl.setStyle("overflow", "auto");
36885 // fix randome scrolling
36886 //this.el.on('scroll', function() {
36887 // Roo.log('fix random scolling');
36888 // this.scrollTo('top',0);
36891 content = content || this.content;
36893 this.setContent(content);
36895 if(config && config.url){
36896 this.setUrl(this.url, this.params, this.loadOnce);
36901 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36903 if (this.view && typeof(this.view.xtype) != 'undefined') {
36904 this.view.el = this.el.appendChild(document.createElement("div"));
36905 this.view = Roo.factory(this.view);
36906 this.view.render && this.view.render(false, '');
36910 this.fireEvent('render', this);
36913 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36917 setRegion : function(region){
36918 this.region = region;
36919 this.setActiveClass(region && !this.background);
36923 setActiveClass: function(state)
36926 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36927 this.el.setStyle('position','relative');
36929 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36930 this.el.setStyle('position', 'absolute');
36935 * Returns the toolbar for this Panel if one was configured.
36936 * @return {Roo.Toolbar}
36938 getToolbar : function(){
36939 return this.toolbar;
36942 setActiveState : function(active)
36944 this.active = active;
36945 this.setActiveClass(active);
36947 if(this.fireEvent("deactivate", this) === false){
36952 this.fireEvent("activate", this);
36956 * Updates this panel's element
36957 * @param {String} content The new content
36958 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36960 setContent : function(content, loadScripts){
36961 this.el.update(content, loadScripts);
36964 ignoreResize : function(w, h){
36965 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36968 this.lastSize = {width: w, height: h};
36973 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36974 * @return {Roo.UpdateManager} The UpdateManager
36976 getUpdateManager : function(){
36977 return this.el.getUpdateManager();
36980 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36981 * @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:
36984 url: "your-url.php",
36985 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36986 callback: yourFunction,
36987 scope: yourObject, //(optional scope)
36990 text: "Loading...",
36995 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36996 * 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.
36997 * @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}
36998 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36999 * @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.
37000 * @return {Roo.ContentPanel} this
37003 var um = this.el.getUpdateManager();
37004 um.update.apply(um, arguments);
37010 * 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.
37011 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37012 * @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)
37013 * @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)
37014 * @return {Roo.UpdateManager} The UpdateManager
37016 setUrl : function(url, params, loadOnce){
37017 if(this.refreshDelegate){
37018 this.removeListener("activate", this.refreshDelegate);
37020 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37021 this.on("activate", this.refreshDelegate);
37022 return this.el.getUpdateManager();
37025 _handleRefresh : function(url, params, loadOnce){
37026 if(!loadOnce || !this.loaded){
37027 var updater = this.el.getUpdateManager();
37028 updater.update(url, params, this._setLoaded.createDelegate(this));
37032 _setLoaded : function(){
37033 this.loaded = true;
37037 * Returns this panel's id
37040 getId : function(){
37045 * Returns this panel's element - used by regiosn to add.
37046 * @return {Roo.Element}
37048 getEl : function(){
37049 return this.wrapEl || this.el;
37054 adjustForComponents : function(width, height)
37056 //Roo.log('adjustForComponents ');
37057 if(this.resizeEl != this.el){
37058 width -= this.el.getFrameWidth('lr');
37059 height -= this.el.getFrameWidth('tb');
37062 var te = this.toolbar.getEl();
37063 te.setWidth(width);
37064 height -= te.getHeight();
37067 var te = this.footer.getEl();
37068 te.setWidth(width);
37069 height -= te.getHeight();
37073 if(this.adjustments){
37074 width += this.adjustments[0];
37075 height += this.adjustments[1];
37077 return {"width": width, "height": height};
37080 setSize : function(width, height){
37081 if(this.fitToFrame && !this.ignoreResize(width, height)){
37082 if(this.fitContainer && this.resizeEl != this.el){
37083 this.el.setSize(width, height);
37085 var size = this.adjustForComponents(width, height);
37086 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37087 this.fireEvent('resize', this, size.width, size.height);
37092 * Returns this panel's title
37095 getTitle : function(){
37097 if (typeof(this.title) != 'object') {
37102 for (var k in this.title) {
37103 if (!this.title.hasOwnProperty(k)) {
37107 if (k.indexOf('-') >= 0) {
37108 var s = k.split('-');
37109 for (var i = 0; i<s.length; i++) {
37110 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37113 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37120 * Set this panel's title
37121 * @param {String} title
37123 setTitle : function(title){
37124 this.title = title;
37126 this.region.updatePanelTitle(this, title);
37131 * Returns true is this panel was configured to be closable
37132 * @return {Boolean}
37134 isClosable : function(){
37135 return this.closable;
37138 beforeSlide : function(){
37140 this.resizeEl.clip();
37143 afterSlide : function(){
37145 this.resizeEl.unclip();
37149 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37150 * Will fail silently if the {@link #setUrl} method has not been called.
37151 * This does not activate the panel, just updates its content.
37153 refresh : function(){
37154 if(this.refreshDelegate){
37155 this.loaded = false;
37156 this.refreshDelegate();
37161 * Destroys this panel
37163 destroy : function(){
37164 this.el.removeAllListeners();
37165 var tempEl = document.createElement("span");
37166 tempEl.appendChild(this.el.dom);
37167 tempEl.innerHTML = "";
37173 * form - if the content panel contains a form - this is a reference to it.
37174 * @type {Roo.form.Form}
37178 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37179 * This contains a reference to it.
37185 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37195 * @param {Object} cfg Xtype definition of item to add.
37199 getChildContainer: function () {
37200 return this.getEl();
37205 var ret = new Roo.factory(cfg);
37210 if (cfg.xtype.match(/^Form$/)) {
37213 //if (this.footer) {
37214 // el = this.footer.container.insertSibling(false, 'before');
37216 el = this.el.createChild();
37219 this.form = new Roo.form.Form(cfg);
37222 if ( this.form.allItems.length) {
37223 this.form.render(el.dom);
37227 // should only have one of theses..
37228 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37229 // views.. should not be just added - used named prop 'view''
37231 cfg.el = this.el.appendChild(document.createElement("div"));
37234 var ret = new Roo.factory(cfg);
37236 ret.render && ret.render(false, ''); // render blank..
37246 * @class Roo.bootstrap.panel.Grid
37247 * @extends Roo.bootstrap.panel.Content
37249 * Create a new GridPanel.
37250 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37251 * @param {Object} config A the config object
37257 Roo.bootstrap.panel.Grid = function(config)
37261 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37262 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37264 config.el = this.wrapper;
37265 //this.el = this.wrapper;
37267 if (config.container) {
37268 // ctor'ed from a Border/panel.grid
37271 this.wrapper.setStyle("overflow", "hidden");
37272 this.wrapper.addClass('roo-grid-container');
37277 if(config.toolbar){
37278 var tool_el = this.wrapper.createChild();
37279 this.toolbar = Roo.factory(config.toolbar);
37281 if (config.toolbar.items) {
37282 ti = config.toolbar.items ;
37283 delete config.toolbar.items ;
37287 this.toolbar.render(tool_el);
37288 for(var i =0;i < ti.length;i++) {
37289 // Roo.log(['add child', items[i]]);
37290 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37292 this.toolbar.items = nitems;
37294 delete config.toolbar;
37297 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37298 config.grid.scrollBody = true;;
37299 config.grid.monitorWindowResize = false; // turn off autosizing
37300 config.grid.autoHeight = false;
37301 config.grid.autoWidth = false;
37303 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37305 if (config.background) {
37306 // render grid on panel activation (if panel background)
37307 this.on('activate', function(gp) {
37308 if (!gp.grid.rendered) {
37309 gp.grid.render(this.wrapper);
37310 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37315 this.grid.render(this.wrapper);
37316 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37319 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37320 // ??? needed ??? config.el = this.wrapper;
37325 // xtype created footer. - not sure if will work as we normally have to render first..
37326 if (this.footer && !this.footer.el && this.footer.xtype) {
37328 var ctr = this.grid.getView().getFooterPanel(true);
37329 this.footer.dataSource = this.grid.dataSource;
37330 this.footer = Roo.factory(this.footer, Roo);
37331 this.footer.render(ctr);
37341 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37342 getId : function(){
37343 return this.grid.id;
37347 * Returns the grid for this panel
37348 * @return {Roo.bootstrap.Table}
37350 getGrid : function(){
37354 setSize : function(width, height){
37355 if(!this.ignoreResize(width, height)){
37356 var grid = this.grid;
37357 var size = this.adjustForComponents(width, height);
37358 var gridel = grid.getGridEl();
37359 gridel.setSize(size.width, size.height);
37361 var thd = grid.getGridEl().select('thead',true).first();
37362 var tbd = grid.getGridEl().select('tbody', true).first();
37364 tbd.setSize(width, height - thd.getHeight());
37373 beforeSlide : function(){
37374 this.grid.getView().scroller.clip();
37377 afterSlide : function(){
37378 this.grid.getView().scroller.unclip();
37381 destroy : function(){
37382 this.grid.destroy();
37384 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37389 * @class Roo.bootstrap.panel.Nest
37390 * @extends Roo.bootstrap.panel.Content
37392 * Create a new Panel, that can contain a layout.Border.
37395 * @param {Roo.BorderLayout} layout The layout for this panel
37396 * @param {String/Object} config A string to set only the title or a config object
37398 Roo.bootstrap.panel.Nest = function(config)
37400 // construct with only one argument..
37401 /* FIXME - implement nicer consturctors
37402 if (layout.layout) {
37404 layout = config.layout;
37405 delete config.layout;
37407 if (layout.xtype && !layout.getEl) {
37408 // then layout needs constructing..
37409 layout = Roo.factory(layout, Roo);
37413 config.el = config.layout.getEl();
37415 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37417 config.layout.monitorWindowResize = false; // turn off autosizing
37418 this.layout = config.layout;
37419 this.layout.getEl().addClass("roo-layout-nested-layout");
37426 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37428 setSize : function(width, height){
37429 if(!this.ignoreResize(width, height)){
37430 var size = this.adjustForComponents(width, height);
37431 var el = this.layout.getEl();
37432 if (size.height < 1) {
37433 el.setWidth(size.width);
37435 el.setSize(size.width, size.height);
37437 var touch = el.dom.offsetWidth;
37438 this.layout.layout();
37439 // ie requires a double layout on the first pass
37440 if(Roo.isIE && !this.initialized){
37441 this.initialized = true;
37442 this.layout.layout();
37447 // activate all subpanels if not currently active..
37449 setActiveState : function(active){
37450 this.active = active;
37451 this.setActiveClass(active);
37454 this.fireEvent("deactivate", this);
37458 this.fireEvent("activate", this);
37459 // not sure if this should happen before or after..
37460 if (!this.layout) {
37461 return; // should not happen..
37464 for (var r in this.layout.regions) {
37465 reg = this.layout.getRegion(r);
37466 if (reg.getActivePanel()) {
37467 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37468 reg.setActivePanel(reg.getActivePanel());
37471 if (!reg.panels.length) {
37474 reg.showPanel(reg.getPanel(0));
37483 * Returns the nested BorderLayout for this panel
37484 * @return {Roo.BorderLayout}
37486 getLayout : function(){
37487 return this.layout;
37491 * Adds a xtype elements to the layout of the nested panel
37495 xtype : 'ContentPanel',
37502 xtype : 'NestedLayoutPanel',
37508 items : [ ... list of content panels or nested layout panels.. ]
37512 * @param {Object} cfg Xtype definition of item to add.
37514 addxtype : function(cfg) {
37515 return this.layout.addxtype(cfg);
37520 * Ext JS Library 1.1.1
37521 * Copyright(c) 2006-2007, Ext JS, LLC.
37523 * Originally Released Under LGPL - original licence link has changed is not relivant.
37526 * <script type="text/javascript">
37529 * @class Roo.TabPanel
37530 * @extends Roo.util.Observable
37531 * A lightweight tab container.
37535 // basic tabs 1, built from existing content
37536 var tabs = new Roo.TabPanel("tabs1");
37537 tabs.addTab("script", "View Script");
37538 tabs.addTab("markup", "View Markup");
37539 tabs.activate("script");
37541 // more advanced tabs, built from javascript
37542 var jtabs = new Roo.TabPanel("jtabs");
37543 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37545 // set up the UpdateManager
37546 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37547 var updater = tab2.getUpdateManager();
37548 updater.setDefaultUrl("ajax1.htm");
37549 tab2.on('activate', updater.refresh, updater, true);
37551 // Use setUrl for Ajax loading
37552 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37553 tab3.setUrl("ajax2.htm", null, true);
37556 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37559 jtabs.activate("jtabs-1");
37562 * Create a new TabPanel.
37563 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37564 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37566 Roo.bootstrap.panel.Tabs = function(config){
37568 * The container element for this TabPanel.
37569 * @type Roo.Element
37571 this.el = Roo.get(config.el);
37574 if(typeof config == "boolean"){
37575 this.tabPosition = config ? "bottom" : "top";
37577 Roo.apply(this, config);
37581 if(this.tabPosition == "bottom"){
37582 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37583 this.el.addClass("roo-tabs-bottom");
37585 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37586 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37587 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37589 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37591 if(this.tabPosition != "bottom"){
37592 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37593 * @type Roo.Element
37595 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37596 this.el.addClass("roo-tabs-top");
37600 this.bodyEl.setStyle("position", "relative");
37602 this.active = null;
37603 this.activateDelegate = this.activate.createDelegate(this);
37608 * Fires when the active tab changes
37609 * @param {Roo.TabPanel} this
37610 * @param {Roo.TabPanelItem} activePanel The new active tab
37614 * @event beforetabchange
37615 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37616 * @param {Roo.TabPanel} this
37617 * @param {Object} e Set cancel to true on this object to cancel the tab change
37618 * @param {Roo.TabPanelItem} tab The tab being changed to
37620 "beforetabchange" : true
37623 Roo.EventManager.onWindowResize(this.onResize, this);
37624 this.cpad = this.el.getPadding("lr");
37625 this.hiddenCount = 0;
37628 // toolbar on the tabbar support...
37629 if (this.toolbar) {
37630 alert("no toolbar support yet");
37631 this.toolbar = false;
37633 var tcfg = this.toolbar;
37634 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37635 this.toolbar = new Roo.Toolbar(tcfg);
37636 if (Roo.isSafari) {
37637 var tbl = tcfg.container.child('table', true);
37638 tbl.setAttribute('width', '100%');
37646 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37649 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37651 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37653 tabPosition : "top",
37655 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37657 currentTabWidth : 0,
37659 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37663 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37667 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37669 preferredTabWidth : 175,
37671 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37673 resizeTabs : false,
37675 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37677 monitorResize : true,
37679 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37684 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37685 * @param {String} id The id of the div to use <b>or create</b>
37686 * @param {String} text The text for the tab
37687 * @param {String} content (optional) Content to put in the TabPanelItem body
37688 * @param {Boolean} closable (optional) True to create a close icon on the tab
37689 * @return {Roo.TabPanelItem} The created TabPanelItem
37691 addTab : function(id, text, content, closable, tpl)
37693 var item = new Roo.bootstrap.panel.TabItem({
37697 closable : closable,
37700 this.addTabItem(item);
37702 item.setContent(content);
37708 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37709 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37710 * @return {Roo.TabPanelItem}
37712 getTab : function(id){
37713 return this.items[id];
37717 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37718 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37720 hideTab : function(id){
37721 var t = this.items[id];
37724 this.hiddenCount++;
37725 this.autoSizeTabs();
37730 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37731 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37733 unhideTab : function(id){
37734 var t = this.items[id];
37736 t.setHidden(false);
37737 this.hiddenCount--;
37738 this.autoSizeTabs();
37743 * Adds an existing {@link Roo.TabPanelItem}.
37744 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37746 addTabItem : function(item){
37747 this.items[item.id] = item;
37748 this.items.push(item);
37749 // if(this.resizeTabs){
37750 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37751 // this.autoSizeTabs();
37753 // item.autoSize();
37758 * Removes a {@link Roo.TabPanelItem}.
37759 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37761 removeTab : function(id){
37762 var items = this.items;
37763 var tab = items[id];
37764 if(!tab) { return; }
37765 var index = items.indexOf(tab);
37766 if(this.active == tab && items.length > 1){
37767 var newTab = this.getNextAvailable(index);
37772 this.stripEl.dom.removeChild(tab.pnode.dom);
37773 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37774 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37776 items.splice(index, 1);
37777 delete this.items[tab.id];
37778 tab.fireEvent("close", tab);
37779 tab.purgeListeners();
37780 this.autoSizeTabs();
37783 getNextAvailable : function(start){
37784 var items = this.items;
37786 // look for a next tab that will slide over to
37787 // replace the one being removed
37788 while(index < items.length){
37789 var item = items[++index];
37790 if(item && !item.isHidden()){
37794 // if one isn't found select the previous tab (on the left)
37797 var item = items[--index];
37798 if(item && !item.isHidden()){
37806 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37807 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37809 disableTab : function(id){
37810 var tab = this.items[id];
37811 if(tab && this.active != tab){
37817 * Enables a {@link Roo.TabPanelItem} that is disabled.
37818 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37820 enableTab : function(id){
37821 var tab = this.items[id];
37826 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37827 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37828 * @return {Roo.TabPanelItem} The TabPanelItem.
37830 activate : function(id){
37831 var tab = this.items[id];
37835 if(tab == this.active || tab.disabled){
37839 this.fireEvent("beforetabchange", this, e, tab);
37840 if(e.cancel !== true && !tab.disabled){
37842 this.active.hide();
37844 this.active = this.items[id];
37845 this.active.show();
37846 this.fireEvent("tabchange", this, this.active);
37852 * Gets the active {@link Roo.TabPanelItem}.
37853 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37855 getActiveTab : function(){
37856 return this.active;
37860 * Updates the tab body element to fit the height of the container element
37861 * for overflow scrolling
37862 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37864 syncHeight : function(targetHeight){
37865 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37866 var bm = this.bodyEl.getMargins();
37867 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37868 this.bodyEl.setHeight(newHeight);
37872 onResize : function(){
37873 if(this.monitorResize){
37874 this.autoSizeTabs();
37879 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37881 beginUpdate : function(){
37882 this.updating = true;
37886 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37888 endUpdate : function(){
37889 this.updating = false;
37890 this.autoSizeTabs();
37894 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37896 autoSizeTabs : function(){
37897 var count = this.items.length;
37898 var vcount = count - this.hiddenCount;
37899 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37902 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37903 var availWidth = Math.floor(w / vcount);
37904 var b = this.stripBody;
37905 if(b.getWidth() > w){
37906 var tabs = this.items;
37907 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37908 if(availWidth < this.minTabWidth){
37909 /*if(!this.sleft){ // incomplete scrolling code
37910 this.createScrollButtons();
37913 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37916 if(this.currentTabWidth < this.preferredTabWidth){
37917 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37923 * Returns the number of tabs in this TabPanel.
37926 getCount : function(){
37927 return this.items.length;
37931 * Resizes all the tabs to the passed width
37932 * @param {Number} The new width
37934 setTabWidth : function(width){
37935 this.currentTabWidth = width;
37936 for(var i = 0, len = this.items.length; i < len; i++) {
37937 if(!this.items[i].isHidden()) {
37938 this.items[i].setWidth(width);
37944 * Destroys this TabPanel
37945 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37947 destroy : function(removeEl){
37948 Roo.EventManager.removeResizeListener(this.onResize, this);
37949 for(var i = 0, len = this.items.length; i < len; i++){
37950 this.items[i].purgeListeners();
37952 if(removeEl === true){
37953 this.el.update("");
37958 createStrip : function(container)
37960 var strip = document.createElement("nav");
37961 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37962 container.appendChild(strip);
37966 createStripList : function(strip)
37968 // div wrapper for retard IE
37969 // returns the "tr" element.
37970 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37971 //'<div class="x-tabs-strip-wrap">'+
37972 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37973 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37974 return strip.firstChild; //.firstChild.firstChild.firstChild;
37976 createBody : function(container)
37978 var body = document.createElement("div");
37979 Roo.id(body, "tab-body");
37980 //Roo.fly(body).addClass("x-tabs-body");
37981 Roo.fly(body).addClass("tab-content");
37982 container.appendChild(body);
37985 createItemBody :function(bodyEl, id){
37986 var body = Roo.getDom(id);
37988 body = document.createElement("div");
37991 //Roo.fly(body).addClass("x-tabs-item-body");
37992 Roo.fly(body).addClass("tab-pane");
37993 bodyEl.insertBefore(body, bodyEl.firstChild);
37997 createStripElements : function(stripEl, text, closable, tpl)
37999 var td = document.createElement("li"); // was td..
38002 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38005 stripEl.appendChild(td);
38007 td.className = "x-tabs-closable";
38008 if(!this.closeTpl){
38009 this.closeTpl = new Roo.Template(
38010 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38011 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38012 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38015 var el = this.closeTpl.overwrite(td, {"text": text});
38016 var close = el.getElementsByTagName("div")[0];
38017 var inner = el.getElementsByTagName("em")[0];
38018 return {"el": el, "close": close, "inner": inner};
38021 // not sure what this is..
38022 // if(!this.tabTpl){
38023 //this.tabTpl = new Roo.Template(
38024 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38025 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38027 // this.tabTpl = new Roo.Template(
38028 // '<a href="#">' +
38029 // '<span unselectable="on"' +
38030 // (this.disableTooltips ? '' : ' title="{text}"') +
38031 // ' >{text}</span></a>'
38037 var template = tpl || this.tabTpl || false;
38041 template = new Roo.Template(
38043 '<span unselectable="on"' +
38044 (this.disableTooltips ? '' : ' title="{text}"') +
38045 ' >{text}</span></a>'
38049 switch (typeof(template)) {
38053 template = new Roo.Template(template);
38059 var el = template.overwrite(td, {"text": text});
38061 var inner = el.getElementsByTagName("span")[0];
38063 return {"el": el, "inner": inner};
38071 * @class Roo.TabPanelItem
38072 * @extends Roo.util.Observable
38073 * Represents an individual item (tab plus body) in a TabPanel.
38074 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38075 * @param {String} id The id of this TabPanelItem
38076 * @param {String} text The text for the tab of this TabPanelItem
38077 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38079 Roo.bootstrap.panel.TabItem = function(config){
38081 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38082 * @type Roo.TabPanel
38084 this.tabPanel = config.panel;
38086 * The id for this TabPanelItem
38089 this.id = config.id;
38091 this.disabled = false;
38093 this.text = config.text;
38095 this.loaded = false;
38096 this.closable = config.closable;
38099 * The body element for this TabPanelItem.
38100 * @type Roo.Element
38102 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38103 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38104 this.bodyEl.setStyle("display", "block");
38105 this.bodyEl.setStyle("zoom", "1");
38106 //this.hideAction();
38108 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38110 this.el = Roo.get(els.el);
38111 this.inner = Roo.get(els.inner, true);
38112 this.textEl = Roo.get(this.el.dom.firstChild, true);
38113 this.pnode = Roo.get(els.el.parentNode, true);
38114 // this.el.on("mousedown", this.onTabMouseDown, this);
38115 this.el.on("click", this.onTabClick, this);
38117 if(config.closable){
38118 var c = Roo.get(els.close, true);
38119 c.dom.title = this.closeText;
38120 c.addClassOnOver("close-over");
38121 c.on("click", this.closeClick, this);
38127 * Fires when this tab becomes the active tab.
38128 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38129 * @param {Roo.TabPanelItem} this
38133 * @event beforeclose
38134 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38135 * @param {Roo.TabPanelItem} this
38136 * @param {Object} e Set cancel to true on this object to cancel the close.
38138 "beforeclose": true,
38141 * Fires when this tab is closed.
38142 * @param {Roo.TabPanelItem} this
38146 * @event deactivate
38147 * Fires when this tab is no longer the active tab.
38148 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38149 * @param {Roo.TabPanelItem} this
38151 "deactivate" : true
38153 this.hidden = false;
38155 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38158 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38160 purgeListeners : function(){
38161 Roo.util.Observable.prototype.purgeListeners.call(this);
38162 this.el.removeAllListeners();
38165 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38168 this.pnode.addClass("active");
38171 this.tabPanel.stripWrap.repaint();
38173 this.fireEvent("activate", this.tabPanel, this);
38177 * Returns true if this tab is the active tab.
38178 * @return {Boolean}
38180 isActive : function(){
38181 return this.tabPanel.getActiveTab() == this;
38185 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38188 this.pnode.removeClass("active");
38190 this.fireEvent("deactivate", this.tabPanel, this);
38193 hideAction : function(){
38194 this.bodyEl.hide();
38195 this.bodyEl.setStyle("position", "absolute");
38196 this.bodyEl.setLeft("-20000px");
38197 this.bodyEl.setTop("-20000px");
38200 showAction : function(){
38201 this.bodyEl.setStyle("position", "relative");
38202 this.bodyEl.setTop("");
38203 this.bodyEl.setLeft("");
38204 this.bodyEl.show();
38208 * Set the tooltip for the tab.
38209 * @param {String} tooltip The tab's tooltip
38211 setTooltip : function(text){
38212 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38213 this.textEl.dom.qtip = text;
38214 this.textEl.dom.removeAttribute('title');
38216 this.textEl.dom.title = text;
38220 onTabClick : function(e){
38221 e.preventDefault();
38222 this.tabPanel.activate(this.id);
38225 onTabMouseDown : function(e){
38226 e.preventDefault();
38227 this.tabPanel.activate(this.id);
38230 getWidth : function(){
38231 return this.inner.getWidth();
38234 setWidth : function(width){
38235 var iwidth = width - this.pnode.getPadding("lr");
38236 this.inner.setWidth(iwidth);
38237 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38238 this.pnode.setWidth(width);
38242 * Show or hide the tab
38243 * @param {Boolean} hidden True to hide or false to show.
38245 setHidden : function(hidden){
38246 this.hidden = hidden;
38247 this.pnode.setStyle("display", hidden ? "none" : "");
38251 * Returns true if this tab is "hidden"
38252 * @return {Boolean}
38254 isHidden : function(){
38255 return this.hidden;
38259 * Returns the text for this tab
38262 getText : function(){
38266 autoSize : function(){
38267 //this.el.beginMeasure();
38268 this.textEl.setWidth(1);
38270 * #2804 [new] Tabs in Roojs
38271 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38273 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38274 //this.el.endMeasure();
38278 * Sets the text for the tab (Note: this also sets the tooltip text)
38279 * @param {String} text The tab's text and tooltip
38281 setText : function(text){
38283 this.textEl.update(text);
38284 this.setTooltip(text);
38285 //if(!this.tabPanel.resizeTabs){
38286 // this.autoSize();
38290 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38292 activate : function(){
38293 this.tabPanel.activate(this.id);
38297 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38299 disable : function(){
38300 if(this.tabPanel.active != this){
38301 this.disabled = true;
38302 this.pnode.addClass("disabled");
38307 * Enables this TabPanelItem if it was previously disabled.
38309 enable : function(){
38310 this.disabled = false;
38311 this.pnode.removeClass("disabled");
38315 * Sets the content for this TabPanelItem.
38316 * @param {String} content The content
38317 * @param {Boolean} loadScripts true to look for and load scripts
38319 setContent : function(content, loadScripts){
38320 this.bodyEl.update(content, loadScripts);
38324 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38325 * @return {Roo.UpdateManager} The UpdateManager
38327 getUpdateManager : function(){
38328 return this.bodyEl.getUpdateManager();
38332 * Set a URL to be used to load the content for this TabPanelItem.
38333 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38334 * @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)
38335 * @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)
38336 * @return {Roo.UpdateManager} The UpdateManager
38338 setUrl : function(url, params, loadOnce){
38339 if(this.refreshDelegate){
38340 this.un('activate', this.refreshDelegate);
38342 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38343 this.on("activate", this.refreshDelegate);
38344 return this.bodyEl.getUpdateManager();
38348 _handleRefresh : function(url, params, loadOnce){
38349 if(!loadOnce || !this.loaded){
38350 var updater = this.bodyEl.getUpdateManager();
38351 updater.update(url, params, this._setLoaded.createDelegate(this));
38356 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38357 * Will fail silently if the setUrl method has not been called.
38358 * This does not activate the panel, just updates its content.
38360 refresh : function(){
38361 if(this.refreshDelegate){
38362 this.loaded = false;
38363 this.refreshDelegate();
38368 _setLoaded : function(){
38369 this.loaded = true;
38373 closeClick : function(e){
38376 this.fireEvent("beforeclose", this, o);
38377 if(o.cancel !== true){
38378 this.tabPanel.removeTab(this.id);
38382 * The text displayed in the tooltip for the close icon.
38385 closeText : "Close this tab"
38388 * This script refer to:
38389 * Title: International Telephone Input
38390 * Author: Jack O'Connor
38391 * Code version: v12.1.12
38392 * Availability: https://github.com/jackocnr/intl-tel-input.git
38395 Roo.bootstrap.PhoneInputData = function() {
38398 "Afghanistan (افغانستان)",
38403 "Albania (Shqipëri)",
38408 "Algeria (الجزائر)",
38433 "Antigua and Barbuda",
38443 "Armenia (Հայաստան)",
38459 "Austria (Österreich)",
38464 "Azerbaijan (Azərbaycan)",
38474 "Bahrain (البحرين)",
38479 "Bangladesh (বাংলাদেশ)",
38489 "Belarus (Беларусь)",
38494 "Belgium (België)",
38524 "Bosnia and Herzegovina (Босна и Херцеговина)",
38539 "British Indian Ocean Territory",
38544 "British Virgin Islands",
38554 "Bulgaria (България)",
38564 "Burundi (Uburundi)",
38569 "Cambodia (កម្ពុជា)",
38574 "Cameroon (Cameroun)",
38583 ["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"]
38586 "Cape Verde (Kabu Verdi)",
38591 "Caribbean Netherlands",
38602 "Central African Republic (République centrafricaine)",
38622 "Christmas Island",
38628 "Cocos (Keeling) Islands",
38639 "Comoros (جزر القمر)",
38644 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38649 "Congo (Republic) (Congo-Brazzaville)",
38669 "Croatia (Hrvatska)",
38690 "Czech Republic (Česká republika)",
38695 "Denmark (Danmark)",
38710 "Dominican Republic (República Dominicana)",
38714 ["809", "829", "849"]
38732 "Equatorial Guinea (Guinea Ecuatorial)",
38752 "Falkland Islands (Islas Malvinas)",
38757 "Faroe Islands (Føroyar)",
38778 "French Guiana (Guyane française)",
38783 "French Polynesia (Polynésie française)",
38798 "Georgia (საქართველო)",
38803 "Germany (Deutschland)",
38823 "Greenland (Kalaallit Nunaat)",
38860 "Guinea-Bissau (Guiné Bissau)",
38885 "Hungary (Magyarország)",
38890 "Iceland (Ísland)",
38910 "Iraq (العراق)",
38926 "Israel (ישראל)",
38953 "Jordan (الأردن)",
38958 "Kazakhstan (Казахстан)",
38979 "Kuwait (الكويت)",
38984 "Kyrgyzstan (Кыргызстан)",
38994 "Latvia (Latvija)",
38999 "Lebanon (لبنان)",
39014 "Libya (ليبيا)",
39024 "Lithuania (Lietuva)",
39039 "Macedonia (FYROM) (Македонија)",
39044 "Madagascar (Madagasikara)",
39074 "Marshall Islands",
39084 "Mauritania (موريتانيا)",
39089 "Mauritius (Moris)",
39110 "Moldova (Republica Moldova)",
39120 "Mongolia (Монгол)",
39125 "Montenegro (Crna Gora)",
39135 "Morocco (المغرب)",
39141 "Mozambique (Moçambique)",
39146 "Myanmar (Burma) (မြန်မာ)",
39151 "Namibia (Namibië)",
39166 "Netherlands (Nederland)",
39171 "New Caledonia (Nouvelle-Calédonie)",
39206 "North Korea (조선 민주주의 인민 공화국)",
39211 "Northern Mariana Islands",
39227 "Pakistan (پاکستان)",
39237 "Palestine (فلسطين)",
39247 "Papua New Guinea",
39289 "Réunion (La Réunion)",
39295 "Romania (România)",
39311 "Saint Barthélemy",
39322 "Saint Kitts and Nevis",
39332 "Saint Martin (Saint-Martin (partie française))",
39338 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39343 "Saint Vincent and the Grenadines",
39358 "São Tomé and Príncipe (São Tomé e Príncipe)",
39363 "Saudi Arabia (المملكة العربية السعودية)",
39368 "Senegal (Sénégal)",
39398 "Slovakia (Slovensko)",
39403 "Slovenia (Slovenija)",
39413 "Somalia (Soomaaliya)",
39423 "South Korea (대한민국)",
39428 "South Sudan (جنوب السودان)",
39438 "Sri Lanka (ශ්රී ලංකාව)",
39443 "Sudan (السودان)",
39453 "Svalbard and Jan Mayen",
39464 "Sweden (Sverige)",
39469 "Switzerland (Schweiz)",
39474 "Syria (سوريا)",
39519 "Trinidad and Tobago",
39524 "Tunisia (تونس)",
39529 "Turkey (Türkiye)",
39539 "Turks and Caicos Islands",
39549 "U.S. Virgin Islands",
39559 "Ukraine (Україна)",
39564 "United Arab Emirates (الإمارات العربية المتحدة)",
39586 "Uzbekistan (Oʻzbekiston)",
39596 "Vatican City (Città del Vaticano)",
39607 "Vietnam (Việt Nam)",
39612 "Wallis and Futuna (Wallis-et-Futuna)",
39617 "Western Sahara (الصحراء الغربية)",
39623 "Yemen (اليمن)",
39647 * This script refer to:
39648 * Title: International Telephone Input
39649 * Author: Jack O'Connor
39650 * Code version: v12.1.12
39651 * Availability: https://github.com/jackocnr/intl-tel-input.git
39655 * @class Roo.bootstrap.PhoneInput
39656 * @extends Roo.bootstrap.TriggerField
39657 * An input with International dial-code selection
39659 * @cfg {String} defaultDialCode default '+852'
39660 * @cfg {Array} preferedCountries default []
39663 * Create a new PhoneInput.
39664 * @param {Object} config Configuration options
39667 Roo.bootstrap.PhoneInput = function(config) {
39668 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39671 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39673 listWidth: undefined,
39675 selectedClass: 'active',
39677 invalidClass : "has-warning",
39679 validClass: 'has-success',
39681 allowed: '0123456789',
39684 * @cfg {String} defaultDialCode The default dial code when initializing the input
39686 defaultDialCode: '+852',
39689 * @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
39691 preferedCountries: false,
39693 getAutoCreate : function()
39695 var data = Roo.bootstrap.PhoneInputData();
39696 var align = this.labelAlign || this.parentLabelAlign();
39699 this.allCountries = [];
39700 this.dialCodeMapping = [];
39702 for (var i = 0; i < data.length; i++) {
39704 this.allCountries[i] = {
39708 priority: c[3] || 0,
39709 areaCodes: c[4] || null
39711 this.dialCodeMapping[c[2]] = {
39714 priority: c[3] || 0,
39715 areaCodes: c[4] || null
39727 cls : 'form-control tel-input',
39728 autocomplete: 'new-password'
39731 var hiddenInput = {
39734 cls: 'hidden-tel-input'
39738 hiddenInput.name = this.name;
39741 if (this.disabled) {
39742 input.disabled = true;
39745 var flag_container = {
39762 cls: this.hasFeedback ? 'has-feedback' : '',
39768 cls: 'dial-code-holder',
39775 cls: 'roo-select2-container input-group',
39782 if (this.fieldLabel.length) {
39785 tooltip: 'This field is required'
39791 cls: 'control-label',
39797 html: this.fieldLabel
39800 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39806 if(this.indicatorpos == 'right') {
39807 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39814 if(align == 'left') {
39822 if(this.labelWidth > 12){
39823 label.style = "width: " + this.labelWidth + 'px';
39825 if(this.labelWidth < 13 && this.labelmd == 0){
39826 this.labelmd = this.labelWidth;
39828 if(this.labellg > 0){
39829 label.cls += ' col-lg-' + this.labellg;
39830 input.cls += ' col-lg-' + (12 - this.labellg);
39832 if(this.labelmd > 0){
39833 label.cls += ' col-md-' + this.labelmd;
39834 container.cls += ' col-md-' + (12 - this.labelmd);
39836 if(this.labelsm > 0){
39837 label.cls += ' col-sm-' + this.labelsm;
39838 container.cls += ' col-sm-' + (12 - this.labelsm);
39840 if(this.labelxs > 0){
39841 label.cls += ' col-xs-' + this.labelxs;
39842 container.cls += ' col-xs-' + (12 - this.labelxs);
39852 var settings = this;
39854 ['xs','sm','md','lg'].map(function(size){
39855 if (settings[size]) {
39856 cfg.cls += ' col-' + size + '-' + settings[size];
39860 this.store = new Roo.data.Store({
39861 proxy : new Roo.data.MemoryProxy({}),
39862 reader : new Roo.data.JsonReader({
39873 'name' : 'dialCode',
39877 'name' : 'priority',
39881 'name' : 'areaCodes',
39888 if(!this.preferedCountries) {
39889 this.preferedCountries = [
39896 var p = this.preferedCountries.reverse();
39899 for (var i = 0; i < p.length; i++) {
39900 for (var j = 0; j < this.allCountries.length; j++) {
39901 if(this.allCountries[j].iso2 == p[i]) {
39902 var t = this.allCountries[j];
39903 this.allCountries.splice(j,1);
39904 this.allCountries.unshift(t);
39910 this.store.proxy.data = {
39912 data: this.allCountries
39918 initEvents : function()
39921 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39923 this.indicator = this.indicatorEl();
39924 this.flag = this.flagEl();
39925 this.dialCodeHolder = this.dialCodeHolderEl();
39927 this.trigger = this.el.select('div.flag-box',true).first();
39928 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39933 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39934 _this.list.setWidth(lw);
39937 this.list.on('mouseover', this.onViewOver, this);
39938 this.list.on('mousemove', this.onViewMove, this);
39939 this.inputEl().on("keyup", this.onKeyUp, this);
39941 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39943 this.view = new Roo.View(this.list, this.tpl, {
39944 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39947 this.view.on('click', this.onViewClick, this);
39948 this.setValue(this.defaultDialCode);
39951 onTriggerClick : function(e)
39953 Roo.log('trigger click');
39958 if(this.isExpanded()){
39960 this.hasFocus = false;
39962 this.store.load({});
39963 this.hasFocus = true;
39968 isExpanded : function()
39970 return this.list.isVisible();
39973 collapse : function()
39975 if(!this.isExpanded()){
39979 Roo.get(document).un('mousedown', this.collapseIf, this);
39980 Roo.get(document).un('mousewheel', this.collapseIf, this);
39981 this.fireEvent('collapse', this);
39985 expand : function()
39989 if(this.isExpanded() || !this.hasFocus){
39993 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39994 this.list.setWidth(lw);
39997 this.restrictHeight();
39999 Roo.get(document).on('mousedown', this.collapseIf, this);
40000 Roo.get(document).on('mousewheel', this.collapseIf, this);
40002 this.fireEvent('expand', this);
40005 restrictHeight : function()
40007 this.list.alignTo(this.inputEl(), this.listAlign);
40008 this.list.alignTo(this.inputEl(), this.listAlign);
40011 onViewOver : function(e, t)
40013 if(this.inKeyMode){
40016 var item = this.view.findItemFromChild(t);
40019 var index = this.view.indexOf(item);
40020 this.select(index, false);
40025 onViewClick : function(view, doFocus, el, e)
40027 var index = this.view.getSelectedIndexes()[0];
40029 var r = this.store.getAt(index);
40032 this.onSelect(r, index);
40034 if(doFocus !== false && !this.blockFocus){
40035 this.inputEl().focus();
40039 onViewMove : function(e, t)
40041 this.inKeyMode = false;
40044 select : function(index, scrollIntoView)
40046 this.selectedIndex = index;
40047 this.view.select(index);
40048 if(scrollIntoView !== false){
40049 var el = this.view.getNode(index);
40051 this.list.scrollChildIntoView(el, false);
40056 createList : function()
40058 this.list = Roo.get(document.body).createChild({
40060 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40061 style: 'display:none'
40064 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40067 collapseIf : function(e)
40069 var in_combo = e.within(this.el);
40070 var in_list = e.within(this.list);
40071 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40073 if (in_combo || in_list || is_list) {
40079 onSelect : function(record, index)
40081 if(this.fireEvent('beforeselect', this, record, index) !== false){
40083 this.setFlagClass(record.data.iso2);
40084 this.setDialCode(record.data.dialCode);
40085 this.hasFocus = false;
40087 this.fireEvent('select', this, record, index);
40091 flagEl : function()
40093 var flag = this.el.select('div.flag',true).first();
40100 dialCodeHolderEl : function()
40102 var d = this.el.select('input.dial-code-holder',true).first();
40109 setDialCode : function(v)
40111 this.dialCodeHolder.dom.value = '+'+v;
40114 setFlagClass : function(n)
40116 this.flag.dom.className = 'flag '+n;
40119 getValue : function()
40121 var v = this.inputEl().getValue();
40122 if(this.dialCodeHolder) {
40123 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40128 setValue : function(v)
40130 var d = this.getDialCode(v);
40132 //invalid dial code
40133 if(v.length == 0 || !d || d.length == 0) {
40135 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40136 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40142 this.setFlagClass(this.dialCodeMapping[d].iso2);
40143 this.setDialCode(d);
40144 this.inputEl().dom.value = v.replace('+'+d,'');
40145 this.hiddenEl().dom.value = this.getValue();
40150 getDialCode : function(v)
40154 if (v.length == 0) {
40155 return this.dialCodeHolder.dom.value;
40159 if (v.charAt(0) != "+") {
40162 var numericChars = "";
40163 for (var i = 1; i < v.length; i++) {
40164 var c = v.charAt(i);
40167 if (this.dialCodeMapping[numericChars]) {
40168 dialCode = v.substr(1, i);
40170 if (numericChars.length == 4) {
40180 this.setValue(this.defaultDialCode);
40184 hiddenEl : function()
40186 return this.el.select('input.hidden-tel-input',true).first();
40189 onKeyUp : function(e){
40191 var k = e.getKey();
40192 var c = e.getCharCode();
40195 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40196 this.allowed.indexOf(String.fromCharCode(c)) === -1
40201 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40204 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40208 this.setValue(this.getValue());
40213 * @class Roo.bootstrap.MoneyField
40214 * @extends Roo.bootstrap.ComboBox
40215 * Bootstrap MoneyField class
40218 * Create a new MoneyField.
40219 * @param {Object} config Configuration options
40222 Roo.bootstrap.MoneyField = function(config) {
40224 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40228 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40231 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40233 allowDecimals : true,
40235 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40237 decimalSeparator : ".",
40239 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40241 decimalPrecision : 0,
40243 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40245 allowNegative : true,
40247 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40251 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40253 minValue : Number.NEGATIVE_INFINITY,
40255 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40257 maxValue : Number.MAX_VALUE,
40259 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40261 minText : "The minimum value for this field is {0}",
40263 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40265 maxText : "The maximum value for this field is {0}",
40267 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40268 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40270 nanText : "{0} is not a valid number",
40272 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40276 * @cfg {String} defaults currency of the MoneyField
40277 * value should be in lkey
40279 defaultCurrency : false,
40281 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40283 thousandsDelimiter : false,
40293 getAutoCreate : function()
40295 var align = this.labelAlign || this.parentLabelAlign();
40307 cls : 'form-control roo-money-amount-input',
40308 autocomplete: 'new-password'
40311 var hiddenInput = {
40315 cls: 'hidden-number-input'
40319 hiddenInput.name = this.name;
40322 if (this.disabled) {
40323 input.disabled = true;
40326 var clg = 12 - this.inputlg;
40327 var cmd = 12 - this.inputmd;
40328 var csm = 12 - this.inputsm;
40329 var cxs = 12 - this.inputxs;
40333 cls : 'row roo-money-field',
40337 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40341 cls: 'roo-select2-container input-group',
40345 cls : 'form-control roo-money-currency-input',
40346 autocomplete: 'new-password',
40348 name : this.currencyName
40352 cls : 'input-group-addon',
40366 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40370 cls: this.hasFeedback ? 'has-feedback' : '',
40381 if (this.fieldLabel.length) {
40384 tooltip: 'This field is required'
40390 cls: 'control-label',
40396 html: this.fieldLabel
40399 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40405 if(this.indicatorpos == 'right') {
40406 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40413 if(align == 'left') {
40421 if(this.labelWidth > 12){
40422 label.style = "width: " + this.labelWidth + 'px';
40424 if(this.labelWidth < 13 && this.labelmd == 0){
40425 this.labelmd = this.labelWidth;
40427 if(this.labellg > 0){
40428 label.cls += ' col-lg-' + this.labellg;
40429 input.cls += ' col-lg-' + (12 - this.labellg);
40431 if(this.labelmd > 0){
40432 label.cls += ' col-md-' + this.labelmd;
40433 container.cls += ' col-md-' + (12 - this.labelmd);
40435 if(this.labelsm > 0){
40436 label.cls += ' col-sm-' + this.labelsm;
40437 container.cls += ' col-sm-' + (12 - this.labelsm);
40439 if(this.labelxs > 0){
40440 label.cls += ' col-xs-' + this.labelxs;
40441 container.cls += ' col-xs-' + (12 - this.labelxs);
40452 var settings = this;
40454 ['xs','sm','md','lg'].map(function(size){
40455 if (settings[size]) {
40456 cfg.cls += ' col-' + size + '-' + settings[size];
40463 initEvents : function()
40465 this.indicator = this.indicatorEl();
40467 this.initCurrencyEvent();
40469 this.initNumberEvent();
40472 initCurrencyEvent : function()
40475 throw "can not find store for combo";
40478 this.store = Roo.factory(this.store, Roo.data);
40479 this.store.parent = this;
40483 this.triggerEl = this.el.select('.input-group-addon', true).first();
40485 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40490 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40491 _this.list.setWidth(lw);
40494 this.list.on('mouseover', this.onViewOver, this);
40495 this.list.on('mousemove', this.onViewMove, this);
40496 this.list.on('scroll', this.onViewScroll, this);
40499 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40502 this.view = new Roo.View(this.list, this.tpl, {
40503 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40506 this.view.on('click', this.onViewClick, this);
40508 this.store.on('beforeload', this.onBeforeLoad, this);
40509 this.store.on('load', this.onLoad, this);
40510 this.store.on('loadexception', this.onLoadException, this);
40512 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40513 "up" : function(e){
40514 this.inKeyMode = true;
40518 "down" : function(e){
40519 if(!this.isExpanded()){
40520 this.onTriggerClick();
40522 this.inKeyMode = true;
40527 "enter" : function(e){
40530 if(this.fireEvent("specialkey", this, e)){
40531 this.onViewClick(false);
40537 "esc" : function(e){
40541 "tab" : function(e){
40544 if(this.fireEvent("specialkey", this, e)){
40545 this.onViewClick(false);
40553 doRelay : function(foo, bar, hname){
40554 if(hname == 'down' || this.scope.isExpanded()){
40555 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40563 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40567 initNumberEvent : function(e)
40569 this.inputEl().on("keydown" , this.fireKey, this);
40570 this.inputEl().on("focus", this.onFocus, this);
40571 this.inputEl().on("blur", this.onBlur, this);
40573 this.inputEl().relayEvent('keyup', this);
40575 if(this.indicator){
40576 this.indicator.addClass('invisible');
40579 this.originalValue = this.getValue();
40581 if(this.validationEvent == 'keyup'){
40582 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40583 this.inputEl().on('keyup', this.filterValidation, this);
40585 else if(this.validationEvent !== false){
40586 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40589 if(this.selectOnFocus){
40590 this.on("focus", this.preFocus, this);
40593 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40594 this.inputEl().on("keypress", this.filterKeys, this);
40596 this.inputEl().relayEvent('keypress', this);
40599 var allowed = "0123456789";
40601 if(this.allowDecimals){
40602 allowed += this.decimalSeparator;
40605 if(this.allowNegative){
40609 if(this.thousandsDelimiter) {
40613 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40615 var keyPress = function(e){
40617 var k = e.getKey();
40619 var c = e.getCharCode();
40622 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40623 allowed.indexOf(String.fromCharCode(c)) === -1
40629 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40633 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40638 this.inputEl().on("keypress", keyPress, this);
40642 onTriggerClick : function(e)
40649 this.loadNext = false;
40651 if(this.isExpanded()){
40656 this.hasFocus = true;
40658 if(this.triggerAction == 'all') {
40659 this.doQuery(this.allQuery, true);
40663 this.doQuery(this.getRawValue());
40666 getCurrency : function()
40668 var v = this.currencyEl().getValue();
40673 restrictHeight : function()
40675 this.list.alignTo(this.currencyEl(), this.listAlign);
40676 this.list.alignTo(this.currencyEl(), this.listAlign);
40679 onViewClick : function(view, doFocus, el, e)
40681 var index = this.view.getSelectedIndexes()[0];
40683 var r = this.store.getAt(index);
40686 this.onSelect(r, index);
40690 onSelect : function(record, index){
40692 if(this.fireEvent('beforeselect', this, record, index) !== false){
40694 this.setFromCurrencyData(index > -1 ? record.data : false);
40698 this.fireEvent('select', this, record, index);
40702 setFromCurrencyData : function(o)
40706 this.lastCurrency = o;
40708 if (this.currencyField) {
40709 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40711 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40714 this.lastSelectionText = currency;
40716 //setting default currency
40717 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40718 this.setCurrency(this.defaultCurrency);
40722 this.setCurrency(currency);
40725 setFromData : function(o)
40729 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40731 this.setFromCurrencyData(c);
40736 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40738 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40741 this.setValue(value);
40745 setCurrency : function(v)
40747 this.currencyValue = v;
40750 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40755 setValue : function(v)
40757 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40763 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40765 this.inputEl().dom.value = (v == '') ? '' :
40766 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40768 if(!this.allowZero && v === '0') {
40769 this.hiddenEl().dom.value = '';
40770 this.inputEl().dom.value = '';
40777 getRawValue : function()
40779 var v = this.inputEl().getValue();
40784 getValue : function()
40786 return this.fixPrecision(this.parseValue(this.getRawValue()));
40789 parseValue : function(value)
40791 if(this.thousandsDelimiter) {
40793 r = new RegExp(",", "g");
40794 value = value.replace(r, "");
40797 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40798 return isNaN(value) ? '' : value;
40802 fixPrecision : function(value)
40804 if(this.thousandsDelimiter) {
40806 r = new RegExp(",", "g");
40807 value = value.replace(r, "");
40810 var nan = isNaN(value);
40812 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40813 return nan ? '' : value;
40815 return parseFloat(value).toFixed(this.decimalPrecision);
40818 decimalPrecisionFcn : function(v)
40820 return Math.floor(v);
40823 validateValue : function(value)
40825 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40829 var num = this.parseValue(value);
40832 this.markInvalid(String.format(this.nanText, value));
40836 if(num < this.minValue){
40837 this.markInvalid(String.format(this.minText, this.minValue));
40841 if(num > this.maxValue){
40842 this.markInvalid(String.format(this.maxText, this.maxValue));
40849 validate : function()
40851 if(this.disabled || this.allowBlank){
40856 var currency = this.getCurrency();
40858 if(this.validateValue(this.getRawValue()) && currency.length){
40863 this.markInvalid();
40867 getName: function()
40872 beforeBlur : function()
40878 var v = this.parseValue(this.getRawValue());
40885 onBlur : function()
40889 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40890 //this.el.removeClass(this.focusClass);
40893 this.hasFocus = false;
40895 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40899 var v = this.getValue();
40901 if(String(v) !== String(this.startValue)){
40902 this.fireEvent('change', this, v, this.startValue);
40905 this.fireEvent("blur", this);
40908 inputEl : function()
40910 return this.el.select('.roo-money-amount-input', true).first();
40913 currencyEl : function()
40915 return this.el.select('.roo-money-currency-input', true).first();
40918 hiddenEl : function()
40920 return this.el.select('input.hidden-number-input',true).first();