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',
20405 useFontAwesomeCheckBox : false,
20407 getAutoCreate : function()
20409 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20415 cfg.cls = 'form-group ' + this.inputType; //input-group
20418 cfg.cls += ' ' + this.inputType + '-inline';
20424 type : this.inputType,
20425 value : this.inputValue,
20426 cls : 'roo-' + this.inputType, //'form-box',
20427 placeholder : this.placeholder || ''
20431 if(this.inputType != 'radio'){
20435 cls : 'roo-hidden-value',
20436 value : this.checked ? this.inputValue : this.valueOff
20441 if (this.weight) { // Validity check?
20442 cfg.cls += " " + this.inputType + "-" + this.weight;
20445 if (this.disabled) {
20446 input.disabled=true;
20450 input.checked = this.checked;
20455 input.name = this.name;
20457 if(this.inputType != 'radio'){
20458 hidden.name = this.name;
20459 input.name = '_hidden_' + this.name;
20464 input.cls += ' input-' + this.size;
20469 ['xs','sm','md','lg'].map(function(size){
20470 if (settings[size]) {
20471 cfg.cls += ' col-' + size + '-' + settings[size];
20475 var inputblock = input;
20477 if (this.before || this.after) {
20480 cls : 'input-group',
20485 inputblock.cn.push({
20487 cls : 'input-group-addon',
20492 inputblock.cn.push(input);
20494 if(this.inputType != 'radio'){
20495 inputblock.cn.push(hidden);
20499 inputblock.cn.push({
20501 cls : 'input-group-addon',
20508 if (align ==='left' && this.fieldLabel.length) {
20509 // Roo.log("left and has label");
20514 cls : 'control-label',
20515 html : this.fieldLabel
20525 if(this.labelWidth > 12){
20526 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20529 if(this.labelWidth < 13 && this.labelmd == 0){
20530 this.labelmd = this.labelWidth;
20533 if(this.labellg > 0){
20534 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20535 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20538 if(this.labelmd > 0){
20539 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20540 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20543 if(this.labelsm > 0){
20544 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20545 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20548 if(this.labelxs > 0){
20549 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20550 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20553 } else if ( this.fieldLabel.length) {
20554 // Roo.log(" label");
20558 tag: this.boxLabel ? 'span' : 'label',
20560 cls: 'control-label box-input-label',
20561 //cls : 'input-group-addon',
20562 html : this.fieldLabel
20571 // Roo.log(" no label && no align");
20572 cfg.cn = [ inputblock ] ;
20578 var boxLabelCfg = {
20580 //'for': id, // box label is handled by onclick - so no for...
20582 html: this.boxLabel
20585 if(this.useFontAwesomeCheckBox) {
20586 boxLabelCfg.cls = 'box-label fa-checkbox'
20590 boxLabelCfg.tooltip = this.tooltip;
20593 cfg.cn.push(boxLabelCfg);
20596 if(this.inputType != 'radio'){
20597 cfg.cn.push(hidden);
20605 * return the real input element.
20607 inputEl: function ()
20609 return this.el.select('input.roo-' + this.inputType,true).first();
20611 hiddenEl: function ()
20613 return this.el.select('input.roo-hidden-value',true).first();
20616 labelEl: function()
20618 return this.el.select('label.control-label',true).first();
20620 /* depricated... */
20624 return this.labelEl();
20627 boxLabelEl: function()
20629 return this.el.select('label.box-label',true).first();
20632 initEvents : function()
20634 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20636 this.inputEl().on('click', this.onClick, this);
20638 if (this.boxLabel) {
20639 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20642 this.startValue = this.getValue();
20645 Roo.bootstrap.CheckBox.register(this);
20649 onClick : function(e)
20651 if(this.fireEvent('click', this, e) !== false){
20652 this.setChecked(!this.checked);
20657 setChecked : function(state,suppressEvent)
20659 this.startValue = this.getValue();
20661 if(this.inputType == 'radio'){
20663 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20664 e.dom.checked = false;
20667 this.inputEl().dom.checked = true;
20669 this.inputEl().dom.value = this.inputValue;
20671 if(suppressEvent !== true){
20672 this.fireEvent('check', this, true);
20680 this.checked = state;
20682 this.inputEl().dom.checked = state;
20685 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20687 if(suppressEvent !== true){
20688 this.fireEvent('check', this, state);
20694 getValue : function()
20696 if(this.inputType == 'radio'){
20697 return this.getGroupValue();
20700 return this.hiddenEl().dom.value;
20704 getGroupValue : function()
20706 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20710 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20713 setValue : function(v,suppressEvent)
20715 if(this.inputType == 'radio'){
20716 this.setGroupValue(v, suppressEvent);
20720 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20725 setGroupValue : function(v, suppressEvent)
20727 this.startValue = this.getValue();
20729 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20730 e.dom.checked = false;
20732 if(e.dom.value == v){
20733 e.dom.checked = true;
20737 if(suppressEvent !== true){
20738 this.fireEvent('check', this, true);
20746 validate : function()
20748 if(this.getVisibilityEl().hasClass('hidden')){
20754 (this.inputType == 'radio' && this.validateRadio()) ||
20755 (this.inputType == 'checkbox' && this.validateCheckbox())
20761 this.markInvalid();
20765 validateRadio : function()
20767 if(this.getVisibilityEl().hasClass('hidden')){
20771 if(this.allowBlank){
20777 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20778 if(!e.dom.checked){
20790 validateCheckbox : function()
20793 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20794 //return (this.getValue() == this.inputValue) ? true : false;
20797 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20805 for(var i in group){
20806 if(group[i].el.isVisible(true)){
20814 for(var i in group){
20819 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20826 * Mark this field as valid
20828 markValid : function()
20832 this.fireEvent('valid', this);
20834 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20837 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20844 if(this.inputType == 'radio'){
20845 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20846 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20847 e.findParent('.form-group', false, true).addClass(_this.validClass);
20854 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20855 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20859 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20865 for(var i in group){
20866 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20867 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20872 * Mark this field as invalid
20873 * @param {String} msg The validation message
20875 markInvalid : function(msg)
20877 if(this.allowBlank){
20883 this.fireEvent('invalid', this, msg);
20885 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20888 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20892 label.markInvalid();
20895 if(this.inputType == 'radio'){
20896 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20897 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20898 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20905 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20906 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20910 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20916 for(var i in group){
20917 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20918 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20923 clearInvalid : function()
20925 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20927 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20929 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20931 if (label && label.iconEl) {
20932 label.iconEl.removeClass(label.validClass);
20933 label.iconEl.removeClass(label.invalidClass);
20937 disable : function()
20939 if(this.inputType != 'radio'){
20940 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20947 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20948 _this.getActionEl().addClass(this.disabledClass);
20949 e.dom.disabled = true;
20953 this.disabled = true;
20954 this.fireEvent("disable", this);
20958 enable : function()
20960 if(this.inputType != 'radio'){
20961 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20968 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20969 _this.getActionEl().removeClass(this.disabledClass);
20970 e.dom.disabled = false;
20974 this.disabled = false;
20975 this.fireEvent("enable", this);
20979 setBoxLabel : function(v)
20984 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20990 Roo.apply(Roo.bootstrap.CheckBox, {
20995 * register a CheckBox Group
20996 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20998 register : function(checkbox)
21000 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21001 this.groups[checkbox.groupId] = {};
21004 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21008 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21012 * fetch a CheckBox Group based on the group ID
21013 * @param {string} the group ID
21014 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21016 get: function(groupId) {
21017 if (typeof(this.groups[groupId]) == 'undefined') {
21021 return this.groups[groupId] ;
21034 * @class Roo.bootstrap.Radio
21035 * @extends Roo.bootstrap.Component
21036 * Bootstrap Radio class
21037 * @cfg {String} boxLabel - the label associated
21038 * @cfg {String} value - the value of radio
21041 * Create a new Radio
21042 * @param {Object} config The config object
21044 Roo.bootstrap.Radio = function(config){
21045 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21049 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21055 getAutoCreate : function()
21059 cls : 'form-group radio',
21064 html : this.boxLabel
21072 initEvents : function()
21074 this.parent().register(this);
21076 this.el.on('click', this.onClick, this);
21080 onClick : function(e)
21082 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21083 this.setChecked(true);
21087 setChecked : function(state, suppressEvent)
21089 this.parent().setValue(this.value, suppressEvent);
21093 setBoxLabel : function(v)
21098 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21113 * @class Roo.bootstrap.SecurePass
21114 * @extends Roo.bootstrap.Input
21115 * Bootstrap SecurePass class
21119 * Create a new SecurePass
21120 * @param {Object} config The config object
21123 Roo.bootstrap.SecurePass = function (config) {
21124 // these go here, so the translation tool can replace them..
21126 PwdEmpty: "Please type a password, and then retype it to confirm.",
21127 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21128 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21129 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21130 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21131 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21132 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21133 TooWeak: "Your password is Too Weak."
21135 this.meterLabel = "Password strength:";
21136 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21137 this.meterClass = [
21138 "roo-password-meter-tooweak",
21139 "roo-password-meter-weak",
21140 "roo-password-meter-medium",
21141 "roo-password-meter-strong",
21142 "roo-password-meter-grey"
21147 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21150 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21152 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21154 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21155 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21156 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21157 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21158 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21159 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21160 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21170 * @cfg {String/Object} Label for the strength meter (defaults to
21171 * 'Password strength:')
21176 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21177 * ['Weak', 'Medium', 'Strong'])
21180 pwdStrengths: false,
21193 initEvents: function ()
21195 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21197 if (this.el.is('input[type=password]') && Roo.isSafari) {
21198 this.el.on('keydown', this.SafariOnKeyDown, this);
21201 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21204 onRender: function (ct, position)
21206 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21207 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21208 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21210 this.trigger.createChild({
21215 cls: 'roo-password-meter-grey col-xs-12',
21218 //width: this.meterWidth + 'px'
21222 cls: 'roo-password-meter-text'
21228 if (this.hideTrigger) {
21229 this.trigger.setDisplayed(false);
21231 this.setSize(this.width || '', this.height || '');
21234 onDestroy: function ()
21236 if (this.trigger) {
21237 this.trigger.removeAllListeners();
21238 this.trigger.remove();
21241 this.wrap.remove();
21243 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21246 checkStrength: function ()
21248 var pwd = this.inputEl().getValue();
21249 if (pwd == this._lastPwd) {
21254 if (this.ClientSideStrongPassword(pwd)) {
21256 } else if (this.ClientSideMediumPassword(pwd)) {
21258 } else if (this.ClientSideWeakPassword(pwd)) {
21264 Roo.log('strength1: ' + strength);
21266 //var pm = this.trigger.child('div/div/div').dom;
21267 var pm = this.trigger.child('div/div');
21268 pm.removeClass(this.meterClass);
21269 pm.addClass(this.meterClass[strength]);
21272 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21274 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21276 this._lastPwd = pwd;
21280 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21282 this._lastPwd = '';
21284 var pm = this.trigger.child('div/div');
21285 pm.removeClass(this.meterClass);
21286 pm.addClass('roo-password-meter-grey');
21289 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21292 this.inputEl().dom.type='password';
21295 validateValue: function (value)
21298 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21301 if (value.length == 0) {
21302 if (this.allowBlank) {
21303 this.clearInvalid();
21307 this.markInvalid(this.errors.PwdEmpty);
21308 this.errorMsg = this.errors.PwdEmpty;
21316 if ('[\x21-\x7e]*'.match(value)) {
21317 this.markInvalid(this.errors.PwdBadChar);
21318 this.errorMsg = this.errors.PwdBadChar;
21321 if (value.length < 6) {
21322 this.markInvalid(this.errors.PwdShort);
21323 this.errorMsg = this.errors.PwdShort;
21326 if (value.length > 16) {
21327 this.markInvalid(this.errors.PwdLong);
21328 this.errorMsg = this.errors.PwdLong;
21332 if (this.ClientSideStrongPassword(value)) {
21334 } else if (this.ClientSideMediumPassword(value)) {
21336 } else if (this.ClientSideWeakPassword(value)) {
21343 if (strength < 2) {
21344 //this.markInvalid(this.errors.TooWeak);
21345 this.errorMsg = this.errors.TooWeak;
21350 console.log('strength2: ' + strength);
21352 //var pm = this.trigger.child('div/div/div').dom;
21354 var pm = this.trigger.child('div/div');
21355 pm.removeClass(this.meterClass);
21356 pm.addClass(this.meterClass[strength]);
21358 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21360 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21362 this.errorMsg = '';
21366 CharacterSetChecks: function (type)
21369 this.fResult = false;
21372 isctype: function (character, type)
21375 case this.kCapitalLetter:
21376 if (character >= 'A' && character <= 'Z') {
21381 case this.kSmallLetter:
21382 if (character >= 'a' && character <= 'z') {
21388 if (character >= '0' && character <= '9') {
21393 case this.kPunctuation:
21394 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21405 IsLongEnough: function (pwd, size)
21407 return !(pwd == null || isNaN(size) || pwd.length < size);
21410 SpansEnoughCharacterSets: function (word, nb)
21412 if (!this.IsLongEnough(word, nb))
21417 var characterSetChecks = new Array(
21418 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21419 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21422 for (var index = 0; index < word.length; ++index) {
21423 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21424 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21425 characterSetChecks[nCharSet].fResult = true;
21432 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21433 if (characterSetChecks[nCharSet].fResult) {
21438 if (nCharSets < nb) {
21444 ClientSideStrongPassword: function (pwd)
21446 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21449 ClientSideMediumPassword: function (pwd)
21451 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21454 ClientSideWeakPassword: function (pwd)
21456 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21459 })//<script type="text/javascript">
21462 * Based Ext JS Library 1.1.1
21463 * Copyright(c) 2006-2007, Ext JS, LLC.
21469 * @class Roo.HtmlEditorCore
21470 * @extends Roo.Component
21471 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21473 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21476 Roo.HtmlEditorCore = function(config){
21479 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21484 * @event initialize
21485 * Fires when the editor is fully initialized (including the iframe)
21486 * @param {Roo.HtmlEditorCore} this
21491 * Fires when the editor is first receives the focus. Any insertion must wait
21492 * until after this event.
21493 * @param {Roo.HtmlEditorCore} this
21497 * @event beforesync
21498 * Fires before the textarea is updated with content from the editor iframe. Return false
21499 * to cancel the sync.
21500 * @param {Roo.HtmlEditorCore} this
21501 * @param {String} html
21505 * @event beforepush
21506 * Fires before the iframe editor is updated with content from the textarea. Return false
21507 * to cancel the push.
21508 * @param {Roo.HtmlEditorCore} this
21509 * @param {String} html
21514 * Fires when the textarea is updated with content from the editor iframe.
21515 * @param {Roo.HtmlEditorCore} this
21516 * @param {String} html
21521 * Fires when the iframe editor is updated with content from the textarea.
21522 * @param {Roo.HtmlEditorCore} this
21523 * @param {String} html
21528 * @event editorevent
21529 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21530 * @param {Roo.HtmlEditorCore} this
21536 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21538 // defaults : white / black...
21539 this.applyBlacklists();
21546 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21550 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21556 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21561 * @cfg {Number} height (in pixels)
21565 * @cfg {Number} width (in pixels)
21570 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21573 stylesheets: false,
21578 // private properties
21579 validationEvent : false,
21581 initialized : false,
21583 sourceEditMode : false,
21584 onFocus : Roo.emptyFn,
21586 hideMode:'offsets',
21590 // blacklist + whitelisted elements..
21597 * Protected method that will not generally be called directly. It
21598 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21599 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21601 getDocMarkup : function(){
21605 // inherit styels from page...??
21606 if (this.stylesheets === false) {
21608 Roo.get(document.head).select('style').each(function(node) {
21609 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21612 Roo.get(document.head).select('link').each(function(node) {
21613 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21616 } else if (!this.stylesheets.length) {
21618 st = '<style type="text/css">' +
21619 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21622 st = '<style type="text/css">' +
21627 st += '<style type="text/css">' +
21628 'IMG { cursor: pointer } ' +
21631 var cls = 'roo-htmleditor-body';
21633 if(this.bodyCls.length){
21634 cls += ' ' + this.bodyCls;
21637 return '<html><head>' + st +
21638 //<style type="text/css">' +
21639 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21641 ' </head><body class="' + cls + '"></body></html>';
21645 onRender : function(ct, position)
21648 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21649 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21652 this.el.dom.style.border = '0 none';
21653 this.el.dom.setAttribute('tabIndex', -1);
21654 this.el.addClass('x-hidden hide');
21658 if(Roo.isIE){ // fix IE 1px bogus margin
21659 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21663 this.frameId = Roo.id();
21667 var iframe = this.owner.wrap.createChild({
21669 cls: 'form-control', // bootstrap..
21671 name: this.frameId,
21672 frameBorder : 'no',
21673 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21678 this.iframe = iframe.dom;
21680 this.assignDocWin();
21682 this.doc.designMode = 'on';
21685 this.doc.write(this.getDocMarkup());
21689 var task = { // must defer to wait for browser to be ready
21691 //console.log("run task?" + this.doc.readyState);
21692 this.assignDocWin();
21693 if(this.doc.body || this.doc.readyState == 'complete'){
21695 this.doc.designMode="on";
21699 Roo.TaskMgr.stop(task);
21700 this.initEditor.defer(10, this);
21707 Roo.TaskMgr.start(task);
21712 onResize : function(w, h)
21714 Roo.log('resize: ' +w + ',' + h );
21715 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21719 if(typeof w == 'number'){
21721 this.iframe.style.width = w + 'px';
21723 if(typeof h == 'number'){
21725 this.iframe.style.height = h + 'px';
21727 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21734 * Toggles the editor between standard and source edit mode.
21735 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21737 toggleSourceEdit : function(sourceEditMode){
21739 this.sourceEditMode = sourceEditMode === true;
21741 if(this.sourceEditMode){
21743 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21746 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21747 //this.iframe.className = '';
21750 //this.setSize(this.owner.wrap.getSize());
21751 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21758 * Protected method that will not generally be called directly. If you need/want
21759 * custom HTML cleanup, this is the method you should override.
21760 * @param {String} html The HTML to be cleaned
21761 * return {String} The cleaned HTML
21763 cleanHtml : function(html){
21764 html = String(html);
21765 if(html.length > 5){
21766 if(Roo.isSafari){ // strip safari nonsense
21767 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21770 if(html == ' '){
21777 * HTML Editor -> Textarea
21778 * Protected method that will not generally be called directly. Syncs the contents
21779 * of the editor iframe with the textarea.
21781 syncValue : function(){
21782 if(this.initialized){
21783 var bd = (this.doc.body || this.doc.documentElement);
21784 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21785 var html = bd.innerHTML;
21787 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21788 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21790 html = '<div style="'+m[0]+'">' + html + '</div>';
21793 html = this.cleanHtml(html);
21794 // fix up the special chars.. normaly like back quotes in word...
21795 // however we do not want to do this with chinese..
21796 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21797 var cc = b.charCodeAt();
21799 (cc >= 0x4E00 && cc < 0xA000 ) ||
21800 (cc >= 0x3400 && cc < 0x4E00 ) ||
21801 (cc >= 0xf900 && cc < 0xfb00 )
21807 if(this.owner.fireEvent('beforesync', this, html) !== false){
21808 this.el.dom.value = html;
21809 this.owner.fireEvent('sync', this, html);
21815 * Protected method that will not generally be called directly. Pushes the value of the textarea
21816 * into the iframe editor.
21818 pushValue : function(){
21819 if(this.initialized){
21820 var v = this.el.dom.value.trim();
21822 // if(v.length < 1){
21826 if(this.owner.fireEvent('beforepush', this, v) !== false){
21827 var d = (this.doc.body || this.doc.documentElement);
21829 this.cleanUpPaste();
21830 this.el.dom.value = d.innerHTML;
21831 this.owner.fireEvent('push', this, v);
21837 deferFocus : function(){
21838 this.focus.defer(10, this);
21842 focus : function(){
21843 if(this.win && !this.sourceEditMode){
21850 assignDocWin: function()
21852 var iframe = this.iframe;
21855 this.doc = iframe.contentWindow.document;
21856 this.win = iframe.contentWindow;
21858 // if (!Roo.get(this.frameId)) {
21861 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21862 // this.win = Roo.get(this.frameId).dom.contentWindow;
21864 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21868 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21869 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21874 initEditor : function(){
21875 //console.log("INIT EDITOR");
21876 this.assignDocWin();
21880 this.doc.designMode="on";
21882 this.doc.write(this.getDocMarkup());
21885 var dbody = (this.doc.body || this.doc.documentElement);
21886 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21887 // this copies styles from the containing element into thsi one..
21888 // not sure why we need all of this..
21889 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21891 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21892 //ss['background-attachment'] = 'fixed'; // w3c
21893 dbody.bgProperties = 'fixed'; // ie
21894 //Roo.DomHelper.applyStyles(dbody, ss);
21895 Roo.EventManager.on(this.doc, {
21896 //'mousedown': this.onEditorEvent,
21897 'mouseup': this.onEditorEvent,
21898 'dblclick': this.onEditorEvent,
21899 'click': this.onEditorEvent,
21900 'keyup': this.onEditorEvent,
21905 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21907 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21908 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21910 this.initialized = true;
21912 this.owner.fireEvent('initialize', this);
21917 onDestroy : function(){
21923 //for (var i =0; i < this.toolbars.length;i++) {
21924 // // fixme - ask toolbars for heights?
21925 // this.toolbars[i].onDestroy();
21928 //this.wrap.dom.innerHTML = '';
21929 //this.wrap.remove();
21934 onFirstFocus : function(){
21936 this.assignDocWin();
21939 this.activated = true;
21942 if(Roo.isGecko){ // prevent silly gecko errors
21944 var s = this.win.getSelection();
21945 if(!s.focusNode || s.focusNode.nodeType != 3){
21946 var r = s.getRangeAt(0);
21947 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21952 this.execCmd('useCSS', true);
21953 this.execCmd('styleWithCSS', false);
21956 this.owner.fireEvent('activate', this);
21960 adjustFont: function(btn){
21961 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21962 //if(Roo.isSafari){ // safari
21965 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21966 if(Roo.isSafari){ // safari
21967 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21968 v = (v < 10) ? 10 : v;
21969 v = (v > 48) ? 48 : v;
21970 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21975 v = Math.max(1, v+adjust);
21977 this.execCmd('FontSize', v );
21980 onEditorEvent : function(e)
21982 this.owner.fireEvent('editorevent', this, e);
21983 // this.updateToolbar();
21984 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21987 insertTag : function(tg)
21989 // could be a bit smarter... -> wrap the current selected tRoo..
21990 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21992 range = this.createRange(this.getSelection());
21993 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21994 wrappingNode.appendChild(range.extractContents());
21995 range.insertNode(wrappingNode);
22002 this.execCmd("formatblock", tg);
22006 insertText : function(txt)
22010 var range = this.createRange();
22011 range.deleteContents();
22012 //alert(Sender.getAttribute('label'));
22014 range.insertNode(this.doc.createTextNode(txt));
22020 * Executes a Midas editor command on the editor document and performs necessary focus and
22021 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22022 * @param {String} cmd The Midas command
22023 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22025 relayCmd : function(cmd, value){
22027 this.execCmd(cmd, value);
22028 this.owner.fireEvent('editorevent', this);
22029 //this.updateToolbar();
22030 this.owner.deferFocus();
22034 * Executes a Midas editor command directly on the editor document.
22035 * For visual commands, you should use {@link #relayCmd} instead.
22036 * <b>This should only be called after the editor is initialized.</b>
22037 * @param {String} cmd The Midas command
22038 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22040 execCmd : function(cmd, value){
22041 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22048 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22050 * @param {String} text | dom node..
22052 insertAtCursor : function(text)
22055 if(!this.activated){
22061 var r = this.doc.selection.createRange();
22072 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22076 // from jquery ui (MIT licenced)
22078 var win = this.win;
22080 if (win.getSelection && win.getSelection().getRangeAt) {
22081 range = win.getSelection().getRangeAt(0);
22082 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22083 range.insertNode(node);
22084 } else if (win.document.selection && win.document.selection.createRange) {
22085 // no firefox support
22086 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22087 win.document.selection.createRange().pasteHTML(txt);
22089 // no firefox support
22090 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22091 this.execCmd('InsertHTML', txt);
22100 mozKeyPress : function(e){
22102 var c = e.getCharCode(), cmd;
22105 c = String.fromCharCode(c).toLowerCase();
22119 this.cleanUpPaste.defer(100, this);
22127 e.preventDefault();
22135 fixKeys : function(){ // load time branching for fastest keydown performance
22137 return function(e){
22138 var k = e.getKey(), r;
22141 r = this.doc.selection.createRange();
22144 r.pasteHTML('    ');
22151 r = this.doc.selection.createRange();
22153 var target = r.parentElement();
22154 if(!target || target.tagName.toLowerCase() != 'li'){
22156 r.pasteHTML('<br />');
22162 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22163 this.cleanUpPaste.defer(100, this);
22169 }else if(Roo.isOpera){
22170 return function(e){
22171 var k = e.getKey();
22175 this.execCmd('InsertHTML','    ');
22178 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22179 this.cleanUpPaste.defer(100, this);
22184 }else if(Roo.isSafari){
22185 return function(e){
22186 var k = e.getKey();
22190 this.execCmd('InsertText','\t');
22194 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22195 this.cleanUpPaste.defer(100, this);
22203 getAllAncestors: function()
22205 var p = this.getSelectedNode();
22208 a.push(p); // push blank onto stack..
22209 p = this.getParentElement();
22213 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22217 a.push(this.doc.body);
22221 lastSelNode : false,
22224 getSelection : function()
22226 this.assignDocWin();
22227 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22230 getSelectedNode: function()
22232 // this may only work on Gecko!!!
22234 // should we cache this!!!!
22239 var range = this.createRange(this.getSelection()).cloneRange();
22242 var parent = range.parentElement();
22244 var testRange = range.duplicate();
22245 testRange.moveToElementText(parent);
22246 if (testRange.inRange(range)) {
22249 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22252 parent = parent.parentElement;
22257 // is ancestor a text element.
22258 var ac = range.commonAncestorContainer;
22259 if (ac.nodeType == 3) {
22260 ac = ac.parentNode;
22263 var ar = ac.childNodes;
22266 var other_nodes = [];
22267 var has_other_nodes = false;
22268 for (var i=0;i<ar.length;i++) {
22269 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22272 // fullly contained node.
22274 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22279 // probably selected..
22280 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22281 other_nodes.push(ar[i]);
22285 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22290 has_other_nodes = true;
22292 if (!nodes.length && other_nodes.length) {
22293 nodes= other_nodes;
22295 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22301 createRange: function(sel)
22303 // this has strange effects when using with
22304 // top toolbar - not sure if it's a great idea.
22305 //this.editor.contentWindow.focus();
22306 if (typeof sel != "undefined") {
22308 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22310 return this.doc.createRange();
22313 return this.doc.createRange();
22316 getParentElement: function()
22319 this.assignDocWin();
22320 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22322 var range = this.createRange(sel);
22325 var p = range.commonAncestorContainer;
22326 while (p.nodeType == 3) { // text node
22337 * Range intersection.. the hard stuff...
22341 * [ -- selected range --- ]
22345 * if end is before start or hits it. fail.
22346 * if start is after end or hits it fail.
22348 * if either hits (but other is outside. - then it's not
22354 // @see http://www.thismuchiknow.co.uk/?p=64.
22355 rangeIntersectsNode : function(range, node)
22357 var nodeRange = node.ownerDocument.createRange();
22359 nodeRange.selectNode(node);
22361 nodeRange.selectNodeContents(node);
22364 var rangeStartRange = range.cloneRange();
22365 rangeStartRange.collapse(true);
22367 var rangeEndRange = range.cloneRange();
22368 rangeEndRange.collapse(false);
22370 var nodeStartRange = nodeRange.cloneRange();
22371 nodeStartRange.collapse(true);
22373 var nodeEndRange = nodeRange.cloneRange();
22374 nodeEndRange.collapse(false);
22376 return rangeStartRange.compareBoundaryPoints(
22377 Range.START_TO_START, nodeEndRange) == -1 &&
22378 rangeEndRange.compareBoundaryPoints(
22379 Range.START_TO_START, nodeStartRange) == 1;
22383 rangeCompareNode : function(range, node)
22385 var nodeRange = node.ownerDocument.createRange();
22387 nodeRange.selectNode(node);
22389 nodeRange.selectNodeContents(node);
22393 range.collapse(true);
22395 nodeRange.collapse(true);
22397 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22398 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22400 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22402 var nodeIsBefore = ss == 1;
22403 var nodeIsAfter = ee == -1;
22405 if (nodeIsBefore && nodeIsAfter) {
22408 if (!nodeIsBefore && nodeIsAfter) {
22409 return 1; //right trailed.
22412 if (nodeIsBefore && !nodeIsAfter) {
22413 return 2; // left trailed.
22419 // private? - in a new class?
22420 cleanUpPaste : function()
22422 // cleans up the whole document..
22423 Roo.log('cleanuppaste');
22425 this.cleanUpChildren(this.doc.body);
22426 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22427 if (clean != this.doc.body.innerHTML) {
22428 this.doc.body.innerHTML = clean;
22433 cleanWordChars : function(input) {// change the chars to hex code
22434 var he = Roo.HtmlEditorCore;
22436 var output = input;
22437 Roo.each(he.swapCodes, function(sw) {
22438 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22440 output = output.replace(swapper, sw[1]);
22447 cleanUpChildren : function (n)
22449 if (!n.childNodes.length) {
22452 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22453 this.cleanUpChild(n.childNodes[i]);
22460 cleanUpChild : function (node)
22463 //console.log(node);
22464 if (node.nodeName == "#text") {
22465 // clean up silly Windows -- stuff?
22468 if (node.nodeName == "#comment") {
22469 node.parentNode.removeChild(node);
22470 // clean up silly Windows -- stuff?
22473 var lcname = node.tagName.toLowerCase();
22474 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22475 // whitelist of tags..
22477 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22479 node.parentNode.removeChild(node);
22484 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22486 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22487 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22489 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22490 // remove_keep_children = true;
22493 if (remove_keep_children) {
22494 this.cleanUpChildren(node);
22495 // inserts everything just before this node...
22496 while (node.childNodes.length) {
22497 var cn = node.childNodes[0];
22498 node.removeChild(cn);
22499 node.parentNode.insertBefore(cn, node);
22501 node.parentNode.removeChild(node);
22505 if (!node.attributes || !node.attributes.length) {
22506 this.cleanUpChildren(node);
22510 function cleanAttr(n,v)
22513 if (v.match(/^\./) || v.match(/^\//)) {
22516 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22519 if (v.match(/^#/)) {
22522 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22523 node.removeAttribute(n);
22527 var cwhite = this.cwhite;
22528 var cblack = this.cblack;
22530 function cleanStyle(n,v)
22532 if (v.match(/expression/)) { //XSS?? should we even bother..
22533 node.removeAttribute(n);
22537 var parts = v.split(/;/);
22540 Roo.each(parts, function(p) {
22541 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22545 var l = p.split(':').shift().replace(/\s+/g,'');
22546 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22548 if ( cwhite.length && cblack.indexOf(l) > -1) {
22549 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22550 //node.removeAttribute(n);
22554 // only allow 'c whitelisted system attributes'
22555 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22556 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22557 //node.removeAttribute(n);
22567 if (clean.length) {
22568 node.setAttribute(n, clean.join(';'));
22570 node.removeAttribute(n);
22576 for (var i = node.attributes.length-1; i > -1 ; i--) {
22577 var a = node.attributes[i];
22580 if (a.name.toLowerCase().substr(0,2)=='on') {
22581 node.removeAttribute(a.name);
22584 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22585 node.removeAttribute(a.name);
22588 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22589 cleanAttr(a.name,a.value); // fixme..
22592 if (a.name == 'style') {
22593 cleanStyle(a.name,a.value);
22596 /// clean up MS crap..
22597 // tecnically this should be a list of valid class'es..
22600 if (a.name == 'class') {
22601 if (a.value.match(/^Mso/)) {
22602 node.className = '';
22605 if (a.value.match(/^body$/)) {
22606 node.className = '';
22617 this.cleanUpChildren(node);
22623 * Clean up MS wordisms...
22625 cleanWord : function(node)
22630 this.cleanWord(this.doc.body);
22633 if (node.nodeName == "#text") {
22634 // clean up silly Windows -- stuff?
22637 if (node.nodeName == "#comment") {
22638 node.parentNode.removeChild(node);
22639 // clean up silly Windows -- stuff?
22643 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22644 node.parentNode.removeChild(node);
22648 // remove - but keep children..
22649 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22650 while (node.childNodes.length) {
22651 var cn = node.childNodes[0];
22652 node.removeChild(cn);
22653 node.parentNode.insertBefore(cn, node);
22655 node.parentNode.removeChild(node);
22656 this.iterateChildren(node, this.cleanWord);
22660 if (node.className.length) {
22662 var cn = node.className.split(/\W+/);
22664 Roo.each(cn, function(cls) {
22665 if (cls.match(/Mso[a-zA-Z]+/)) {
22670 node.className = cna.length ? cna.join(' ') : '';
22672 node.removeAttribute("class");
22676 if (node.hasAttribute("lang")) {
22677 node.removeAttribute("lang");
22680 if (node.hasAttribute("style")) {
22682 var styles = node.getAttribute("style").split(";");
22684 Roo.each(styles, function(s) {
22685 if (!s.match(/:/)) {
22688 var kv = s.split(":");
22689 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22692 // what ever is left... we allow.
22695 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22696 if (!nstyle.length) {
22697 node.removeAttribute('style');
22700 this.iterateChildren(node, this.cleanWord);
22706 * iterateChildren of a Node, calling fn each time, using this as the scole..
22707 * @param {DomNode} node node to iterate children of.
22708 * @param {Function} fn method of this class to call on each item.
22710 iterateChildren : function(node, fn)
22712 if (!node.childNodes.length) {
22715 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22716 fn.call(this, node.childNodes[i])
22722 * cleanTableWidths.
22724 * Quite often pasting from word etc.. results in tables with column and widths.
22725 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22728 cleanTableWidths : function(node)
22733 this.cleanTableWidths(this.doc.body);
22738 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22741 Roo.log(node.tagName);
22742 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22743 this.iterateChildren(node, this.cleanTableWidths);
22746 if (node.hasAttribute('width')) {
22747 node.removeAttribute('width');
22751 if (node.hasAttribute("style")) {
22754 var styles = node.getAttribute("style").split(";");
22756 Roo.each(styles, function(s) {
22757 if (!s.match(/:/)) {
22760 var kv = s.split(":");
22761 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22764 // what ever is left... we allow.
22767 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22768 if (!nstyle.length) {
22769 node.removeAttribute('style');
22773 this.iterateChildren(node, this.cleanTableWidths);
22781 domToHTML : function(currentElement, depth, nopadtext) {
22783 depth = depth || 0;
22784 nopadtext = nopadtext || false;
22786 if (!currentElement) {
22787 return this.domToHTML(this.doc.body);
22790 //Roo.log(currentElement);
22792 var allText = false;
22793 var nodeName = currentElement.nodeName;
22794 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22796 if (nodeName == '#text') {
22798 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22803 if (nodeName != 'BODY') {
22806 // Prints the node tagName, such as <A>, <IMG>, etc
22809 for(i = 0; i < currentElement.attributes.length;i++) {
22811 var aname = currentElement.attributes.item(i).name;
22812 if (!currentElement.attributes.item(i).value.length) {
22815 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22818 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22827 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22830 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22835 // Traverse the tree
22837 var currentElementChild = currentElement.childNodes.item(i);
22838 var allText = true;
22839 var innerHTML = '';
22841 while (currentElementChild) {
22842 // Formatting code (indent the tree so it looks nice on the screen)
22843 var nopad = nopadtext;
22844 if (lastnode == 'SPAN') {
22848 if (currentElementChild.nodeName == '#text') {
22849 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22850 toadd = nopadtext ? toadd : toadd.trim();
22851 if (!nopad && toadd.length > 80) {
22852 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22854 innerHTML += toadd;
22857 currentElementChild = currentElement.childNodes.item(i);
22863 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22865 // Recursively traverse the tree structure of the child node
22866 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22867 lastnode = currentElementChild.nodeName;
22869 currentElementChild=currentElement.childNodes.item(i);
22875 // The remaining code is mostly for formatting the tree
22876 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22881 ret+= "</"+tagName+">";
22887 applyBlacklists : function()
22889 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22890 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22894 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22895 if (b.indexOf(tag) > -1) {
22898 this.white.push(tag);
22902 Roo.each(w, function(tag) {
22903 if (b.indexOf(tag) > -1) {
22906 if (this.white.indexOf(tag) > -1) {
22909 this.white.push(tag);
22914 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22915 if (w.indexOf(tag) > -1) {
22918 this.black.push(tag);
22922 Roo.each(b, function(tag) {
22923 if (w.indexOf(tag) > -1) {
22926 if (this.black.indexOf(tag) > -1) {
22929 this.black.push(tag);
22934 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22935 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22939 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22940 if (b.indexOf(tag) > -1) {
22943 this.cwhite.push(tag);
22947 Roo.each(w, function(tag) {
22948 if (b.indexOf(tag) > -1) {
22951 if (this.cwhite.indexOf(tag) > -1) {
22954 this.cwhite.push(tag);
22959 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22960 if (w.indexOf(tag) > -1) {
22963 this.cblack.push(tag);
22967 Roo.each(b, function(tag) {
22968 if (w.indexOf(tag) > -1) {
22971 if (this.cblack.indexOf(tag) > -1) {
22974 this.cblack.push(tag);
22979 setStylesheets : function(stylesheets)
22981 if(typeof(stylesheets) == 'string'){
22982 Roo.get(this.iframe.contentDocument.head).createChild({
22984 rel : 'stylesheet',
22993 Roo.each(stylesheets, function(s) {
22998 Roo.get(_this.iframe.contentDocument.head).createChild({
23000 rel : 'stylesheet',
23009 removeStylesheets : function()
23013 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23018 setStyle : function(style)
23020 Roo.get(this.iframe.contentDocument.head).createChild({
23029 // hide stuff that is not compatible
23043 * @event specialkey
23047 * @cfg {String} fieldClass @hide
23050 * @cfg {String} focusClass @hide
23053 * @cfg {String} autoCreate @hide
23056 * @cfg {String} inputType @hide
23059 * @cfg {String} invalidClass @hide
23062 * @cfg {String} invalidText @hide
23065 * @cfg {String} msgFx @hide
23068 * @cfg {String} validateOnBlur @hide
23072 Roo.HtmlEditorCore.white = [
23073 'area', 'br', 'img', 'input', 'hr', 'wbr',
23075 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23076 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23077 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23078 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23079 'table', 'ul', 'xmp',
23081 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23084 'dir', 'menu', 'ol', 'ul', 'dl',
23090 Roo.HtmlEditorCore.black = [
23091 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23093 'base', 'basefont', 'bgsound', 'blink', 'body',
23094 'frame', 'frameset', 'head', 'html', 'ilayer',
23095 'iframe', 'layer', 'link', 'meta', 'object',
23096 'script', 'style' ,'title', 'xml' // clean later..
23098 Roo.HtmlEditorCore.clean = [
23099 'script', 'style', 'title', 'xml'
23101 Roo.HtmlEditorCore.remove = [
23106 Roo.HtmlEditorCore.ablack = [
23110 Roo.HtmlEditorCore.aclean = [
23111 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23115 Roo.HtmlEditorCore.pwhite= [
23116 'http', 'https', 'mailto'
23119 // white listed style attributes.
23120 Roo.HtmlEditorCore.cwhite= [
23121 // 'text-align', /// default is to allow most things..
23127 // black listed style attributes.
23128 Roo.HtmlEditorCore.cblack= [
23129 // 'font-size' -- this can be set by the project
23133 Roo.HtmlEditorCore.swapCodes =[
23152 * @class Roo.bootstrap.HtmlEditor
23153 * @extends Roo.bootstrap.TextArea
23154 * Bootstrap HtmlEditor class
23157 * Create a new HtmlEditor
23158 * @param {Object} config The config object
23161 Roo.bootstrap.HtmlEditor = function(config){
23162 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23163 if (!this.toolbars) {
23164 this.toolbars = [];
23167 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23170 * @event initialize
23171 * Fires when the editor is fully initialized (including the iframe)
23172 * @param {HtmlEditor} this
23177 * Fires when the editor is first receives the focus. Any insertion must wait
23178 * until after this event.
23179 * @param {HtmlEditor} this
23183 * @event beforesync
23184 * Fires before the textarea is updated with content from the editor iframe. Return false
23185 * to cancel the sync.
23186 * @param {HtmlEditor} this
23187 * @param {String} html
23191 * @event beforepush
23192 * Fires before the iframe editor is updated with content from the textarea. Return false
23193 * to cancel the push.
23194 * @param {HtmlEditor} this
23195 * @param {String} html
23200 * Fires when the textarea is updated with content from the editor iframe.
23201 * @param {HtmlEditor} this
23202 * @param {String} html
23207 * Fires when the iframe editor is updated with content from the textarea.
23208 * @param {HtmlEditor} this
23209 * @param {String} html
23213 * @event editmodechange
23214 * Fires when the editor switches edit modes
23215 * @param {HtmlEditor} this
23216 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23218 editmodechange: true,
23220 * @event editorevent
23221 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23222 * @param {HtmlEditor} this
23226 * @event firstfocus
23227 * Fires when on first focus - needed by toolbars..
23228 * @param {HtmlEditor} this
23233 * Auto save the htmlEditor value as a file into Events
23234 * @param {HtmlEditor} this
23238 * @event savedpreview
23239 * preview the saved version of htmlEditor
23240 * @param {HtmlEditor} this
23247 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23251 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23256 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23261 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23266 * @cfg {Number} height (in pixels)
23270 * @cfg {Number} width (in pixels)
23275 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23278 stylesheets: false,
23283 // private properties
23284 validationEvent : false,
23286 initialized : false,
23289 onFocus : Roo.emptyFn,
23291 hideMode:'offsets',
23293 tbContainer : false,
23297 toolbarContainer :function() {
23298 return this.wrap.select('.x-html-editor-tb',true).first();
23302 * Protected method that will not generally be called directly. It
23303 * is called when the editor creates its toolbar. Override this method if you need to
23304 * add custom toolbar buttons.
23305 * @param {HtmlEditor} editor
23307 createToolbar : function(){
23308 Roo.log('renewing');
23309 Roo.log("create toolbars");
23311 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23312 this.toolbars[0].render(this.toolbarContainer());
23316 // if (!editor.toolbars || !editor.toolbars.length) {
23317 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23320 // for (var i =0 ; i < editor.toolbars.length;i++) {
23321 // editor.toolbars[i] = Roo.factory(
23322 // typeof(editor.toolbars[i]) == 'string' ?
23323 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23324 // Roo.bootstrap.HtmlEditor);
23325 // editor.toolbars[i].init(editor);
23331 onRender : function(ct, position)
23333 // Roo.log("Call onRender: " + this.xtype);
23335 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23337 this.wrap = this.inputEl().wrap({
23338 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23341 this.editorcore.onRender(ct, position);
23343 if (this.resizable) {
23344 this.resizeEl = new Roo.Resizable(this.wrap, {
23348 minHeight : this.height,
23349 height: this.height,
23350 handles : this.resizable,
23353 resize : function(r, w, h) {
23354 _t.onResize(w,h); // -something
23360 this.createToolbar(this);
23363 if(!this.width && this.resizable){
23364 this.setSize(this.wrap.getSize());
23366 if (this.resizeEl) {
23367 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23368 // should trigger onReize..
23374 onResize : function(w, h)
23376 Roo.log('resize: ' +w + ',' + h );
23377 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23381 if(this.inputEl() ){
23382 if(typeof w == 'number'){
23383 var aw = w - this.wrap.getFrameWidth('lr');
23384 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23387 if(typeof h == 'number'){
23388 var tbh = -11; // fixme it needs to tool bar size!
23389 for (var i =0; i < this.toolbars.length;i++) {
23390 // fixme - ask toolbars for heights?
23391 tbh += this.toolbars[i].el.getHeight();
23392 //if (this.toolbars[i].footer) {
23393 // tbh += this.toolbars[i].footer.el.getHeight();
23401 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23402 ah -= 5; // knock a few pixes off for look..
23403 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23407 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23408 this.editorcore.onResize(ew,eh);
23413 * Toggles the editor between standard and source edit mode.
23414 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23416 toggleSourceEdit : function(sourceEditMode)
23418 this.editorcore.toggleSourceEdit(sourceEditMode);
23420 if(this.editorcore.sourceEditMode){
23421 Roo.log('editor - showing textarea');
23424 // Roo.log(this.syncValue());
23426 this.inputEl().removeClass(['hide', 'x-hidden']);
23427 this.inputEl().dom.removeAttribute('tabIndex');
23428 this.inputEl().focus();
23430 Roo.log('editor - hiding textarea');
23432 // Roo.log(this.pushValue());
23435 this.inputEl().addClass(['hide', 'x-hidden']);
23436 this.inputEl().dom.setAttribute('tabIndex', -1);
23437 //this.deferFocus();
23440 if(this.resizable){
23441 this.setSize(this.wrap.getSize());
23444 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23447 // private (for BoxComponent)
23448 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23450 // private (for BoxComponent)
23451 getResizeEl : function(){
23455 // private (for BoxComponent)
23456 getPositionEl : function(){
23461 initEvents : function(){
23462 this.originalValue = this.getValue();
23466 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23469 // markInvalid : Roo.emptyFn,
23471 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23474 // clearInvalid : Roo.emptyFn,
23476 setValue : function(v){
23477 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23478 this.editorcore.pushValue();
23483 deferFocus : function(){
23484 this.focus.defer(10, this);
23488 focus : function(){
23489 this.editorcore.focus();
23495 onDestroy : function(){
23501 for (var i =0; i < this.toolbars.length;i++) {
23502 // fixme - ask toolbars for heights?
23503 this.toolbars[i].onDestroy();
23506 this.wrap.dom.innerHTML = '';
23507 this.wrap.remove();
23512 onFirstFocus : function(){
23513 //Roo.log("onFirstFocus");
23514 this.editorcore.onFirstFocus();
23515 for (var i =0; i < this.toolbars.length;i++) {
23516 this.toolbars[i].onFirstFocus();
23522 syncValue : function()
23524 this.editorcore.syncValue();
23527 pushValue : function()
23529 this.editorcore.pushValue();
23533 // hide stuff that is not compatible
23547 * @event specialkey
23551 * @cfg {String} fieldClass @hide
23554 * @cfg {String} focusClass @hide
23557 * @cfg {String} autoCreate @hide
23560 * @cfg {String} inputType @hide
23563 * @cfg {String} invalidClass @hide
23566 * @cfg {String} invalidText @hide
23569 * @cfg {String} msgFx @hide
23572 * @cfg {String} validateOnBlur @hide
23581 Roo.namespace('Roo.bootstrap.htmleditor');
23583 * @class Roo.bootstrap.HtmlEditorToolbar1
23588 new Roo.bootstrap.HtmlEditor({
23591 new Roo.bootstrap.HtmlEditorToolbar1({
23592 disable : { fonts: 1 , format: 1, ..., ... , ...],
23598 * @cfg {Object} disable List of elements to disable..
23599 * @cfg {Array} btns List of additional buttons.
23603 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23606 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23609 Roo.apply(this, config);
23611 // default disabled, based on 'good practice'..
23612 this.disable = this.disable || {};
23613 Roo.applyIf(this.disable, {
23616 specialElements : true
23618 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23620 this.editor = config.editor;
23621 this.editorcore = config.editor.editorcore;
23623 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23625 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23626 // dont call parent... till later.
23628 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23633 editorcore : false,
23638 "h1","h2","h3","h4","h5","h6",
23640 "abbr", "acronym", "address", "cite", "samp", "var",
23644 onRender : function(ct, position)
23646 // Roo.log("Call onRender: " + this.xtype);
23648 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23650 this.el.dom.style.marginBottom = '0';
23652 var editorcore = this.editorcore;
23653 var editor= this.editor;
23656 var btn = function(id,cmd , toggle, handler, html){
23658 var event = toggle ? 'toggle' : 'click';
23663 xns: Roo.bootstrap,
23666 enableToggle:toggle !== false,
23668 pressed : toggle ? false : null,
23671 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23672 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23678 // var cb_box = function...
23683 xns: Roo.bootstrap,
23684 glyphicon : 'font',
23688 xns: Roo.bootstrap,
23692 Roo.each(this.formats, function(f) {
23693 style.menu.items.push({
23695 xns: Roo.bootstrap,
23696 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23701 editorcore.insertTag(this.tagname);
23708 children.push(style);
23710 btn('bold',false,true);
23711 btn('italic',false,true);
23712 btn('align-left', 'justifyleft',true);
23713 btn('align-center', 'justifycenter',true);
23714 btn('align-right' , 'justifyright',true);
23715 btn('link', false, false, function(btn) {
23716 //Roo.log("create link?");
23717 var url = prompt(this.createLinkText, this.defaultLinkValue);
23718 if(url && url != 'http:/'+'/'){
23719 this.editorcore.relayCmd('createlink', url);
23722 btn('list','insertunorderedlist',true);
23723 btn('pencil', false,true, function(btn){
23725 this.toggleSourceEdit(btn.pressed);
23728 if (this.editor.btns.length > 0) {
23729 for (var i = 0; i<this.editor.btns.length; i++) {
23730 children.push(this.editor.btns[i]);
23738 xns: Roo.bootstrap,
23743 xns: Roo.bootstrap,
23748 cog.menu.items.push({
23750 xns: Roo.bootstrap,
23751 html : Clean styles,
23756 editorcore.insertTag(this.tagname);
23765 this.xtype = 'NavSimplebar';
23767 for(var i=0;i< children.length;i++) {
23769 this.buttons.add(this.addxtypeChild(children[i]));
23773 editor.on('editorevent', this.updateToolbar, this);
23775 onBtnClick : function(id)
23777 this.editorcore.relayCmd(id);
23778 this.editorcore.focus();
23782 * Protected method that will not generally be called directly. It triggers
23783 * a toolbar update by reading the markup state of the current selection in the editor.
23785 updateToolbar: function(){
23787 if(!this.editorcore.activated){
23788 this.editor.onFirstFocus(); // is this neeed?
23792 var btns = this.buttons;
23793 var doc = this.editorcore.doc;
23794 btns.get('bold').setActive(doc.queryCommandState('bold'));
23795 btns.get('italic').setActive(doc.queryCommandState('italic'));
23796 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23798 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23799 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23800 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23802 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23803 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23806 var ans = this.editorcore.getAllAncestors();
23807 if (this.formatCombo) {
23810 var store = this.formatCombo.store;
23811 this.formatCombo.setValue("");
23812 for (var i =0; i < ans.length;i++) {
23813 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23815 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23823 // hides menus... - so this cant be on a menu...
23824 Roo.bootstrap.MenuMgr.hideAll();
23826 Roo.bootstrap.MenuMgr.hideAll();
23827 //this.editorsyncValue();
23829 onFirstFocus: function() {
23830 this.buttons.each(function(item){
23834 toggleSourceEdit : function(sourceEditMode){
23837 if(sourceEditMode){
23838 Roo.log("disabling buttons");
23839 this.buttons.each( function(item){
23840 if(item.cmd != 'pencil'){
23846 Roo.log("enabling buttons");
23847 if(this.editorcore.initialized){
23848 this.buttons.each( function(item){
23854 Roo.log("calling toggole on editor");
23855 // tell the editor that it's been pressed..
23856 this.editor.toggleSourceEdit(sourceEditMode);
23866 * @class Roo.bootstrap.Table.AbstractSelectionModel
23867 * @extends Roo.util.Observable
23868 * Abstract base class for grid SelectionModels. It provides the interface that should be
23869 * implemented by descendant classes. This class should not be directly instantiated.
23872 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23873 this.locked = false;
23874 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23878 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23879 /** @ignore Called by the grid automatically. Do not call directly. */
23880 init : function(grid){
23886 * Locks the selections.
23889 this.locked = true;
23893 * Unlocks the selections.
23895 unlock : function(){
23896 this.locked = false;
23900 * Returns true if the selections are locked.
23901 * @return {Boolean}
23903 isLocked : function(){
23904 return this.locked;
23908 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23909 * @class Roo.bootstrap.Table.RowSelectionModel
23910 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23911 * It supports multiple selections and keyboard selection/navigation.
23913 * @param {Object} config
23916 Roo.bootstrap.Table.RowSelectionModel = function(config){
23917 Roo.apply(this, config);
23918 this.selections = new Roo.util.MixedCollection(false, function(o){
23923 this.lastActive = false;
23927 * @event selectionchange
23928 * Fires when the selection changes
23929 * @param {SelectionModel} this
23931 "selectionchange" : true,
23933 * @event afterselectionchange
23934 * Fires after the selection changes (eg. by key press or clicking)
23935 * @param {SelectionModel} this
23937 "afterselectionchange" : true,
23939 * @event beforerowselect
23940 * Fires when a row is selected being selected, return false to cancel.
23941 * @param {SelectionModel} this
23942 * @param {Number} rowIndex The selected index
23943 * @param {Boolean} keepExisting False if other selections will be cleared
23945 "beforerowselect" : true,
23948 * Fires when a row is selected.
23949 * @param {SelectionModel} this
23950 * @param {Number} rowIndex The selected index
23951 * @param {Roo.data.Record} r The record
23953 "rowselect" : true,
23955 * @event rowdeselect
23956 * Fires when a row is deselected.
23957 * @param {SelectionModel} this
23958 * @param {Number} rowIndex The selected index
23960 "rowdeselect" : true
23962 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23963 this.locked = false;
23966 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23968 * @cfg {Boolean} singleSelect
23969 * True to allow selection of only one row at a time (defaults to false)
23971 singleSelect : false,
23974 initEvents : function()
23977 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23978 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23979 //}else{ // allow click to work like normal
23980 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23982 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23983 this.grid.on("rowclick", this.handleMouseDown, this);
23985 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23986 "up" : function(e){
23988 this.selectPrevious(e.shiftKey);
23989 }else if(this.last !== false && this.lastActive !== false){
23990 var last = this.last;
23991 this.selectRange(this.last, this.lastActive-1);
23992 this.grid.getView().focusRow(this.lastActive);
23993 if(last !== false){
23997 this.selectFirstRow();
23999 this.fireEvent("afterselectionchange", this);
24001 "down" : function(e){
24003 this.selectNext(e.shiftKey);
24004 }else if(this.last !== false && this.lastActive !== false){
24005 var last = this.last;
24006 this.selectRange(this.last, this.lastActive+1);
24007 this.grid.getView().focusRow(this.lastActive);
24008 if(last !== false){
24012 this.selectFirstRow();
24014 this.fireEvent("afterselectionchange", this);
24018 this.grid.store.on('load', function(){
24019 this.selections.clear();
24022 var view = this.grid.view;
24023 view.on("refresh", this.onRefresh, this);
24024 view.on("rowupdated", this.onRowUpdated, this);
24025 view.on("rowremoved", this.onRemove, this);
24030 onRefresh : function()
24032 var ds = this.grid.store, i, v = this.grid.view;
24033 var s = this.selections;
24034 s.each(function(r){
24035 if((i = ds.indexOfId(r.id)) != -1){
24044 onRemove : function(v, index, r){
24045 this.selections.remove(r);
24049 onRowUpdated : function(v, index, r){
24050 if(this.isSelected(r)){
24051 v.onRowSelect(index);
24057 * @param {Array} records The records to select
24058 * @param {Boolean} keepExisting (optional) True to keep existing selections
24060 selectRecords : function(records, keepExisting)
24063 this.clearSelections();
24065 var ds = this.grid.store;
24066 for(var i = 0, len = records.length; i < len; i++){
24067 this.selectRow(ds.indexOf(records[i]), true);
24072 * Gets the number of selected rows.
24075 getCount : function(){
24076 return this.selections.length;
24080 * Selects the first row in the grid.
24082 selectFirstRow : function(){
24087 * Select the last row.
24088 * @param {Boolean} keepExisting (optional) True to keep existing selections
24090 selectLastRow : function(keepExisting){
24091 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24092 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24096 * Selects the row immediately following the last selected row.
24097 * @param {Boolean} keepExisting (optional) True to keep existing selections
24099 selectNext : function(keepExisting)
24101 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24102 this.selectRow(this.last+1, keepExisting);
24103 this.grid.getView().focusRow(this.last);
24108 * Selects the row that precedes the last selected row.
24109 * @param {Boolean} keepExisting (optional) True to keep existing selections
24111 selectPrevious : function(keepExisting){
24113 this.selectRow(this.last-1, keepExisting);
24114 this.grid.getView().focusRow(this.last);
24119 * Returns the selected records
24120 * @return {Array} Array of selected records
24122 getSelections : function(){
24123 return [].concat(this.selections.items);
24127 * Returns the first selected record.
24130 getSelected : function(){
24131 return this.selections.itemAt(0);
24136 * Clears all selections.
24138 clearSelections : function(fast)
24144 var ds = this.grid.store;
24145 var s = this.selections;
24146 s.each(function(r){
24147 this.deselectRow(ds.indexOfId(r.id));
24151 this.selections.clear();
24158 * Selects all rows.
24160 selectAll : function(){
24164 this.selections.clear();
24165 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24166 this.selectRow(i, true);
24171 * Returns True if there is a selection.
24172 * @return {Boolean}
24174 hasSelection : function(){
24175 return this.selections.length > 0;
24179 * Returns True if the specified row is selected.
24180 * @param {Number/Record} record The record or index of the record to check
24181 * @return {Boolean}
24183 isSelected : function(index){
24184 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24185 return (r && this.selections.key(r.id) ? true : false);
24189 * Returns True if the specified record id is selected.
24190 * @param {String} id The id of record to check
24191 * @return {Boolean}
24193 isIdSelected : function(id){
24194 return (this.selections.key(id) ? true : false);
24199 handleMouseDBClick : function(e, t){
24203 handleMouseDown : function(e, t)
24205 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24206 if(this.isLocked() || rowIndex < 0 ){
24209 if(e.shiftKey && this.last !== false){
24210 var last = this.last;
24211 this.selectRange(last, rowIndex, e.ctrlKey);
24212 this.last = last; // reset the last
24216 var isSelected = this.isSelected(rowIndex);
24217 //Roo.log("select row:" + rowIndex);
24219 this.deselectRow(rowIndex);
24221 this.selectRow(rowIndex, true);
24225 if(e.button !== 0 && isSelected){
24226 alert('rowIndex 2: ' + rowIndex);
24227 view.focusRow(rowIndex);
24228 }else if(e.ctrlKey && isSelected){
24229 this.deselectRow(rowIndex);
24230 }else if(!isSelected){
24231 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24232 view.focusRow(rowIndex);
24236 this.fireEvent("afterselectionchange", this);
24239 handleDragableRowClick : function(grid, rowIndex, e)
24241 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24242 this.selectRow(rowIndex, false);
24243 grid.view.focusRow(rowIndex);
24244 this.fireEvent("afterselectionchange", this);
24249 * Selects multiple rows.
24250 * @param {Array} rows Array of the indexes of the row to select
24251 * @param {Boolean} keepExisting (optional) True to keep existing selections
24253 selectRows : function(rows, keepExisting){
24255 this.clearSelections();
24257 for(var i = 0, len = rows.length; i < len; i++){
24258 this.selectRow(rows[i], true);
24263 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24264 * @param {Number} startRow The index of the first row in the range
24265 * @param {Number} endRow The index of the last row in the range
24266 * @param {Boolean} keepExisting (optional) True to retain existing selections
24268 selectRange : function(startRow, endRow, keepExisting){
24273 this.clearSelections();
24275 if(startRow <= endRow){
24276 for(var i = startRow; i <= endRow; i++){
24277 this.selectRow(i, true);
24280 for(var i = startRow; i >= endRow; i--){
24281 this.selectRow(i, true);
24287 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24288 * @param {Number} startRow The index of the first row in the range
24289 * @param {Number} endRow The index of the last row in the range
24291 deselectRange : function(startRow, endRow, preventViewNotify){
24295 for(var i = startRow; i <= endRow; i++){
24296 this.deselectRow(i, preventViewNotify);
24302 * @param {Number} row The index of the row to select
24303 * @param {Boolean} keepExisting (optional) True to keep existing selections
24305 selectRow : function(index, keepExisting, preventViewNotify)
24307 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24310 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24311 if(!keepExisting || this.singleSelect){
24312 this.clearSelections();
24315 var r = this.grid.store.getAt(index);
24316 //console.log('selectRow - record id :' + r.id);
24318 this.selections.add(r);
24319 this.last = this.lastActive = index;
24320 if(!preventViewNotify){
24321 var proxy = new Roo.Element(
24322 this.grid.getRowDom(index)
24324 proxy.addClass('bg-info info');
24326 this.fireEvent("rowselect", this, index, r);
24327 this.fireEvent("selectionchange", this);
24333 * @param {Number} row The index of the row to deselect
24335 deselectRow : function(index, preventViewNotify)
24340 if(this.last == index){
24343 if(this.lastActive == index){
24344 this.lastActive = false;
24347 var r = this.grid.store.getAt(index);
24352 this.selections.remove(r);
24353 //.console.log('deselectRow - record id :' + r.id);
24354 if(!preventViewNotify){
24356 var proxy = new Roo.Element(
24357 this.grid.getRowDom(index)
24359 proxy.removeClass('bg-info info');
24361 this.fireEvent("rowdeselect", this, index);
24362 this.fireEvent("selectionchange", this);
24366 restoreLast : function(){
24368 this.last = this._last;
24373 acceptsNav : function(row, col, cm){
24374 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24378 onEditorKey : function(field, e){
24379 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24384 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24386 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24388 }else if(k == e.ENTER && !e.ctrlKey){
24392 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24394 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24396 }else if(k == e.ESC){
24400 g.startEditing(newCell[0], newCell[1]);
24406 * Ext JS Library 1.1.1
24407 * Copyright(c) 2006-2007, Ext JS, LLC.
24409 * Originally Released Under LGPL - original licence link has changed is not relivant.
24412 * <script type="text/javascript">
24416 * @class Roo.bootstrap.PagingToolbar
24417 * @extends Roo.bootstrap.NavSimplebar
24418 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24420 * Create a new PagingToolbar
24421 * @param {Object} config The config object
24422 * @param {Roo.data.Store} store
24424 Roo.bootstrap.PagingToolbar = function(config)
24426 // old args format still supported... - xtype is prefered..
24427 // created from xtype...
24429 this.ds = config.dataSource;
24431 if (config.store && !this.ds) {
24432 this.store= Roo.factory(config.store, Roo.data);
24433 this.ds = this.store;
24434 this.ds.xmodule = this.xmodule || false;
24437 this.toolbarItems = [];
24438 if (config.items) {
24439 this.toolbarItems = config.items;
24442 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24447 this.bind(this.ds);
24450 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24454 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24456 * @cfg {Roo.data.Store} dataSource
24457 * The underlying data store providing the paged data
24460 * @cfg {String/HTMLElement/Element} container
24461 * container The id or element that will contain the toolbar
24464 * @cfg {Boolean} displayInfo
24465 * True to display the displayMsg (defaults to false)
24468 * @cfg {Number} pageSize
24469 * The number of records to display per page (defaults to 20)
24473 * @cfg {String} displayMsg
24474 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24476 displayMsg : 'Displaying {0} - {1} of {2}',
24478 * @cfg {String} emptyMsg
24479 * The message to display when no records are found (defaults to "No data to display")
24481 emptyMsg : 'No data to display',
24483 * Customizable piece of the default paging text (defaults to "Page")
24486 beforePageText : "Page",
24488 * Customizable piece of the default paging text (defaults to "of %0")
24491 afterPageText : "of {0}",
24493 * Customizable piece of the default paging text (defaults to "First Page")
24496 firstText : "First Page",
24498 * Customizable piece of the default paging text (defaults to "Previous Page")
24501 prevText : "Previous Page",
24503 * Customizable piece of the default paging text (defaults to "Next Page")
24506 nextText : "Next Page",
24508 * Customizable piece of the default paging text (defaults to "Last Page")
24511 lastText : "Last Page",
24513 * Customizable piece of the default paging text (defaults to "Refresh")
24516 refreshText : "Refresh",
24520 onRender : function(ct, position)
24522 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24523 this.navgroup.parentId = this.id;
24524 this.navgroup.onRender(this.el, null);
24525 // add the buttons to the navgroup
24527 if(this.displayInfo){
24528 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24529 this.displayEl = this.el.select('.x-paging-info', true).first();
24530 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24531 // this.displayEl = navel.el.select('span',true).first();
24537 Roo.each(_this.buttons, function(e){ // this might need to use render????
24538 Roo.factory(e).render(_this.el);
24542 Roo.each(_this.toolbarItems, function(e) {
24543 _this.navgroup.addItem(e);
24547 this.first = this.navgroup.addItem({
24548 tooltip: this.firstText,
24550 icon : 'fa fa-backward',
24552 preventDefault: true,
24553 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24556 this.prev = this.navgroup.addItem({
24557 tooltip: this.prevText,
24559 icon : 'fa fa-step-backward',
24561 preventDefault: true,
24562 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24564 //this.addSeparator();
24567 var field = this.navgroup.addItem( {
24569 cls : 'x-paging-position',
24571 html : this.beforePageText +
24572 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24573 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24576 this.field = field.el.select('input', true).first();
24577 this.field.on("keydown", this.onPagingKeydown, this);
24578 this.field.on("focus", function(){this.dom.select();});
24581 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24582 //this.field.setHeight(18);
24583 //this.addSeparator();
24584 this.next = this.navgroup.addItem({
24585 tooltip: this.nextText,
24587 html : ' <i class="fa fa-step-forward">',
24589 preventDefault: true,
24590 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24592 this.last = this.navgroup.addItem({
24593 tooltip: this.lastText,
24594 icon : 'fa fa-forward',
24597 preventDefault: true,
24598 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24600 //this.addSeparator();
24601 this.loading = this.navgroup.addItem({
24602 tooltip: this.refreshText,
24603 icon: 'fa fa-refresh',
24604 preventDefault: true,
24605 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24611 updateInfo : function(){
24612 if(this.displayEl){
24613 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24614 var msg = count == 0 ?
24618 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24620 this.displayEl.update(msg);
24625 onLoad : function(ds, r, o)
24627 this.cursor = o.params.start ? o.params.start : 0;
24629 var d = this.getPageData(),
24634 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24635 this.field.dom.value = ap;
24636 this.first.setDisabled(ap == 1);
24637 this.prev.setDisabled(ap == 1);
24638 this.next.setDisabled(ap == ps);
24639 this.last.setDisabled(ap == ps);
24640 this.loading.enable();
24645 getPageData : function(){
24646 var total = this.ds.getTotalCount();
24649 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24650 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24655 onLoadError : function(){
24656 this.loading.enable();
24660 onPagingKeydown : function(e){
24661 var k = e.getKey();
24662 var d = this.getPageData();
24664 var v = this.field.dom.value, pageNum;
24665 if(!v || isNaN(pageNum = parseInt(v, 10))){
24666 this.field.dom.value = d.activePage;
24669 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24670 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24673 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))
24675 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24676 this.field.dom.value = pageNum;
24677 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24680 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24682 var v = this.field.dom.value, pageNum;
24683 var increment = (e.shiftKey) ? 10 : 1;
24684 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24687 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24688 this.field.dom.value = d.activePage;
24691 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24693 this.field.dom.value = parseInt(v, 10) + increment;
24694 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24695 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24702 beforeLoad : function(){
24704 this.loading.disable();
24709 onClick : function(which){
24718 ds.load({params:{start: 0, limit: this.pageSize}});
24721 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24724 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24727 var total = ds.getTotalCount();
24728 var extra = total % this.pageSize;
24729 var lastStart = extra ? (total - extra) : total-this.pageSize;
24730 ds.load({params:{start: lastStart, limit: this.pageSize}});
24733 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24739 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24740 * @param {Roo.data.Store} store The data store to unbind
24742 unbind : function(ds){
24743 ds.un("beforeload", this.beforeLoad, this);
24744 ds.un("load", this.onLoad, this);
24745 ds.un("loadexception", this.onLoadError, this);
24746 ds.un("remove", this.updateInfo, this);
24747 ds.un("add", this.updateInfo, this);
24748 this.ds = undefined;
24752 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24753 * @param {Roo.data.Store} store The data store to bind
24755 bind : function(ds){
24756 ds.on("beforeload", this.beforeLoad, this);
24757 ds.on("load", this.onLoad, this);
24758 ds.on("loadexception", this.onLoadError, this);
24759 ds.on("remove", this.updateInfo, this);
24760 ds.on("add", this.updateInfo, this);
24771 * @class Roo.bootstrap.MessageBar
24772 * @extends Roo.bootstrap.Component
24773 * Bootstrap MessageBar class
24774 * @cfg {String} html contents of the MessageBar
24775 * @cfg {String} weight (info | success | warning | danger) default info
24776 * @cfg {String} beforeClass insert the bar before the given class
24777 * @cfg {Boolean} closable (true | false) default false
24778 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24781 * Create a new Element
24782 * @param {Object} config The config object
24785 Roo.bootstrap.MessageBar = function(config){
24786 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24789 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24795 beforeClass: 'bootstrap-sticky-wrap',
24797 getAutoCreate : function(){
24801 cls: 'alert alert-dismissable alert-' + this.weight,
24806 html: this.html || ''
24812 cfg.cls += ' alert-messages-fixed';
24826 onRender : function(ct, position)
24828 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24831 var cfg = Roo.apply({}, this.getAutoCreate());
24835 cfg.cls += ' ' + this.cls;
24838 cfg.style = this.style;
24840 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24842 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24845 this.el.select('>button.close').on('click', this.hide, this);
24851 if (!this.rendered) {
24857 this.fireEvent('show', this);
24863 if (!this.rendered) {
24869 this.fireEvent('hide', this);
24872 update : function()
24874 // var e = this.el.dom.firstChild;
24876 // if(this.closable){
24877 // e = e.nextSibling;
24880 // e.data = this.html || '';
24882 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24898 * @class Roo.bootstrap.Graph
24899 * @extends Roo.bootstrap.Component
24900 * Bootstrap Graph class
24904 @cfg {String} graphtype bar | vbar | pie
24905 @cfg {number} g_x coodinator | centre x (pie)
24906 @cfg {number} g_y coodinator | centre y (pie)
24907 @cfg {number} g_r radius (pie)
24908 @cfg {number} g_height height of the chart (respected by all elements in the set)
24909 @cfg {number} g_width width of the chart (respected by all elements in the set)
24910 @cfg {Object} title The title of the chart
24913 -opts (object) options for the chart
24915 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24916 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24918 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.
24919 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24921 o stretch (boolean)
24923 -opts (object) options for the pie
24926 o startAngle (number)
24927 o endAngle (number)
24931 * Create a new Input
24932 * @param {Object} config The config object
24935 Roo.bootstrap.Graph = function(config){
24936 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24942 * The img click event for the img.
24943 * @param {Roo.EventObject} e
24949 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24960 //g_colors: this.colors,
24967 getAutoCreate : function(){
24978 onRender : function(ct,position){
24981 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24983 if (typeof(Raphael) == 'undefined') {
24984 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24988 this.raphael = Raphael(this.el.dom);
24990 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24991 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24992 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24993 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24995 r.text(160, 10, "Single Series Chart").attr(txtattr);
24996 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24997 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24998 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25000 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25001 r.barchart(330, 10, 300, 220, data1);
25002 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25003 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25006 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25007 // r.barchart(30, 30, 560, 250, xdata, {
25008 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25009 // axis : "0 0 1 1",
25010 // axisxlabels : xdata
25011 // //yvalues : cols,
25014 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25016 // this.load(null,xdata,{
25017 // axis : "0 0 1 1",
25018 // axisxlabels : xdata
25023 load : function(graphtype,xdata,opts)
25025 this.raphael.clear();
25027 graphtype = this.graphtype;
25032 var r = this.raphael,
25033 fin = function () {
25034 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25036 fout = function () {
25037 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25039 pfin = function() {
25040 this.sector.stop();
25041 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25044 this.label[0].stop();
25045 this.label[0].attr({ r: 7.5 });
25046 this.label[1].attr({ "font-weight": 800 });
25049 pfout = function() {
25050 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25053 this.label[0].animate({ r: 5 }, 500, "bounce");
25054 this.label[1].attr({ "font-weight": 400 });
25060 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25063 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25066 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25067 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25069 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25076 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25081 setTitle: function(o)
25086 initEvents: function() {
25089 this.el.on('click', this.onClick, this);
25093 onClick : function(e)
25095 Roo.log('img onclick');
25096 this.fireEvent('click', this, e);
25108 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25111 * @class Roo.bootstrap.dash.NumberBox
25112 * @extends Roo.bootstrap.Component
25113 * Bootstrap NumberBox class
25114 * @cfg {String} headline Box headline
25115 * @cfg {String} content Box content
25116 * @cfg {String} icon Box icon
25117 * @cfg {String} footer Footer text
25118 * @cfg {String} fhref Footer href
25121 * Create a new NumberBox
25122 * @param {Object} config The config object
25126 Roo.bootstrap.dash.NumberBox = function(config){
25127 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25131 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25140 getAutoCreate : function(){
25144 cls : 'small-box ',
25152 cls : 'roo-headline',
25153 html : this.headline
25157 cls : 'roo-content',
25158 html : this.content
25172 cls : 'ion ' + this.icon
25181 cls : 'small-box-footer',
25182 href : this.fhref || '#',
25186 cfg.cn.push(footer);
25193 onRender : function(ct,position){
25194 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25201 setHeadline: function (value)
25203 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25206 setFooter: function (value, href)
25208 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25211 this.el.select('a.small-box-footer',true).first().attr('href', href);
25216 setContent: function (value)
25218 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25221 initEvents: function()
25235 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25238 * @class Roo.bootstrap.dash.TabBox
25239 * @extends Roo.bootstrap.Component
25240 * Bootstrap TabBox class
25241 * @cfg {String} title Title of the TabBox
25242 * @cfg {String} icon Icon of the TabBox
25243 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25244 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25247 * Create a new TabBox
25248 * @param {Object} config The config object
25252 Roo.bootstrap.dash.TabBox = function(config){
25253 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25258 * When a pane is added
25259 * @param {Roo.bootstrap.dash.TabPane} pane
25263 * @event activatepane
25264 * When a pane is activated
25265 * @param {Roo.bootstrap.dash.TabPane} pane
25267 "activatepane" : true
25275 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25280 tabScrollable : false,
25282 getChildContainer : function()
25284 return this.el.select('.tab-content', true).first();
25287 getAutoCreate : function(){
25291 cls: 'pull-left header',
25299 cls: 'fa ' + this.icon
25305 cls: 'nav nav-tabs pull-right',
25311 if(this.tabScrollable){
25318 cls: 'nav nav-tabs pull-right',
25329 cls: 'nav-tabs-custom',
25334 cls: 'tab-content no-padding',
25342 initEvents : function()
25344 //Roo.log('add add pane handler');
25345 this.on('addpane', this.onAddPane, this);
25348 * Updates the box title
25349 * @param {String} html to set the title to.
25351 setTitle : function(value)
25353 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25355 onAddPane : function(pane)
25357 this.panes.push(pane);
25358 //Roo.log('addpane');
25360 // tabs are rendere left to right..
25361 if(!this.showtabs){
25365 var ctr = this.el.select('.nav-tabs', true).first();
25368 var existing = ctr.select('.nav-tab',true);
25369 var qty = existing.getCount();;
25372 var tab = ctr.createChild({
25374 cls : 'nav-tab' + (qty ? '' : ' active'),
25382 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25385 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25387 pane.el.addClass('active');
25392 onTabClick : function(ev,un,ob,pane)
25394 //Roo.log('tab - prev default');
25395 ev.preventDefault();
25398 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25399 pane.tab.addClass('active');
25400 //Roo.log(pane.title);
25401 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25402 // technically we should have a deactivate event.. but maybe add later.
25403 // and it should not de-activate the selected tab...
25404 this.fireEvent('activatepane', pane);
25405 pane.el.addClass('active');
25406 pane.fireEvent('activate');
25411 getActivePane : function()
25414 Roo.each(this.panes, function(p) {
25415 if(p.el.hasClass('active')){
25436 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25438 * @class Roo.bootstrap.TabPane
25439 * @extends Roo.bootstrap.Component
25440 * Bootstrap TabPane class
25441 * @cfg {Boolean} active (false | true) Default false
25442 * @cfg {String} title title of panel
25446 * Create a new TabPane
25447 * @param {Object} config The config object
25450 Roo.bootstrap.dash.TabPane = function(config){
25451 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25457 * When a pane is activated
25458 * @param {Roo.bootstrap.dash.TabPane} pane
25465 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25470 // the tabBox that this is attached to.
25473 getAutoCreate : function()
25481 cfg.cls += ' active';
25486 initEvents : function()
25488 //Roo.log('trigger add pane handler');
25489 this.parent().fireEvent('addpane', this)
25493 * Updates the tab title
25494 * @param {String} html to set the title to.
25496 setTitle: function(str)
25502 this.tab.select('a', true).first().dom.innerHTML = str;
25519 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25522 * @class Roo.bootstrap.menu.Menu
25523 * @extends Roo.bootstrap.Component
25524 * Bootstrap Menu class - container for Menu
25525 * @cfg {String} html Text of the menu
25526 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25527 * @cfg {String} icon Font awesome icon
25528 * @cfg {String} pos Menu align to (top | bottom) default bottom
25532 * Create a new Menu
25533 * @param {Object} config The config object
25537 Roo.bootstrap.menu.Menu = function(config){
25538 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25542 * @event beforeshow
25543 * Fires before this menu is displayed
25544 * @param {Roo.bootstrap.menu.Menu} this
25548 * @event beforehide
25549 * Fires before this menu is hidden
25550 * @param {Roo.bootstrap.menu.Menu} this
25555 * Fires after this menu is displayed
25556 * @param {Roo.bootstrap.menu.Menu} this
25561 * Fires after this menu is hidden
25562 * @param {Roo.bootstrap.menu.Menu} this
25567 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25568 * @param {Roo.bootstrap.menu.Menu} this
25569 * @param {Roo.EventObject} e
25576 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25580 weight : 'default',
25585 getChildContainer : function() {
25586 if(this.isSubMenu){
25590 return this.el.select('ul.dropdown-menu', true).first();
25593 getAutoCreate : function()
25598 cls : 'roo-menu-text',
25606 cls : 'fa ' + this.icon
25617 cls : 'dropdown-button btn btn-' + this.weight,
25622 cls : 'dropdown-toggle btn btn-' + this.weight,
25632 cls : 'dropdown-menu'
25638 if(this.pos == 'top'){
25639 cfg.cls += ' dropup';
25642 if(this.isSubMenu){
25645 cls : 'dropdown-menu'
25652 onRender : function(ct, position)
25654 this.isSubMenu = ct.hasClass('dropdown-submenu');
25656 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25659 initEvents : function()
25661 if(this.isSubMenu){
25665 this.hidden = true;
25667 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25668 this.triggerEl.on('click', this.onTriggerPress, this);
25670 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25671 this.buttonEl.on('click', this.onClick, this);
25677 if(this.isSubMenu){
25681 return this.el.select('ul.dropdown-menu', true).first();
25684 onClick : function(e)
25686 this.fireEvent("click", this, e);
25689 onTriggerPress : function(e)
25691 if (this.isVisible()) {
25698 isVisible : function(){
25699 return !this.hidden;
25704 this.fireEvent("beforeshow", this);
25706 this.hidden = false;
25707 this.el.addClass('open');
25709 Roo.get(document).on("mouseup", this.onMouseUp, this);
25711 this.fireEvent("show", this);
25718 this.fireEvent("beforehide", this);
25720 this.hidden = true;
25721 this.el.removeClass('open');
25723 Roo.get(document).un("mouseup", this.onMouseUp);
25725 this.fireEvent("hide", this);
25728 onMouseUp : function()
25742 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25745 * @class Roo.bootstrap.menu.Item
25746 * @extends Roo.bootstrap.Component
25747 * Bootstrap MenuItem class
25748 * @cfg {Boolean} submenu (true | false) default false
25749 * @cfg {String} html text of the item
25750 * @cfg {String} href the link
25751 * @cfg {Boolean} disable (true | false) default false
25752 * @cfg {Boolean} preventDefault (true | false) default true
25753 * @cfg {String} icon Font awesome icon
25754 * @cfg {String} pos Submenu align to (left | right) default right
25758 * Create a new Item
25759 * @param {Object} config The config object
25763 Roo.bootstrap.menu.Item = function(config){
25764 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25768 * Fires when the mouse is hovering over this menu
25769 * @param {Roo.bootstrap.menu.Item} this
25770 * @param {Roo.EventObject} e
25775 * Fires when the mouse exits this menu
25776 * @param {Roo.bootstrap.menu.Item} this
25777 * @param {Roo.EventObject} e
25783 * The raw click event for the entire grid.
25784 * @param {Roo.EventObject} e
25790 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25795 preventDefault: true,
25800 getAutoCreate : function()
25805 cls : 'roo-menu-item-text',
25813 cls : 'fa ' + this.icon
25822 href : this.href || '#',
25829 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25833 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25835 if(this.pos == 'left'){
25836 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25843 initEvents : function()
25845 this.el.on('mouseover', this.onMouseOver, this);
25846 this.el.on('mouseout', this.onMouseOut, this);
25848 this.el.select('a', true).first().on('click', this.onClick, this);
25852 onClick : function(e)
25854 if(this.preventDefault){
25855 e.preventDefault();
25858 this.fireEvent("click", this, e);
25861 onMouseOver : function(e)
25863 if(this.submenu && this.pos == 'left'){
25864 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25867 this.fireEvent("mouseover", this, e);
25870 onMouseOut : function(e)
25872 this.fireEvent("mouseout", this, e);
25884 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25887 * @class Roo.bootstrap.menu.Separator
25888 * @extends Roo.bootstrap.Component
25889 * Bootstrap Separator class
25892 * Create a new Separator
25893 * @param {Object} config The config object
25897 Roo.bootstrap.menu.Separator = function(config){
25898 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25901 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25903 getAutoCreate : function(){
25924 * @class Roo.bootstrap.Tooltip
25925 * Bootstrap Tooltip class
25926 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25927 * to determine which dom element triggers the tooltip.
25929 * It needs to add support for additional attributes like tooltip-position
25932 * Create a new Toolti
25933 * @param {Object} config The config object
25936 Roo.bootstrap.Tooltip = function(config){
25937 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25939 this.alignment = Roo.bootstrap.Tooltip.alignment;
25941 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25942 this.alignment = config.alignment;
25947 Roo.apply(Roo.bootstrap.Tooltip, {
25949 * @function init initialize tooltip monitoring.
25953 currentTip : false,
25954 currentRegion : false,
25960 Roo.get(document).on('mouseover', this.enter ,this);
25961 Roo.get(document).on('mouseout', this.leave, this);
25964 this.currentTip = new Roo.bootstrap.Tooltip();
25967 enter : function(ev)
25969 var dom = ev.getTarget();
25971 //Roo.log(['enter',dom]);
25972 var el = Roo.fly(dom);
25973 if (this.currentEl) {
25975 //Roo.log(this.currentEl);
25976 //Roo.log(this.currentEl.contains(dom));
25977 if (this.currentEl == el) {
25980 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25986 if (this.currentTip.el) {
25987 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25991 if(!el || el.dom == document){
25997 // you can not look for children, as if el is the body.. then everythign is the child..
25998 if (!el.attr('tooltip')) { //
25999 if (!el.select("[tooltip]").elements.length) {
26002 // is the mouse over this child...?
26003 bindEl = el.select("[tooltip]").first();
26004 var xy = ev.getXY();
26005 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26006 //Roo.log("not in region.");
26009 //Roo.log("child element over..");
26012 this.currentEl = bindEl;
26013 this.currentTip.bind(bindEl);
26014 this.currentRegion = Roo.lib.Region.getRegion(dom);
26015 this.currentTip.enter();
26018 leave : function(ev)
26020 var dom = ev.getTarget();
26021 //Roo.log(['leave',dom]);
26022 if (!this.currentEl) {
26027 if (dom != this.currentEl.dom) {
26030 var xy = ev.getXY();
26031 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26034 // only activate leave if mouse cursor is outside... bounding box..
26039 if (this.currentTip) {
26040 this.currentTip.leave();
26042 //Roo.log('clear currentEl');
26043 this.currentEl = false;
26048 'left' : ['r-l', [-2,0], 'right'],
26049 'right' : ['l-r', [2,0], 'left'],
26050 'bottom' : ['t-b', [0,2], 'top'],
26051 'top' : [ 'b-t', [0,-2], 'bottom']
26057 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26062 delay : null, // can be { show : 300 , hide: 500}
26066 hoverState : null, //???
26068 placement : 'bottom',
26072 getAutoCreate : function(){
26079 cls : 'tooltip-arrow'
26082 cls : 'tooltip-inner'
26089 bind : function(el)
26095 enter : function () {
26097 if (this.timeout != null) {
26098 clearTimeout(this.timeout);
26101 this.hoverState = 'in';
26102 //Roo.log("enter - show");
26103 if (!this.delay || !this.delay.show) {
26108 this.timeout = setTimeout(function () {
26109 if (_t.hoverState == 'in') {
26112 }, this.delay.show);
26116 clearTimeout(this.timeout);
26118 this.hoverState = 'out';
26119 if (!this.delay || !this.delay.hide) {
26125 this.timeout = setTimeout(function () {
26126 //Roo.log("leave - timeout");
26128 if (_t.hoverState == 'out') {
26130 Roo.bootstrap.Tooltip.currentEl = false;
26135 show : function (msg)
26138 this.render(document.body);
26141 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26143 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26145 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26147 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26149 var placement = typeof this.placement == 'function' ?
26150 this.placement.call(this, this.el, on_el) :
26153 var autoToken = /\s?auto?\s?/i;
26154 var autoPlace = autoToken.test(placement);
26156 placement = placement.replace(autoToken, '') || 'top';
26160 //this.el.setXY([0,0]);
26162 //this.el.dom.style.display='block';
26164 //this.el.appendTo(on_el);
26166 var p = this.getPosition();
26167 var box = this.el.getBox();
26173 var align = this.alignment[placement];
26175 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26177 if(placement == 'top' || placement == 'bottom'){
26179 placement = 'right';
26182 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26183 placement = 'left';
26186 var scroll = Roo.select('body', true).first().getScroll();
26188 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26194 this.el.alignTo(this.bindEl, align[0],align[1]);
26195 //var arrow = this.el.select('.arrow',true).first();
26196 //arrow.set(align[2],
26198 this.el.addClass(placement);
26200 this.el.addClass('in fade');
26202 this.hoverState = null;
26204 if (this.el.hasClass('fade')) {
26215 //this.el.setXY([0,0]);
26216 this.el.removeClass('in');
26232 * @class Roo.bootstrap.LocationPicker
26233 * @extends Roo.bootstrap.Component
26234 * Bootstrap LocationPicker class
26235 * @cfg {Number} latitude Position when init default 0
26236 * @cfg {Number} longitude Position when init default 0
26237 * @cfg {Number} zoom default 15
26238 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26239 * @cfg {Boolean} mapTypeControl default false
26240 * @cfg {Boolean} disableDoubleClickZoom default false
26241 * @cfg {Boolean} scrollwheel default true
26242 * @cfg {Boolean} streetViewControl default false
26243 * @cfg {Number} radius default 0
26244 * @cfg {String} locationName
26245 * @cfg {Boolean} draggable default true
26246 * @cfg {Boolean} enableAutocomplete default false
26247 * @cfg {Boolean} enableReverseGeocode default true
26248 * @cfg {String} markerTitle
26251 * Create a new LocationPicker
26252 * @param {Object} config The config object
26256 Roo.bootstrap.LocationPicker = function(config){
26258 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26263 * Fires when the picker initialized.
26264 * @param {Roo.bootstrap.LocationPicker} this
26265 * @param {Google Location} location
26269 * @event positionchanged
26270 * Fires when the picker position changed.
26271 * @param {Roo.bootstrap.LocationPicker} this
26272 * @param {Google Location} location
26274 positionchanged : true,
26277 * Fires when the map resize.
26278 * @param {Roo.bootstrap.LocationPicker} this
26283 * Fires when the map show.
26284 * @param {Roo.bootstrap.LocationPicker} this
26289 * Fires when the map hide.
26290 * @param {Roo.bootstrap.LocationPicker} this
26295 * Fires when click the map.
26296 * @param {Roo.bootstrap.LocationPicker} this
26297 * @param {Map event} e
26301 * @event mapRightClick
26302 * Fires when right click the map.
26303 * @param {Roo.bootstrap.LocationPicker} this
26304 * @param {Map event} e
26306 mapRightClick : true,
26308 * @event markerClick
26309 * Fires when click the marker.
26310 * @param {Roo.bootstrap.LocationPicker} this
26311 * @param {Map event} e
26313 markerClick : true,
26315 * @event markerRightClick
26316 * Fires when right click the marker.
26317 * @param {Roo.bootstrap.LocationPicker} this
26318 * @param {Map event} e
26320 markerRightClick : true,
26322 * @event OverlayViewDraw
26323 * Fires when OverlayView Draw
26324 * @param {Roo.bootstrap.LocationPicker} this
26326 OverlayViewDraw : true,
26328 * @event OverlayViewOnAdd
26329 * Fires when OverlayView Draw
26330 * @param {Roo.bootstrap.LocationPicker} this
26332 OverlayViewOnAdd : true,
26334 * @event OverlayViewOnRemove
26335 * Fires when OverlayView Draw
26336 * @param {Roo.bootstrap.LocationPicker} this
26338 OverlayViewOnRemove : true,
26340 * @event OverlayViewShow
26341 * Fires when OverlayView Draw
26342 * @param {Roo.bootstrap.LocationPicker} this
26343 * @param {Pixel} cpx
26345 OverlayViewShow : true,
26347 * @event OverlayViewHide
26348 * Fires when OverlayView Draw
26349 * @param {Roo.bootstrap.LocationPicker} this
26351 OverlayViewHide : true,
26353 * @event loadexception
26354 * Fires when load google lib failed.
26355 * @param {Roo.bootstrap.LocationPicker} this
26357 loadexception : true
26362 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26364 gMapContext: false,
26370 mapTypeControl: false,
26371 disableDoubleClickZoom: false,
26373 streetViewControl: false,
26377 enableAutocomplete: false,
26378 enableReverseGeocode: true,
26381 getAutoCreate: function()
26386 cls: 'roo-location-picker'
26392 initEvents: function(ct, position)
26394 if(!this.el.getWidth() || this.isApplied()){
26398 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26403 initial: function()
26405 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26406 this.fireEvent('loadexception', this);
26410 if(!this.mapTypeId){
26411 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26414 this.gMapContext = this.GMapContext();
26416 this.initOverlayView();
26418 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26422 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26423 _this.setPosition(_this.gMapContext.marker.position);
26426 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26427 _this.fireEvent('mapClick', this, event);
26431 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26432 _this.fireEvent('mapRightClick', this, event);
26436 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26437 _this.fireEvent('markerClick', this, event);
26441 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26442 _this.fireEvent('markerRightClick', this, event);
26446 this.setPosition(this.gMapContext.location);
26448 this.fireEvent('initial', this, this.gMapContext.location);
26451 initOverlayView: function()
26455 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26459 _this.fireEvent('OverlayViewDraw', _this);
26464 _this.fireEvent('OverlayViewOnAdd', _this);
26467 onRemove: function()
26469 _this.fireEvent('OverlayViewOnRemove', _this);
26472 show: function(cpx)
26474 _this.fireEvent('OverlayViewShow', _this, cpx);
26479 _this.fireEvent('OverlayViewHide', _this);
26485 fromLatLngToContainerPixel: function(event)
26487 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26490 isApplied: function()
26492 return this.getGmapContext() == false ? false : true;
26495 getGmapContext: function()
26497 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26500 GMapContext: function()
26502 var position = new google.maps.LatLng(this.latitude, this.longitude);
26504 var _map = new google.maps.Map(this.el.dom, {
26507 mapTypeId: this.mapTypeId,
26508 mapTypeControl: this.mapTypeControl,
26509 disableDoubleClickZoom: this.disableDoubleClickZoom,
26510 scrollwheel: this.scrollwheel,
26511 streetViewControl: this.streetViewControl,
26512 locationName: this.locationName,
26513 draggable: this.draggable,
26514 enableAutocomplete: this.enableAutocomplete,
26515 enableReverseGeocode: this.enableReverseGeocode
26518 var _marker = new google.maps.Marker({
26519 position: position,
26521 title: this.markerTitle,
26522 draggable: this.draggable
26529 location: position,
26530 radius: this.radius,
26531 locationName: this.locationName,
26532 addressComponents: {
26533 formatted_address: null,
26534 addressLine1: null,
26535 addressLine2: null,
26537 streetNumber: null,
26541 stateOrProvince: null
26544 domContainer: this.el.dom,
26545 geodecoder: new google.maps.Geocoder()
26549 drawCircle: function(center, radius, options)
26551 if (this.gMapContext.circle != null) {
26552 this.gMapContext.circle.setMap(null);
26556 options = Roo.apply({}, options, {
26557 strokeColor: "#0000FF",
26558 strokeOpacity: .35,
26560 fillColor: "#0000FF",
26564 options.map = this.gMapContext.map;
26565 options.radius = radius;
26566 options.center = center;
26567 this.gMapContext.circle = new google.maps.Circle(options);
26568 return this.gMapContext.circle;
26574 setPosition: function(location)
26576 this.gMapContext.location = location;
26577 this.gMapContext.marker.setPosition(location);
26578 this.gMapContext.map.panTo(location);
26579 this.drawCircle(location, this.gMapContext.radius, {});
26583 if (this.gMapContext.settings.enableReverseGeocode) {
26584 this.gMapContext.geodecoder.geocode({
26585 latLng: this.gMapContext.location
26586 }, function(results, status) {
26588 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26589 _this.gMapContext.locationName = results[0].formatted_address;
26590 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26592 _this.fireEvent('positionchanged', this, location);
26599 this.fireEvent('positionchanged', this, location);
26604 google.maps.event.trigger(this.gMapContext.map, "resize");
26606 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26608 this.fireEvent('resize', this);
26611 setPositionByLatLng: function(latitude, longitude)
26613 this.setPosition(new google.maps.LatLng(latitude, longitude));
26616 getCurrentPosition: function()
26619 latitude: this.gMapContext.location.lat(),
26620 longitude: this.gMapContext.location.lng()
26624 getAddressName: function()
26626 return this.gMapContext.locationName;
26629 getAddressComponents: function()
26631 return this.gMapContext.addressComponents;
26634 address_component_from_google_geocode: function(address_components)
26638 for (var i = 0; i < address_components.length; i++) {
26639 var component = address_components[i];
26640 if (component.types.indexOf("postal_code") >= 0) {
26641 result.postalCode = component.short_name;
26642 } else if (component.types.indexOf("street_number") >= 0) {
26643 result.streetNumber = component.short_name;
26644 } else if (component.types.indexOf("route") >= 0) {
26645 result.streetName = component.short_name;
26646 } else if (component.types.indexOf("neighborhood") >= 0) {
26647 result.city = component.short_name;
26648 } else if (component.types.indexOf("locality") >= 0) {
26649 result.city = component.short_name;
26650 } else if (component.types.indexOf("sublocality") >= 0) {
26651 result.district = component.short_name;
26652 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26653 result.stateOrProvince = component.short_name;
26654 } else if (component.types.indexOf("country") >= 0) {
26655 result.country = component.short_name;
26659 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26660 result.addressLine2 = "";
26664 setZoomLevel: function(zoom)
26666 this.gMapContext.map.setZoom(zoom);
26679 this.fireEvent('show', this);
26690 this.fireEvent('hide', this);
26695 Roo.apply(Roo.bootstrap.LocationPicker, {
26697 OverlayView : function(map, options)
26699 options = options || {};
26713 * @class Roo.bootstrap.Alert
26714 * @extends Roo.bootstrap.Component
26715 * Bootstrap Alert class
26716 * @cfg {String} title The title of alert
26717 * @cfg {String} html The content of alert
26718 * @cfg {String} weight ( success | info | warning | danger )
26719 * @cfg {String} faicon font-awesomeicon
26722 * Create a new alert
26723 * @param {Object} config The config object
26727 Roo.bootstrap.Alert = function(config){
26728 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26732 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26739 getAutoCreate : function()
26748 cls : 'roo-alert-icon'
26753 cls : 'roo-alert-title',
26758 cls : 'roo-alert-text',
26765 cfg.cn[0].cls += ' fa ' + this.faicon;
26769 cfg.cls += ' alert-' + this.weight;
26775 initEvents: function()
26777 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26780 setTitle : function(str)
26782 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26785 setText : function(str)
26787 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26790 setWeight : function(weight)
26793 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26796 this.weight = weight;
26798 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26801 setIcon : function(icon)
26804 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26807 this.faicon = icon;
26809 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26830 * @class Roo.bootstrap.UploadCropbox
26831 * @extends Roo.bootstrap.Component
26832 * Bootstrap UploadCropbox class
26833 * @cfg {String} emptyText show when image has been loaded
26834 * @cfg {String} rotateNotify show when image too small to rotate
26835 * @cfg {Number} errorTimeout default 3000
26836 * @cfg {Number} minWidth default 300
26837 * @cfg {Number} minHeight default 300
26838 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26839 * @cfg {Boolean} isDocument (true|false) default false
26840 * @cfg {String} url action url
26841 * @cfg {String} paramName default 'imageUpload'
26842 * @cfg {String} method default POST
26843 * @cfg {Boolean} loadMask (true|false) default true
26844 * @cfg {Boolean} loadingText default 'Loading...'
26847 * Create a new UploadCropbox
26848 * @param {Object} config The config object
26851 Roo.bootstrap.UploadCropbox = function(config){
26852 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26856 * @event beforeselectfile
26857 * Fire before select file
26858 * @param {Roo.bootstrap.UploadCropbox} this
26860 "beforeselectfile" : true,
26863 * Fire after initEvent
26864 * @param {Roo.bootstrap.UploadCropbox} this
26869 * Fire after initEvent
26870 * @param {Roo.bootstrap.UploadCropbox} this
26871 * @param {String} data
26876 * Fire when preparing the file data
26877 * @param {Roo.bootstrap.UploadCropbox} this
26878 * @param {Object} file
26883 * Fire when get exception
26884 * @param {Roo.bootstrap.UploadCropbox} this
26885 * @param {XMLHttpRequest} xhr
26887 "exception" : true,
26889 * @event beforeloadcanvas
26890 * Fire before load the canvas
26891 * @param {Roo.bootstrap.UploadCropbox} this
26892 * @param {String} src
26894 "beforeloadcanvas" : true,
26897 * Fire when trash image
26898 * @param {Roo.bootstrap.UploadCropbox} this
26903 * Fire when download the image
26904 * @param {Roo.bootstrap.UploadCropbox} this
26908 * @event footerbuttonclick
26909 * Fire when footerbuttonclick
26910 * @param {Roo.bootstrap.UploadCropbox} this
26911 * @param {String} type
26913 "footerbuttonclick" : true,
26917 * @param {Roo.bootstrap.UploadCropbox} this
26922 * Fire when rotate the image
26923 * @param {Roo.bootstrap.UploadCropbox} this
26924 * @param {String} pos
26929 * Fire when inspect the file
26930 * @param {Roo.bootstrap.UploadCropbox} this
26931 * @param {Object} file
26936 * Fire when xhr upload the file
26937 * @param {Roo.bootstrap.UploadCropbox} this
26938 * @param {Object} data
26943 * Fire when arrange the file data
26944 * @param {Roo.bootstrap.UploadCropbox} this
26945 * @param {Object} formData
26950 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26953 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26955 emptyText : 'Click to upload image',
26956 rotateNotify : 'Image is too small to rotate',
26957 errorTimeout : 3000,
26971 cropType : 'image/jpeg',
26973 canvasLoaded : false,
26974 isDocument : false,
26976 paramName : 'imageUpload',
26978 loadingText : 'Loading...',
26981 getAutoCreate : function()
26985 cls : 'roo-upload-cropbox',
26989 cls : 'roo-upload-cropbox-selector',
26994 cls : 'roo-upload-cropbox-body',
26995 style : 'cursor:pointer',
26999 cls : 'roo-upload-cropbox-preview'
27003 cls : 'roo-upload-cropbox-thumb'
27007 cls : 'roo-upload-cropbox-empty-notify',
27008 html : this.emptyText
27012 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27013 html : this.rotateNotify
27019 cls : 'roo-upload-cropbox-footer',
27022 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27032 onRender : function(ct, position)
27034 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27036 if (this.buttons.length) {
27038 Roo.each(this.buttons, function(bb) {
27040 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27042 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27048 this.maskEl = this.el;
27052 initEvents : function()
27054 this.urlAPI = (window.createObjectURL && window) ||
27055 (window.URL && URL.revokeObjectURL && URL) ||
27056 (window.webkitURL && webkitURL);
27058 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27059 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27061 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27062 this.selectorEl.hide();
27064 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27065 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27067 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27068 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27069 this.thumbEl.hide();
27071 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27072 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27074 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27075 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27076 this.errorEl.hide();
27078 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27079 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27080 this.footerEl.hide();
27082 this.setThumbBoxSize();
27088 this.fireEvent('initial', this);
27095 window.addEventListener("resize", function() { _this.resize(); } );
27097 this.bodyEl.on('click', this.beforeSelectFile, this);
27100 this.bodyEl.on('touchstart', this.onTouchStart, this);
27101 this.bodyEl.on('touchmove', this.onTouchMove, this);
27102 this.bodyEl.on('touchend', this.onTouchEnd, this);
27106 this.bodyEl.on('mousedown', this.onMouseDown, this);
27107 this.bodyEl.on('mousemove', this.onMouseMove, this);
27108 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27109 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27110 Roo.get(document).on('mouseup', this.onMouseUp, this);
27113 this.selectorEl.on('change', this.onFileSelected, this);
27119 this.baseScale = 1;
27121 this.baseRotate = 1;
27122 this.dragable = false;
27123 this.pinching = false;
27126 this.cropData = false;
27127 this.notifyEl.dom.innerHTML = this.emptyText;
27129 this.selectorEl.dom.value = '';
27133 resize : function()
27135 if(this.fireEvent('resize', this) != false){
27136 this.setThumbBoxPosition();
27137 this.setCanvasPosition();
27141 onFooterButtonClick : function(e, el, o, type)
27144 case 'rotate-left' :
27145 this.onRotateLeft(e);
27147 case 'rotate-right' :
27148 this.onRotateRight(e);
27151 this.beforeSelectFile(e);
27166 this.fireEvent('footerbuttonclick', this, type);
27169 beforeSelectFile : function(e)
27171 e.preventDefault();
27173 if(this.fireEvent('beforeselectfile', this) != false){
27174 this.selectorEl.dom.click();
27178 onFileSelected : function(e)
27180 e.preventDefault();
27182 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27186 var file = this.selectorEl.dom.files[0];
27188 if(this.fireEvent('inspect', this, file) != false){
27189 this.prepare(file);
27194 trash : function(e)
27196 this.fireEvent('trash', this);
27199 download : function(e)
27201 this.fireEvent('download', this);
27204 loadCanvas : function(src)
27206 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27210 this.imageEl = document.createElement('img');
27214 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27216 this.imageEl.src = src;
27220 onLoadCanvas : function()
27222 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27223 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27225 this.bodyEl.un('click', this.beforeSelectFile, this);
27227 this.notifyEl.hide();
27228 this.thumbEl.show();
27229 this.footerEl.show();
27231 this.baseRotateLevel();
27233 if(this.isDocument){
27234 this.setThumbBoxSize();
27237 this.setThumbBoxPosition();
27239 this.baseScaleLevel();
27245 this.canvasLoaded = true;
27248 this.maskEl.unmask();
27253 setCanvasPosition : function()
27255 if(!this.canvasEl){
27259 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27260 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27262 this.previewEl.setLeft(pw);
27263 this.previewEl.setTop(ph);
27267 onMouseDown : function(e)
27271 this.dragable = true;
27272 this.pinching = false;
27274 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27275 this.dragable = false;
27279 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27280 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27284 onMouseMove : function(e)
27288 if(!this.canvasLoaded){
27292 if (!this.dragable){
27296 var minX = Math.ceil(this.thumbEl.getLeft(true));
27297 var minY = Math.ceil(this.thumbEl.getTop(true));
27299 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27300 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27302 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27303 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27305 x = x - this.mouseX;
27306 y = y - this.mouseY;
27308 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27309 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27311 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27312 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27314 this.previewEl.setLeft(bgX);
27315 this.previewEl.setTop(bgY);
27317 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27318 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27321 onMouseUp : function(e)
27325 this.dragable = false;
27328 onMouseWheel : function(e)
27332 this.startScale = this.scale;
27334 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27336 if(!this.zoomable()){
27337 this.scale = this.startScale;
27346 zoomable : function()
27348 var minScale = this.thumbEl.getWidth() / this.minWidth;
27350 if(this.minWidth < this.minHeight){
27351 minScale = this.thumbEl.getHeight() / this.minHeight;
27354 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27355 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27359 (this.rotate == 0 || this.rotate == 180) &&
27361 width > this.imageEl.OriginWidth ||
27362 height > this.imageEl.OriginHeight ||
27363 (width < this.minWidth && height < this.minHeight)
27371 (this.rotate == 90 || this.rotate == 270) &&
27373 width > this.imageEl.OriginWidth ||
27374 height > this.imageEl.OriginHeight ||
27375 (width < this.minHeight && height < this.minWidth)
27382 !this.isDocument &&
27383 (this.rotate == 0 || this.rotate == 180) &&
27385 width < this.minWidth ||
27386 width > this.imageEl.OriginWidth ||
27387 height < this.minHeight ||
27388 height > this.imageEl.OriginHeight
27395 !this.isDocument &&
27396 (this.rotate == 90 || this.rotate == 270) &&
27398 width < this.minHeight ||
27399 width > this.imageEl.OriginWidth ||
27400 height < this.minWidth ||
27401 height > this.imageEl.OriginHeight
27411 onRotateLeft : function(e)
27413 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27415 var minScale = this.thumbEl.getWidth() / this.minWidth;
27417 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27418 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27420 this.startScale = this.scale;
27422 while (this.getScaleLevel() < minScale){
27424 this.scale = this.scale + 1;
27426 if(!this.zoomable()){
27431 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27432 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27437 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27444 this.scale = this.startScale;
27446 this.onRotateFail();
27451 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27453 if(this.isDocument){
27454 this.setThumbBoxSize();
27455 this.setThumbBoxPosition();
27456 this.setCanvasPosition();
27461 this.fireEvent('rotate', this, 'left');
27465 onRotateRight : function(e)
27467 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27469 var minScale = this.thumbEl.getWidth() / this.minWidth;
27471 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27472 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27474 this.startScale = this.scale;
27476 while (this.getScaleLevel() < minScale){
27478 this.scale = this.scale + 1;
27480 if(!this.zoomable()){
27485 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27486 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27491 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27498 this.scale = this.startScale;
27500 this.onRotateFail();
27505 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27507 if(this.isDocument){
27508 this.setThumbBoxSize();
27509 this.setThumbBoxPosition();
27510 this.setCanvasPosition();
27515 this.fireEvent('rotate', this, 'right');
27518 onRotateFail : function()
27520 this.errorEl.show(true);
27524 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27529 this.previewEl.dom.innerHTML = '';
27531 var canvasEl = document.createElement("canvas");
27533 var contextEl = canvasEl.getContext("2d");
27535 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27536 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27537 var center = this.imageEl.OriginWidth / 2;
27539 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27540 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27541 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27542 center = this.imageEl.OriginHeight / 2;
27545 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27547 contextEl.translate(center, center);
27548 contextEl.rotate(this.rotate * Math.PI / 180);
27550 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27552 this.canvasEl = document.createElement("canvas");
27554 this.contextEl = this.canvasEl.getContext("2d");
27556 switch (this.rotate) {
27559 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27560 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27562 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27567 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27568 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27570 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27571 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);
27575 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27580 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27581 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27583 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27584 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);
27588 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);
27593 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27594 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27596 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27597 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27601 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);
27608 this.previewEl.appendChild(this.canvasEl);
27610 this.setCanvasPosition();
27615 if(!this.canvasLoaded){
27619 var imageCanvas = document.createElement("canvas");
27621 var imageContext = imageCanvas.getContext("2d");
27623 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27624 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27626 var center = imageCanvas.width / 2;
27628 imageContext.translate(center, center);
27630 imageContext.rotate(this.rotate * Math.PI / 180);
27632 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27634 var canvas = document.createElement("canvas");
27636 var context = canvas.getContext("2d");
27638 canvas.width = this.minWidth;
27639 canvas.height = this.minHeight;
27641 switch (this.rotate) {
27644 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27645 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27647 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27648 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27650 var targetWidth = this.minWidth - 2 * x;
27651 var targetHeight = this.minHeight - 2 * y;
27655 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27656 scale = targetWidth / width;
27659 if(x > 0 && y == 0){
27660 scale = targetHeight / height;
27663 if(x > 0 && y > 0){
27664 scale = targetWidth / width;
27666 if(width < height){
27667 scale = targetHeight / height;
27671 context.scale(scale, scale);
27673 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27674 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27676 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27677 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27679 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27684 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27685 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27687 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27688 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27690 var targetWidth = this.minWidth - 2 * x;
27691 var targetHeight = this.minHeight - 2 * y;
27695 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27696 scale = targetWidth / width;
27699 if(x > 0 && y == 0){
27700 scale = targetHeight / height;
27703 if(x > 0 && y > 0){
27704 scale = targetWidth / width;
27706 if(width < height){
27707 scale = targetHeight / height;
27711 context.scale(scale, scale);
27713 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27714 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27716 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27717 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27719 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27721 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27726 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27727 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27729 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27730 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27732 var targetWidth = this.minWidth - 2 * x;
27733 var targetHeight = this.minHeight - 2 * y;
27737 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27738 scale = targetWidth / width;
27741 if(x > 0 && y == 0){
27742 scale = targetHeight / height;
27745 if(x > 0 && y > 0){
27746 scale = targetWidth / width;
27748 if(width < height){
27749 scale = targetHeight / height;
27753 context.scale(scale, scale);
27755 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27756 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27758 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27759 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27761 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27762 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27764 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27769 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27770 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27772 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27773 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27775 var targetWidth = this.minWidth - 2 * x;
27776 var targetHeight = this.minHeight - 2 * y;
27780 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27781 scale = targetWidth / width;
27784 if(x > 0 && y == 0){
27785 scale = targetHeight / height;
27788 if(x > 0 && y > 0){
27789 scale = targetWidth / width;
27791 if(width < height){
27792 scale = targetHeight / height;
27796 context.scale(scale, scale);
27798 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27799 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27801 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27802 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27804 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27806 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27813 this.cropData = canvas.toDataURL(this.cropType);
27815 if(this.fireEvent('crop', this, this.cropData) !== false){
27816 this.process(this.file, this.cropData);
27823 setThumbBoxSize : function()
27827 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27828 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27829 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27831 this.minWidth = width;
27832 this.minHeight = height;
27834 if(this.rotate == 90 || this.rotate == 270){
27835 this.minWidth = height;
27836 this.minHeight = width;
27841 width = Math.ceil(this.minWidth * height / this.minHeight);
27843 if(this.minWidth > this.minHeight){
27845 height = Math.ceil(this.minHeight * width / this.minWidth);
27848 this.thumbEl.setStyle({
27849 width : width + 'px',
27850 height : height + 'px'
27857 setThumbBoxPosition : function()
27859 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27860 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27862 this.thumbEl.setLeft(x);
27863 this.thumbEl.setTop(y);
27867 baseRotateLevel : function()
27869 this.baseRotate = 1;
27872 typeof(this.exif) != 'undefined' &&
27873 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27874 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27876 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27879 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27883 baseScaleLevel : function()
27887 if(this.isDocument){
27889 if(this.baseRotate == 6 || this.baseRotate == 8){
27891 height = this.thumbEl.getHeight();
27892 this.baseScale = height / this.imageEl.OriginWidth;
27894 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27895 width = this.thumbEl.getWidth();
27896 this.baseScale = width / this.imageEl.OriginHeight;
27902 height = this.thumbEl.getHeight();
27903 this.baseScale = height / this.imageEl.OriginHeight;
27905 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27906 width = this.thumbEl.getWidth();
27907 this.baseScale = width / this.imageEl.OriginWidth;
27913 if(this.baseRotate == 6 || this.baseRotate == 8){
27915 width = this.thumbEl.getHeight();
27916 this.baseScale = width / this.imageEl.OriginHeight;
27918 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27919 height = this.thumbEl.getWidth();
27920 this.baseScale = height / this.imageEl.OriginHeight;
27923 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27924 height = this.thumbEl.getWidth();
27925 this.baseScale = height / this.imageEl.OriginHeight;
27927 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27928 width = this.thumbEl.getHeight();
27929 this.baseScale = width / this.imageEl.OriginWidth;
27936 width = this.thumbEl.getWidth();
27937 this.baseScale = width / this.imageEl.OriginWidth;
27939 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27940 height = this.thumbEl.getHeight();
27941 this.baseScale = height / this.imageEl.OriginHeight;
27944 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27946 height = this.thumbEl.getHeight();
27947 this.baseScale = height / this.imageEl.OriginHeight;
27949 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27950 width = this.thumbEl.getWidth();
27951 this.baseScale = width / this.imageEl.OriginWidth;
27959 getScaleLevel : function()
27961 return this.baseScale * Math.pow(1.1, this.scale);
27964 onTouchStart : function(e)
27966 if(!this.canvasLoaded){
27967 this.beforeSelectFile(e);
27971 var touches = e.browserEvent.touches;
27977 if(touches.length == 1){
27978 this.onMouseDown(e);
27982 if(touches.length != 2){
27988 for(var i = 0, finger; finger = touches[i]; i++){
27989 coords.push(finger.pageX, finger.pageY);
27992 var x = Math.pow(coords[0] - coords[2], 2);
27993 var y = Math.pow(coords[1] - coords[3], 2);
27995 this.startDistance = Math.sqrt(x + y);
27997 this.startScale = this.scale;
27999 this.pinching = true;
28000 this.dragable = false;
28004 onTouchMove : function(e)
28006 if(!this.pinching && !this.dragable){
28010 var touches = e.browserEvent.touches;
28017 this.onMouseMove(e);
28023 for(var i = 0, finger; finger = touches[i]; i++){
28024 coords.push(finger.pageX, finger.pageY);
28027 var x = Math.pow(coords[0] - coords[2], 2);
28028 var y = Math.pow(coords[1] - coords[3], 2);
28030 this.endDistance = Math.sqrt(x + y);
28032 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28034 if(!this.zoomable()){
28035 this.scale = this.startScale;
28043 onTouchEnd : function(e)
28045 this.pinching = false;
28046 this.dragable = false;
28050 process : function(file, crop)
28053 this.maskEl.mask(this.loadingText);
28056 this.xhr = new XMLHttpRequest();
28058 file.xhr = this.xhr;
28060 this.xhr.open(this.method, this.url, true);
28063 "Accept": "application/json",
28064 "Cache-Control": "no-cache",
28065 "X-Requested-With": "XMLHttpRequest"
28068 for (var headerName in headers) {
28069 var headerValue = headers[headerName];
28071 this.xhr.setRequestHeader(headerName, headerValue);
28077 this.xhr.onload = function()
28079 _this.xhrOnLoad(_this.xhr);
28082 this.xhr.onerror = function()
28084 _this.xhrOnError(_this.xhr);
28087 var formData = new FormData();
28089 formData.append('returnHTML', 'NO');
28092 formData.append('crop', crop);
28095 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28096 formData.append(this.paramName, file, file.name);
28099 if(typeof(file.filename) != 'undefined'){
28100 formData.append('filename', file.filename);
28103 if(typeof(file.mimetype) != 'undefined'){
28104 formData.append('mimetype', file.mimetype);
28107 if(this.fireEvent('arrange', this, formData) != false){
28108 this.xhr.send(formData);
28112 xhrOnLoad : function(xhr)
28115 this.maskEl.unmask();
28118 if (xhr.readyState !== 4) {
28119 this.fireEvent('exception', this, xhr);
28123 var response = Roo.decode(xhr.responseText);
28125 if(!response.success){
28126 this.fireEvent('exception', this, xhr);
28130 var response = Roo.decode(xhr.responseText);
28132 this.fireEvent('upload', this, response);
28136 xhrOnError : function()
28139 this.maskEl.unmask();
28142 Roo.log('xhr on error');
28144 var response = Roo.decode(xhr.responseText);
28150 prepare : function(file)
28153 this.maskEl.mask(this.loadingText);
28159 if(typeof(file) === 'string'){
28160 this.loadCanvas(file);
28164 if(!file || !this.urlAPI){
28169 this.cropType = file.type;
28173 if(this.fireEvent('prepare', this, this.file) != false){
28175 var reader = new FileReader();
28177 reader.onload = function (e) {
28178 if (e.target.error) {
28179 Roo.log(e.target.error);
28183 var buffer = e.target.result,
28184 dataView = new DataView(buffer),
28186 maxOffset = dataView.byteLength - 4,
28190 if (dataView.getUint16(0) === 0xffd8) {
28191 while (offset < maxOffset) {
28192 markerBytes = dataView.getUint16(offset);
28194 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28195 markerLength = dataView.getUint16(offset + 2) + 2;
28196 if (offset + markerLength > dataView.byteLength) {
28197 Roo.log('Invalid meta data: Invalid segment size.');
28201 if(markerBytes == 0xffe1){
28202 _this.parseExifData(
28209 offset += markerLength;
28219 var url = _this.urlAPI.createObjectURL(_this.file);
28221 _this.loadCanvas(url);
28226 reader.readAsArrayBuffer(this.file);
28232 parseExifData : function(dataView, offset, length)
28234 var tiffOffset = offset + 10,
28238 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28239 // No Exif data, might be XMP data instead
28243 // Check for the ASCII code for "Exif" (0x45786966):
28244 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28245 // No Exif data, might be XMP data instead
28248 if (tiffOffset + 8 > dataView.byteLength) {
28249 Roo.log('Invalid Exif data: Invalid segment size.');
28252 // Check for the two null bytes:
28253 if (dataView.getUint16(offset + 8) !== 0x0000) {
28254 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28257 // Check the byte alignment:
28258 switch (dataView.getUint16(tiffOffset)) {
28260 littleEndian = true;
28263 littleEndian = false;
28266 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28269 // Check for the TIFF tag marker (0x002A):
28270 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28271 Roo.log('Invalid Exif data: Missing TIFF marker.');
28274 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28275 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28277 this.parseExifTags(
28280 tiffOffset + dirOffset,
28285 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28290 if (dirOffset + 6 > dataView.byteLength) {
28291 Roo.log('Invalid Exif data: Invalid directory offset.');
28294 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28295 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28296 if (dirEndOffset + 4 > dataView.byteLength) {
28297 Roo.log('Invalid Exif data: Invalid directory size.');
28300 for (i = 0; i < tagsNumber; i += 1) {
28304 dirOffset + 2 + 12 * i, // tag offset
28308 // Return the offset to the next directory:
28309 return dataView.getUint32(dirEndOffset, littleEndian);
28312 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28314 var tag = dataView.getUint16(offset, littleEndian);
28316 this.exif[tag] = this.getExifValue(
28320 dataView.getUint16(offset + 2, littleEndian), // tag type
28321 dataView.getUint32(offset + 4, littleEndian), // tag length
28326 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28328 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28337 Roo.log('Invalid Exif data: Invalid tag type.');
28341 tagSize = tagType.size * length;
28342 // Determine if the value is contained in the dataOffset bytes,
28343 // or if the value at the dataOffset is a pointer to the actual data:
28344 dataOffset = tagSize > 4 ?
28345 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28346 if (dataOffset + tagSize > dataView.byteLength) {
28347 Roo.log('Invalid Exif data: Invalid data offset.');
28350 if (length === 1) {
28351 return tagType.getValue(dataView, dataOffset, littleEndian);
28354 for (i = 0; i < length; i += 1) {
28355 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28358 if (tagType.ascii) {
28360 // Concatenate the chars:
28361 for (i = 0; i < values.length; i += 1) {
28363 // Ignore the terminating NULL byte(s):
28364 if (c === '\u0000') {
28376 Roo.apply(Roo.bootstrap.UploadCropbox, {
28378 'Orientation': 0x0112
28382 1: 0, //'top-left',
28384 3: 180, //'bottom-right',
28385 // 4: 'bottom-left',
28387 6: 90, //'right-top',
28388 // 7: 'right-bottom',
28389 8: 270 //'left-bottom'
28393 // byte, 8-bit unsigned int:
28395 getValue: function (dataView, dataOffset) {
28396 return dataView.getUint8(dataOffset);
28400 // ascii, 8-bit byte:
28402 getValue: function (dataView, dataOffset) {
28403 return String.fromCharCode(dataView.getUint8(dataOffset));
28408 // short, 16 bit int:
28410 getValue: function (dataView, dataOffset, littleEndian) {
28411 return dataView.getUint16(dataOffset, littleEndian);
28415 // long, 32 bit int:
28417 getValue: function (dataView, dataOffset, littleEndian) {
28418 return dataView.getUint32(dataOffset, littleEndian);
28422 // rational = two long values, first is numerator, second is denominator:
28424 getValue: function (dataView, dataOffset, littleEndian) {
28425 return dataView.getUint32(dataOffset, littleEndian) /
28426 dataView.getUint32(dataOffset + 4, littleEndian);
28430 // slong, 32 bit signed int:
28432 getValue: function (dataView, dataOffset, littleEndian) {
28433 return dataView.getInt32(dataOffset, littleEndian);
28437 // srational, two slongs, first is numerator, second is denominator:
28439 getValue: function (dataView, dataOffset, littleEndian) {
28440 return dataView.getInt32(dataOffset, littleEndian) /
28441 dataView.getInt32(dataOffset + 4, littleEndian);
28451 cls : 'btn-group roo-upload-cropbox-rotate-left',
28452 action : 'rotate-left',
28456 cls : 'btn btn-default',
28457 html : '<i class="fa fa-undo"></i>'
28463 cls : 'btn-group roo-upload-cropbox-picture',
28464 action : 'picture',
28468 cls : 'btn btn-default',
28469 html : '<i class="fa fa-picture-o"></i>'
28475 cls : 'btn-group roo-upload-cropbox-rotate-right',
28476 action : 'rotate-right',
28480 cls : 'btn btn-default',
28481 html : '<i class="fa fa-repeat"></i>'
28489 cls : 'btn-group roo-upload-cropbox-rotate-left',
28490 action : 'rotate-left',
28494 cls : 'btn btn-default',
28495 html : '<i class="fa fa-undo"></i>'
28501 cls : 'btn-group roo-upload-cropbox-download',
28502 action : 'download',
28506 cls : 'btn btn-default',
28507 html : '<i class="fa fa-download"></i>'
28513 cls : 'btn-group roo-upload-cropbox-crop',
28518 cls : 'btn btn-default',
28519 html : '<i class="fa fa-crop"></i>'
28525 cls : 'btn-group roo-upload-cropbox-trash',
28530 cls : 'btn btn-default',
28531 html : '<i class="fa fa-trash"></i>'
28537 cls : 'btn-group roo-upload-cropbox-rotate-right',
28538 action : 'rotate-right',
28542 cls : 'btn btn-default',
28543 html : '<i class="fa fa-repeat"></i>'
28551 cls : 'btn-group roo-upload-cropbox-rotate-left',
28552 action : 'rotate-left',
28556 cls : 'btn btn-default',
28557 html : '<i class="fa fa-undo"></i>'
28563 cls : 'btn-group roo-upload-cropbox-rotate-right',
28564 action : 'rotate-right',
28568 cls : 'btn btn-default',
28569 html : '<i class="fa fa-repeat"></i>'
28582 * @class Roo.bootstrap.DocumentManager
28583 * @extends Roo.bootstrap.Component
28584 * Bootstrap DocumentManager class
28585 * @cfg {String} paramName default 'imageUpload'
28586 * @cfg {String} toolTipName default 'filename'
28587 * @cfg {String} method default POST
28588 * @cfg {String} url action url
28589 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28590 * @cfg {Boolean} multiple multiple upload default true
28591 * @cfg {Number} thumbSize default 300
28592 * @cfg {String} fieldLabel
28593 * @cfg {Number} labelWidth default 4
28594 * @cfg {String} labelAlign (left|top) default left
28595 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28596 * @cfg {Number} labellg set the width of label (1-12)
28597 * @cfg {Number} labelmd set the width of label (1-12)
28598 * @cfg {Number} labelsm set the width of label (1-12)
28599 * @cfg {Number} labelxs set the width of label (1-12)
28602 * Create a new DocumentManager
28603 * @param {Object} config The config object
28606 Roo.bootstrap.DocumentManager = function(config){
28607 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28610 this.delegates = [];
28615 * Fire when initial the DocumentManager
28616 * @param {Roo.bootstrap.DocumentManager} this
28621 * inspect selected file
28622 * @param {Roo.bootstrap.DocumentManager} this
28623 * @param {File} file
28628 * Fire when xhr load exception
28629 * @param {Roo.bootstrap.DocumentManager} this
28630 * @param {XMLHttpRequest} xhr
28632 "exception" : true,
28634 * @event afterupload
28635 * Fire when xhr load exception
28636 * @param {Roo.bootstrap.DocumentManager} this
28637 * @param {XMLHttpRequest} xhr
28639 "afterupload" : true,
28642 * prepare the form data
28643 * @param {Roo.bootstrap.DocumentManager} this
28644 * @param {Object} formData
28649 * Fire when remove the file
28650 * @param {Roo.bootstrap.DocumentManager} this
28651 * @param {Object} file
28656 * Fire after refresh the file
28657 * @param {Roo.bootstrap.DocumentManager} this
28662 * Fire after click the image
28663 * @param {Roo.bootstrap.DocumentManager} this
28664 * @param {Object} file
28669 * Fire when upload a image and editable set to true
28670 * @param {Roo.bootstrap.DocumentManager} this
28671 * @param {Object} file
28675 * @event beforeselectfile
28676 * Fire before select file
28677 * @param {Roo.bootstrap.DocumentManager} this
28679 "beforeselectfile" : true,
28682 * Fire before process file
28683 * @param {Roo.bootstrap.DocumentManager} this
28684 * @param {Object} file
28688 * @event previewrendered
28689 * Fire when preview rendered
28690 * @param {Roo.bootstrap.DocumentManager} this
28691 * @param {Object} file
28693 "previewrendered" : true
28698 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28707 paramName : 'imageUpload',
28708 toolTipName : 'filename',
28711 labelAlign : 'left',
28721 getAutoCreate : function()
28723 var managerWidget = {
28725 cls : 'roo-document-manager',
28729 cls : 'roo-document-manager-selector',
28734 cls : 'roo-document-manager-uploader',
28738 cls : 'roo-document-manager-upload-btn',
28739 html : '<i class="fa fa-plus"></i>'
28750 cls : 'column col-md-12',
28755 if(this.fieldLabel.length){
28760 cls : 'column col-md-12',
28761 html : this.fieldLabel
28765 cls : 'column col-md-12',
28770 if(this.labelAlign == 'left'){
28775 html : this.fieldLabel
28784 if(this.labelWidth > 12){
28785 content[0].style = "width: " + this.labelWidth + 'px';
28788 if(this.labelWidth < 13 && this.labelmd == 0){
28789 this.labelmd = this.labelWidth;
28792 if(this.labellg > 0){
28793 content[0].cls += ' col-lg-' + this.labellg;
28794 content[1].cls += ' col-lg-' + (12 - this.labellg);
28797 if(this.labelmd > 0){
28798 content[0].cls += ' col-md-' + this.labelmd;
28799 content[1].cls += ' col-md-' + (12 - this.labelmd);
28802 if(this.labelsm > 0){
28803 content[0].cls += ' col-sm-' + this.labelsm;
28804 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28807 if(this.labelxs > 0){
28808 content[0].cls += ' col-xs-' + this.labelxs;
28809 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28817 cls : 'row clearfix',
28825 initEvents : function()
28827 this.managerEl = this.el.select('.roo-document-manager', true).first();
28828 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28830 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28831 this.selectorEl.hide();
28834 this.selectorEl.attr('multiple', 'multiple');
28837 this.selectorEl.on('change', this.onFileSelected, this);
28839 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28840 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28842 this.uploader.on('click', this.onUploaderClick, this);
28844 this.renderProgressDialog();
28848 window.addEventListener("resize", function() { _this.refresh(); } );
28850 this.fireEvent('initial', this);
28853 renderProgressDialog : function()
28857 this.progressDialog = new Roo.bootstrap.Modal({
28858 cls : 'roo-document-manager-progress-dialog',
28859 allow_close : false,
28869 btnclick : function() {
28870 _this.uploadCancel();
28876 this.progressDialog.render(Roo.get(document.body));
28878 this.progress = new Roo.bootstrap.Progress({
28879 cls : 'roo-document-manager-progress',
28884 this.progress.render(this.progressDialog.getChildContainer());
28886 this.progressBar = new Roo.bootstrap.ProgressBar({
28887 cls : 'roo-document-manager-progress-bar',
28890 aria_valuemax : 12,
28894 this.progressBar.render(this.progress.getChildContainer());
28897 onUploaderClick : function(e)
28899 e.preventDefault();
28901 if(this.fireEvent('beforeselectfile', this) != false){
28902 this.selectorEl.dom.click();
28907 onFileSelected : function(e)
28909 e.preventDefault();
28911 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28915 Roo.each(this.selectorEl.dom.files, function(file){
28916 if(this.fireEvent('inspect', this, file) != false){
28917 this.files.push(file);
28927 this.selectorEl.dom.value = '';
28929 if(!this.files || !this.files.length){
28933 if(this.boxes > 0 && this.files.length > this.boxes){
28934 this.files = this.files.slice(0, this.boxes);
28937 this.uploader.show();
28939 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28940 this.uploader.hide();
28949 Roo.each(this.files, function(file){
28951 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28952 var f = this.renderPreview(file);
28957 if(file.type.indexOf('image') != -1){
28958 this.delegates.push(
28960 _this.process(file);
28961 }).createDelegate(this)
28969 _this.process(file);
28970 }).createDelegate(this)
28975 this.files = files;
28977 this.delegates = this.delegates.concat(docs);
28979 if(!this.delegates.length){
28984 this.progressBar.aria_valuemax = this.delegates.length;
28991 arrange : function()
28993 if(!this.delegates.length){
28994 this.progressDialog.hide();
28999 var delegate = this.delegates.shift();
29001 this.progressDialog.show();
29003 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29005 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29010 refresh : function()
29012 this.uploader.show();
29014 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29015 this.uploader.hide();
29018 Roo.isTouch ? this.closable(false) : this.closable(true);
29020 this.fireEvent('refresh', this);
29023 onRemove : function(e, el, o)
29025 e.preventDefault();
29027 this.fireEvent('remove', this, o);
29031 remove : function(o)
29035 Roo.each(this.files, function(file){
29036 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29045 this.files = files;
29052 Roo.each(this.files, function(file){
29057 file.target.remove();
29066 onClick : function(e, el, o)
29068 e.preventDefault();
29070 this.fireEvent('click', this, o);
29074 closable : function(closable)
29076 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29078 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29090 xhrOnLoad : function(xhr)
29092 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29096 if (xhr.readyState !== 4) {
29098 this.fireEvent('exception', this, xhr);
29102 var response = Roo.decode(xhr.responseText);
29104 if(!response.success){
29106 this.fireEvent('exception', this, xhr);
29110 var file = this.renderPreview(response.data);
29112 this.files.push(file);
29116 this.fireEvent('afterupload', this, xhr);
29120 xhrOnError : function(xhr)
29122 Roo.log('xhr on error');
29124 var response = Roo.decode(xhr.responseText);
29131 process : function(file)
29133 if(this.fireEvent('process', this, file) !== false){
29134 if(this.editable && file.type.indexOf('image') != -1){
29135 this.fireEvent('edit', this, file);
29139 this.uploadStart(file, false);
29146 uploadStart : function(file, crop)
29148 this.xhr = new XMLHttpRequest();
29150 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29155 file.xhr = this.xhr;
29157 this.managerEl.createChild({
29159 cls : 'roo-document-manager-loading',
29163 tooltip : file.name,
29164 cls : 'roo-document-manager-thumb',
29165 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29171 this.xhr.open(this.method, this.url, true);
29174 "Accept": "application/json",
29175 "Cache-Control": "no-cache",
29176 "X-Requested-With": "XMLHttpRequest"
29179 for (var headerName in headers) {
29180 var headerValue = headers[headerName];
29182 this.xhr.setRequestHeader(headerName, headerValue);
29188 this.xhr.onload = function()
29190 _this.xhrOnLoad(_this.xhr);
29193 this.xhr.onerror = function()
29195 _this.xhrOnError(_this.xhr);
29198 var formData = new FormData();
29200 formData.append('returnHTML', 'NO');
29203 formData.append('crop', crop);
29206 formData.append(this.paramName, file, file.name);
29213 if(this.fireEvent('prepare', this, formData, options) != false){
29215 if(options.manually){
29219 this.xhr.send(formData);
29223 this.uploadCancel();
29226 uploadCancel : function()
29232 this.delegates = [];
29234 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29241 renderPreview : function(file)
29243 if(typeof(file.target) != 'undefined' && file.target){
29247 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29249 var previewEl = this.managerEl.createChild({
29251 cls : 'roo-document-manager-preview',
29255 tooltip : file[this.toolTipName],
29256 cls : 'roo-document-manager-thumb',
29257 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29262 html : '<i class="fa fa-times-circle"></i>'
29267 var close = previewEl.select('button.close', true).first();
29269 close.on('click', this.onRemove, this, file);
29271 file.target = previewEl;
29273 var image = previewEl.select('img', true).first();
29277 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29279 image.on('click', this.onClick, this, file);
29281 this.fireEvent('previewrendered', this, file);
29287 onPreviewLoad : function(file, image)
29289 if(typeof(file.target) == 'undefined' || !file.target){
29293 var width = image.dom.naturalWidth || image.dom.width;
29294 var height = image.dom.naturalHeight || image.dom.height;
29296 if(width > height){
29297 file.target.addClass('wide');
29301 file.target.addClass('tall');
29306 uploadFromSource : function(file, crop)
29308 this.xhr = new XMLHttpRequest();
29310 this.managerEl.createChild({
29312 cls : 'roo-document-manager-loading',
29316 tooltip : file.name,
29317 cls : 'roo-document-manager-thumb',
29318 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29324 this.xhr.open(this.method, this.url, true);
29327 "Accept": "application/json",
29328 "Cache-Control": "no-cache",
29329 "X-Requested-With": "XMLHttpRequest"
29332 for (var headerName in headers) {
29333 var headerValue = headers[headerName];
29335 this.xhr.setRequestHeader(headerName, headerValue);
29341 this.xhr.onload = function()
29343 _this.xhrOnLoad(_this.xhr);
29346 this.xhr.onerror = function()
29348 _this.xhrOnError(_this.xhr);
29351 var formData = new FormData();
29353 formData.append('returnHTML', 'NO');
29355 formData.append('crop', crop);
29357 if(typeof(file.filename) != 'undefined'){
29358 formData.append('filename', file.filename);
29361 if(typeof(file.mimetype) != 'undefined'){
29362 formData.append('mimetype', file.mimetype);
29367 if(this.fireEvent('prepare', this, formData) != false){
29368 this.xhr.send(formData);
29378 * @class Roo.bootstrap.DocumentViewer
29379 * @extends Roo.bootstrap.Component
29380 * Bootstrap DocumentViewer class
29381 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29382 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29385 * Create a new DocumentViewer
29386 * @param {Object} config The config object
29389 Roo.bootstrap.DocumentViewer = function(config){
29390 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29395 * Fire after initEvent
29396 * @param {Roo.bootstrap.DocumentViewer} this
29402 * @param {Roo.bootstrap.DocumentViewer} this
29407 * Fire after download button
29408 * @param {Roo.bootstrap.DocumentViewer} this
29413 * Fire after trash button
29414 * @param {Roo.bootstrap.DocumentViewer} this
29421 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29423 showDownload : true,
29427 getAutoCreate : function()
29431 cls : 'roo-document-viewer',
29435 cls : 'roo-document-viewer-body',
29439 cls : 'roo-document-viewer-thumb',
29443 cls : 'roo-document-viewer-image'
29451 cls : 'roo-document-viewer-footer',
29454 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29458 cls : 'btn-group roo-document-viewer-download',
29462 cls : 'btn btn-default',
29463 html : '<i class="fa fa-download"></i>'
29469 cls : 'btn-group roo-document-viewer-trash',
29473 cls : 'btn btn-default',
29474 html : '<i class="fa fa-trash"></i>'
29487 initEvents : function()
29489 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29490 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29492 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29493 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29495 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29496 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29498 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29499 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29501 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29502 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29504 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29505 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29507 this.bodyEl.on('click', this.onClick, this);
29508 this.downloadBtn.on('click', this.onDownload, this);
29509 this.trashBtn.on('click', this.onTrash, this);
29511 this.downloadBtn.hide();
29512 this.trashBtn.hide();
29514 if(this.showDownload){
29515 this.downloadBtn.show();
29518 if(this.showTrash){
29519 this.trashBtn.show();
29522 if(!this.showDownload && !this.showTrash) {
29523 this.footerEl.hide();
29528 initial : function()
29530 this.fireEvent('initial', this);
29534 onClick : function(e)
29536 e.preventDefault();
29538 this.fireEvent('click', this);
29541 onDownload : function(e)
29543 e.preventDefault();
29545 this.fireEvent('download', this);
29548 onTrash : function(e)
29550 e.preventDefault();
29552 this.fireEvent('trash', this);
29564 * @class Roo.bootstrap.NavProgressBar
29565 * @extends Roo.bootstrap.Component
29566 * Bootstrap NavProgressBar class
29569 * Create a new nav progress bar
29570 * @param {Object} config The config object
29573 Roo.bootstrap.NavProgressBar = function(config){
29574 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29576 this.bullets = this.bullets || [];
29578 // Roo.bootstrap.NavProgressBar.register(this);
29582 * Fires when the active item changes
29583 * @param {Roo.bootstrap.NavProgressBar} this
29584 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29585 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29592 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29597 getAutoCreate : function()
29599 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29603 cls : 'roo-navigation-bar-group',
29607 cls : 'roo-navigation-top-bar'
29611 cls : 'roo-navigation-bullets-bar',
29615 cls : 'roo-navigation-bar'
29622 cls : 'roo-navigation-bottom-bar'
29632 initEvents: function()
29637 onRender : function(ct, position)
29639 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29641 if(this.bullets.length){
29642 Roo.each(this.bullets, function(b){
29651 addItem : function(cfg)
29653 var item = new Roo.bootstrap.NavProgressItem(cfg);
29655 item.parentId = this.id;
29656 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29659 var top = new Roo.bootstrap.Element({
29661 cls : 'roo-navigation-bar-text'
29664 var bottom = new Roo.bootstrap.Element({
29666 cls : 'roo-navigation-bar-text'
29669 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29670 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29672 var topText = new Roo.bootstrap.Element({
29674 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29677 var bottomText = new Roo.bootstrap.Element({
29679 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29682 topText.onRender(top.el, null);
29683 bottomText.onRender(bottom.el, null);
29686 item.bottomEl = bottom;
29689 this.barItems.push(item);
29694 getActive : function()
29696 var active = false;
29698 Roo.each(this.barItems, function(v){
29700 if (!v.isActive()) {
29712 setActiveItem : function(item)
29716 Roo.each(this.barItems, function(v){
29717 if (v.rid == item.rid) {
29721 if (v.isActive()) {
29722 v.setActive(false);
29727 item.setActive(true);
29729 this.fireEvent('changed', this, item, prev);
29732 getBarItem: function(rid)
29736 Roo.each(this.barItems, function(e) {
29737 if (e.rid != rid) {
29748 indexOfItem : function(item)
29752 Roo.each(this.barItems, function(v, i){
29754 if (v.rid != item.rid) {
29765 setActiveNext : function()
29767 var i = this.indexOfItem(this.getActive());
29769 if (i > this.barItems.length) {
29773 this.setActiveItem(this.barItems[i+1]);
29776 setActivePrev : function()
29778 var i = this.indexOfItem(this.getActive());
29784 this.setActiveItem(this.barItems[i-1]);
29787 format : function()
29789 if(!this.barItems.length){
29793 var width = 100 / this.barItems.length;
29795 Roo.each(this.barItems, function(i){
29796 i.el.setStyle('width', width + '%');
29797 i.topEl.el.setStyle('width', width + '%');
29798 i.bottomEl.el.setStyle('width', width + '%');
29807 * Nav Progress Item
29812 * @class Roo.bootstrap.NavProgressItem
29813 * @extends Roo.bootstrap.Component
29814 * Bootstrap NavProgressItem class
29815 * @cfg {String} rid the reference id
29816 * @cfg {Boolean} active (true|false) Is item active default false
29817 * @cfg {Boolean} disabled (true|false) Is item active default false
29818 * @cfg {String} html
29819 * @cfg {String} position (top|bottom) text position default bottom
29820 * @cfg {String} icon show icon instead of number
29823 * Create a new NavProgressItem
29824 * @param {Object} config The config object
29826 Roo.bootstrap.NavProgressItem = function(config){
29827 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29832 * The raw click event for the entire grid.
29833 * @param {Roo.bootstrap.NavProgressItem} this
29834 * @param {Roo.EventObject} e
29841 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29847 position : 'bottom',
29850 getAutoCreate : function()
29852 var iconCls = 'roo-navigation-bar-item-icon';
29854 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29858 cls: 'roo-navigation-bar-item',
29868 cfg.cls += ' active';
29871 cfg.cls += ' disabled';
29877 disable : function()
29879 this.setDisabled(true);
29882 enable : function()
29884 this.setDisabled(false);
29887 initEvents: function()
29889 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29891 this.iconEl.on('click', this.onClick, this);
29894 onClick : function(e)
29896 e.preventDefault();
29902 if(this.fireEvent('click', this, e) === false){
29906 this.parent().setActiveItem(this);
29909 isActive: function ()
29911 return this.active;
29914 setActive : function(state)
29916 if(this.active == state){
29920 this.active = state;
29923 this.el.addClass('active');
29927 this.el.removeClass('active');
29932 setDisabled : function(state)
29934 if(this.disabled == state){
29938 this.disabled = state;
29941 this.el.addClass('disabled');
29945 this.el.removeClass('disabled');
29948 tooltipEl : function()
29950 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29963 * @class Roo.bootstrap.FieldLabel
29964 * @extends Roo.bootstrap.Component
29965 * Bootstrap FieldLabel class
29966 * @cfg {String} html contents of the element
29967 * @cfg {String} tag tag of the element default label
29968 * @cfg {String} cls class of the element
29969 * @cfg {String} target label target
29970 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29971 * @cfg {String} invalidClass default "text-warning"
29972 * @cfg {String} validClass default "text-success"
29973 * @cfg {String} iconTooltip default "This field is required"
29974 * @cfg {String} indicatorpos (left|right) default left
29977 * Create a new FieldLabel
29978 * @param {Object} config The config object
29981 Roo.bootstrap.FieldLabel = function(config){
29982 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29987 * Fires after the field has been marked as invalid.
29988 * @param {Roo.form.FieldLabel} this
29989 * @param {String} msg The validation message
29994 * Fires after the field has been validated with no errors.
29995 * @param {Roo.form.FieldLabel} this
30001 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30008 invalidClass : 'has-warning',
30009 validClass : 'has-success',
30010 iconTooltip : 'This field is required',
30011 indicatorpos : 'left',
30013 getAutoCreate : function(){
30016 if (!this.allowBlank) {
30022 cls : 'roo-bootstrap-field-label ' + this.cls,
30027 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30028 tooltip : this.iconTooltip
30037 if(this.indicatorpos == 'right'){
30040 cls : 'roo-bootstrap-field-label ' + this.cls,
30049 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30050 tooltip : this.iconTooltip
30059 initEvents: function()
30061 Roo.bootstrap.Element.superclass.initEvents.call(this);
30063 this.indicator = this.indicatorEl();
30065 if(this.indicator){
30066 this.indicator.removeClass('visible');
30067 this.indicator.addClass('invisible');
30070 Roo.bootstrap.FieldLabel.register(this);
30073 indicatorEl : function()
30075 var indicator = this.el.select('i.roo-required-indicator',true).first();
30086 * Mark this field as valid
30088 markValid : function()
30090 if(this.indicator){
30091 this.indicator.removeClass('visible');
30092 this.indicator.addClass('invisible');
30095 this.el.removeClass(this.invalidClass);
30097 this.el.addClass(this.validClass);
30099 this.fireEvent('valid', this);
30103 * Mark this field as invalid
30104 * @param {String} msg The validation message
30106 markInvalid : function(msg)
30108 if(this.indicator){
30109 this.indicator.removeClass('invisible');
30110 this.indicator.addClass('visible');
30113 this.el.removeClass(this.validClass);
30115 this.el.addClass(this.invalidClass);
30117 this.fireEvent('invalid', this, msg);
30123 Roo.apply(Roo.bootstrap.FieldLabel, {
30128 * register a FieldLabel Group
30129 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30131 register : function(label)
30133 if(this.groups.hasOwnProperty(label.target)){
30137 this.groups[label.target] = label;
30141 * fetch a FieldLabel Group based on the target
30142 * @param {string} target
30143 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30145 get: function(target) {
30146 if (typeof(this.groups[target]) == 'undefined') {
30150 return this.groups[target] ;
30159 * page DateSplitField.
30165 * @class Roo.bootstrap.DateSplitField
30166 * @extends Roo.bootstrap.Component
30167 * Bootstrap DateSplitField class
30168 * @cfg {string} fieldLabel - the label associated
30169 * @cfg {Number} labelWidth set the width of label (0-12)
30170 * @cfg {String} labelAlign (top|left)
30171 * @cfg {Boolean} dayAllowBlank (true|false) default false
30172 * @cfg {Boolean} monthAllowBlank (true|false) default false
30173 * @cfg {Boolean} yearAllowBlank (true|false) default false
30174 * @cfg {string} dayPlaceholder
30175 * @cfg {string} monthPlaceholder
30176 * @cfg {string} yearPlaceholder
30177 * @cfg {string} dayFormat default 'd'
30178 * @cfg {string} monthFormat default 'm'
30179 * @cfg {string} yearFormat default 'Y'
30180 * @cfg {Number} labellg set the width of label (1-12)
30181 * @cfg {Number} labelmd set the width of label (1-12)
30182 * @cfg {Number} labelsm set the width of label (1-12)
30183 * @cfg {Number} labelxs set the width of label (1-12)
30187 * Create a new DateSplitField
30188 * @param {Object} config The config object
30191 Roo.bootstrap.DateSplitField = function(config){
30192 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30198 * getting the data of years
30199 * @param {Roo.bootstrap.DateSplitField} this
30200 * @param {Object} years
30205 * getting the data of days
30206 * @param {Roo.bootstrap.DateSplitField} this
30207 * @param {Object} days
30212 * Fires after the field has been marked as invalid.
30213 * @param {Roo.form.Field} this
30214 * @param {String} msg The validation message
30219 * Fires after the field has been validated with no errors.
30220 * @param {Roo.form.Field} this
30226 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30229 labelAlign : 'top',
30231 dayAllowBlank : false,
30232 monthAllowBlank : false,
30233 yearAllowBlank : false,
30234 dayPlaceholder : '',
30235 monthPlaceholder : '',
30236 yearPlaceholder : '',
30240 isFormField : true,
30246 getAutoCreate : function()
30250 cls : 'row roo-date-split-field-group',
30255 cls : 'form-hidden-field roo-date-split-field-group-value',
30261 var labelCls = 'col-md-12';
30262 var contentCls = 'col-md-4';
30264 if(this.fieldLabel){
30268 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30272 html : this.fieldLabel
30277 if(this.labelAlign == 'left'){
30279 if(this.labelWidth > 12){
30280 label.style = "width: " + this.labelWidth + 'px';
30283 if(this.labelWidth < 13 && this.labelmd == 0){
30284 this.labelmd = this.labelWidth;
30287 if(this.labellg > 0){
30288 labelCls = ' col-lg-' + this.labellg;
30289 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30292 if(this.labelmd > 0){
30293 labelCls = ' col-md-' + this.labelmd;
30294 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30297 if(this.labelsm > 0){
30298 labelCls = ' col-sm-' + this.labelsm;
30299 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30302 if(this.labelxs > 0){
30303 labelCls = ' col-xs-' + this.labelxs;
30304 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30308 label.cls += ' ' + labelCls;
30310 cfg.cn.push(label);
30313 Roo.each(['day', 'month', 'year'], function(t){
30316 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30323 inputEl: function ()
30325 return this.el.select('.roo-date-split-field-group-value', true).first();
30328 onRender : function(ct, position)
30332 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30334 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30336 this.dayField = new Roo.bootstrap.ComboBox({
30337 allowBlank : this.dayAllowBlank,
30338 alwaysQuery : true,
30339 displayField : 'value',
30342 forceSelection : true,
30344 placeholder : this.dayPlaceholder,
30345 selectOnFocus : true,
30346 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30347 triggerAction : 'all',
30349 valueField : 'value',
30350 store : new Roo.data.SimpleStore({
30351 data : (function() {
30353 _this.fireEvent('days', _this, days);
30356 fields : [ 'value' ]
30359 select : function (_self, record, index)
30361 _this.setValue(_this.getValue());
30366 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30368 this.monthField = new Roo.bootstrap.MonthField({
30369 after : '<i class=\"fa fa-calendar\"></i>',
30370 allowBlank : this.monthAllowBlank,
30371 placeholder : this.monthPlaceholder,
30374 render : function (_self)
30376 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30377 e.preventDefault();
30381 select : function (_self, oldvalue, newvalue)
30383 _this.setValue(_this.getValue());
30388 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30390 this.yearField = new Roo.bootstrap.ComboBox({
30391 allowBlank : this.yearAllowBlank,
30392 alwaysQuery : true,
30393 displayField : 'value',
30396 forceSelection : true,
30398 placeholder : this.yearPlaceholder,
30399 selectOnFocus : true,
30400 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30401 triggerAction : 'all',
30403 valueField : 'value',
30404 store : new Roo.data.SimpleStore({
30405 data : (function() {
30407 _this.fireEvent('years', _this, years);
30410 fields : [ 'value' ]
30413 select : function (_self, record, index)
30415 _this.setValue(_this.getValue());
30420 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30423 setValue : function(v, format)
30425 this.inputEl.dom.value = v;
30427 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30429 var d = Date.parseDate(v, f);
30436 this.setDay(d.format(this.dayFormat));
30437 this.setMonth(d.format(this.monthFormat));
30438 this.setYear(d.format(this.yearFormat));
30445 setDay : function(v)
30447 this.dayField.setValue(v);
30448 this.inputEl.dom.value = this.getValue();
30453 setMonth : function(v)
30455 this.monthField.setValue(v, true);
30456 this.inputEl.dom.value = this.getValue();
30461 setYear : function(v)
30463 this.yearField.setValue(v);
30464 this.inputEl.dom.value = this.getValue();
30469 getDay : function()
30471 return this.dayField.getValue();
30474 getMonth : function()
30476 return this.monthField.getValue();
30479 getYear : function()
30481 return this.yearField.getValue();
30484 getValue : function()
30486 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30488 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30498 this.inputEl.dom.value = '';
30503 validate : function()
30505 var d = this.dayField.validate();
30506 var m = this.monthField.validate();
30507 var y = this.yearField.validate();
30512 (!this.dayAllowBlank && !d) ||
30513 (!this.monthAllowBlank && !m) ||
30514 (!this.yearAllowBlank && !y)
30519 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30528 this.markInvalid();
30533 markValid : function()
30536 var label = this.el.select('label', true).first();
30537 var icon = this.el.select('i.fa-star', true).first();
30543 this.fireEvent('valid', this);
30547 * Mark this field as invalid
30548 * @param {String} msg The validation message
30550 markInvalid : function(msg)
30553 var label = this.el.select('label', true).first();
30554 var icon = this.el.select('i.fa-star', true).first();
30556 if(label && !icon){
30557 this.el.select('.roo-date-split-field-label', true).createChild({
30559 cls : 'text-danger fa fa-lg fa-star',
30560 tooltip : 'This field is required',
30561 style : 'margin-right:5px;'
30565 this.fireEvent('invalid', this, msg);
30568 clearInvalid : function()
30570 var label = this.el.select('label', true).first();
30571 var icon = this.el.select('i.fa-star', true).first();
30577 this.fireEvent('valid', this);
30580 getName: function()
30590 * http://masonry.desandro.com
30592 * The idea is to render all the bricks based on vertical width...
30594 * The original code extends 'outlayer' - we might need to use that....
30600 * @class Roo.bootstrap.LayoutMasonry
30601 * @extends Roo.bootstrap.Component
30602 * Bootstrap Layout Masonry class
30605 * Create a new Element
30606 * @param {Object} config The config object
30609 Roo.bootstrap.LayoutMasonry = function(config){
30611 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30615 Roo.bootstrap.LayoutMasonry.register(this);
30621 * Fire after layout the items
30622 * @param {Roo.bootstrap.LayoutMasonry} this
30623 * @param {Roo.EventObject} e
30630 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30633 * @cfg {Boolean} isLayoutInstant = no animation?
30635 isLayoutInstant : false, // needed?
30638 * @cfg {Number} boxWidth width of the columns
30643 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30648 * @cfg {Number} padWidth padding below box..
30653 * @cfg {Number} gutter gutter width..
30658 * @cfg {Number} maxCols maximum number of columns
30664 * @cfg {Boolean} isAutoInitial defalut true
30666 isAutoInitial : true,
30671 * @cfg {Boolean} isHorizontal defalut false
30673 isHorizontal : false,
30675 currentSize : null,
30681 bricks: null, //CompositeElement
30685 _isLayoutInited : false,
30687 // isAlternative : false, // only use for vertical layout...
30690 * @cfg {Number} alternativePadWidth padding below box..
30692 alternativePadWidth : 50,
30694 selectedBrick : [],
30696 getAutoCreate : function(){
30698 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30702 cls: 'blog-masonary-wrapper ' + this.cls,
30704 cls : 'mas-boxes masonary'
30711 getChildContainer: function( )
30713 if (this.boxesEl) {
30714 return this.boxesEl;
30717 this.boxesEl = this.el.select('.mas-boxes').first();
30719 return this.boxesEl;
30723 initEvents : function()
30727 if(this.isAutoInitial){
30728 Roo.log('hook children rendered');
30729 this.on('childrenrendered', function() {
30730 Roo.log('children rendered');
30736 initial : function()
30738 this.selectedBrick = [];
30740 this.currentSize = this.el.getBox(true);
30742 Roo.EventManager.onWindowResize(this.resize, this);
30744 if(!this.isAutoInitial){
30752 //this.layout.defer(500,this);
30756 resize : function()
30758 var cs = this.el.getBox(true);
30761 this.currentSize.width == cs.width &&
30762 this.currentSize.x == cs.x &&
30763 this.currentSize.height == cs.height &&
30764 this.currentSize.y == cs.y
30766 Roo.log("no change in with or X or Y");
30770 this.currentSize = cs;
30776 layout : function()
30778 this._resetLayout();
30780 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30782 this.layoutItems( isInstant );
30784 this._isLayoutInited = true;
30786 this.fireEvent('layout', this);
30790 _resetLayout : function()
30792 if(this.isHorizontal){
30793 this.horizontalMeasureColumns();
30797 this.verticalMeasureColumns();
30801 verticalMeasureColumns : function()
30803 this.getContainerWidth();
30805 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30806 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30810 var boxWidth = this.boxWidth + this.padWidth;
30812 if(this.containerWidth < this.boxWidth){
30813 boxWidth = this.containerWidth
30816 var containerWidth = this.containerWidth;
30818 var cols = Math.floor(containerWidth / boxWidth);
30820 this.cols = Math.max( cols, 1 );
30822 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30824 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30826 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30828 this.colWidth = boxWidth + avail - this.padWidth;
30830 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30831 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30834 horizontalMeasureColumns : function()
30836 this.getContainerWidth();
30838 var boxWidth = this.boxWidth;
30840 if(this.containerWidth < boxWidth){
30841 boxWidth = this.containerWidth;
30844 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30846 this.el.setHeight(boxWidth);
30850 getContainerWidth : function()
30852 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30855 layoutItems : function( isInstant )
30857 Roo.log(this.bricks);
30859 var items = Roo.apply([], this.bricks);
30861 if(this.isHorizontal){
30862 this._horizontalLayoutItems( items , isInstant );
30866 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30867 // this._verticalAlternativeLayoutItems( items , isInstant );
30871 this._verticalLayoutItems( items , isInstant );
30875 _verticalLayoutItems : function ( items , isInstant)
30877 if ( !items || !items.length ) {
30882 ['xs', 'xs', 'xs', 'tall'],
30883 ['xs', 'xs', 'tall'],
30884 ['xs', 'xs', 'sm'],
30885 ['xs', 'xs', 'xs'],
30891 ['sm', 'xs', 'xs'],
30895 ['tall', 'xs', 'xs', 'xs'],
30896 ['tall', 'xs', 'xs'],
30908 Roo.each(items, function(item, k){
30910 switch (item.size) {
30911 // these layouts take up a full box,
30922 boxes.push([item]);
30945 var filterPattern = function(box, length)
30953 var pattern = box.slice(0, length);
30957 Roo.each(pattern, function(i){
30958 format.push(i.size);
30961 Roo.each(standard, function(s){
30963 if(String(s) != String(format)){
30972 if(!match && length == 1){
30977 filterPattern(box, length - 1);
30981 queue.push(pattern);
30983 box = box.slice(length, box.length);
30985 filterPattern(box, 4);
30991 Roo.each(boxes, function(box, k){
30997 if(box.length == 1){
31002 filterPattern(box, 4);
31006 this._processVerticalLayoutQueue( queue, isInstant );
31010 // _verticalAlternativeLayoutItems : function( items , isInstant )
31012 // if ( !items || !items.length ) {
31016 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31020 _horizontalLayoutItems : function ( items , isInstant)
31022 if ( !items || !items.length || items.length < 3) {
31028 var eItems = items.slice(0, 3);
31030 items = items.slice(3, items.length);
31033 ['xs', 'xs', 'xs', 'wide'],
31034 ['xs', 'xs', 'wide'],
31035 ['xs', 'xs', 'sm'],
31036 ['xs', 'xs', 'xs'],
31042 ['sm', 'xs', 'xs'],
31046 ['wide', 'xs', 'xs', 'xs'],
31047 ['wide', 'xs', 'xs'],
31060 Roo.each(items, function(item, k){
31062 switch (item.size) {
31073 boxes.push([item]);
31097 var filterPattern = function(box, length)
31105 var pattern = box.slice(0, length);
31109 Roo.each(pattern, function(i){
31110 format.push(i.size);
31113 Roo.each(standard, function(s){
31115 if(String(s) != String(format)){
31124 if(!match && length == 1){
31129 filterPattern(box, length - 1);
31133 queue.push(pattern);
31135 box = box.slice(length, box.length);
31137 filterPattern(box, 4);
31143 Roo.each(boxes, function(box, k){
31149 if(box.length == 1){
31154 filterPattern(box, 4);
31161 var pos = this.el.getBox(true);
31165 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31167 var hit_end = false;
31169 Roo.each(queue, function(box){
31173 Roo.each(box, function(b){
31175 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31185 Roo.each(box, function(b){
31187 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31190 mx = Math.max(mx, b.x);
31194 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31198 Roo.each(box, function(b){
31200 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31214 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31217 /** Sets position of item in DOM
31218 * @param {Element} item
31219 * @param {Number} x - horizontal position
31220 * @param {Number} y - vertical position
31221 * @param {Boolean} isInstant - disables transitions
31223 _processVerticalLayoutQueue : function( queue, isInstant )
31225 var pos = this.el.getBox(true);
31230 for (var i = 0; i < this.cols; i++){
31234 Roo.each(queue, function(box, k){
31236 var col = k % this.cols;
31238 Roo.each(box, function(b,kk){
31240 b.el.position('absolute');
31242 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31243 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31245 if(b.size == 'md-left' || b.size == 'md-right'){
31246 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31247 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31250 b.el.setWidth(width);
31251 b.el.setHeight(height);
31253 b.el.select('iframe',true).setSize(width,height);
31257 for (var i = 0; i < this.cols; i++){
31259 if(maxY[i] < maxY[col]){
31264 col = Math.min(col, i);
31268 x = pos.x + col * (this.colWidth + this.padWidth);
31272 var positions = [];
31274 switch (box.length){
31276 positions = this.getVerticalOneBoxColPositions(x, y, box);
31279 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31282 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31285 positions = this.getVerticalFourBoxColPositions(x, y, box);
31291 Roo.each(box, function(b,kk){
31293 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31295 var sz = b.el.getSize();
31297 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31305 for (var i = 0; i < this.cols; i++){
31306 mY = Math.max(mY, maxY[i]);
31309 this.el.setHeight(mY - pos.y);
31313 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31315 // var pos = this.el.getBox(true);
31318 // var maxX = pos.right;
31320 // var maxHeight = 0;
31322 // Roo.each(items, function(item, k){
31326 // item.el.position('absolute');
31328 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31330 // item.el.setWidth(width);
31332 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31334 // item.el.setHeight(height);
31337 // item.el.setXY([x, y], isInstant ? false : true);
31339 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31342 // y = y + height + this.alternativePadWidth;
31344 // maxHeight = maxHeight + height + this.alternativePadWidth;
31348 // this.el.setHeight(maxHeight);
31352 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31354 var pos = this.el.getBox(true);
31359 var maxX = pos.right;
31361 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31363 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31365 Roo.each(queue, function(box, k){
31367 Roo.each(box, function(b, kk){
31369 b.el.position('absolute');
31371 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31372 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31374 if(b.size == 'md-left' || b.size == 'md-right'){
31375 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31376 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31379 b.el.setWidth(width);
31380 b.el.setHeight(height);
31388 var positions = [];
31390 switch (box.length){
31392 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31395 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31398 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31401 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31407 Roo.each(box, function(b,kk){
31409 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31411 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31419 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31421 Roo.each(eItems, function(b,k){
31423 b.size = (k == 0) ? 'sm' : 'xs';
31424 b.x = (k == 0) ? 2 : 1;
31425 b.y = (k == 0) ? 2 : 1;
31427 b.el.position('absolute');
31429 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31431 b.el.setWidth(width);
31433 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31435 b.el.setHeight(height);
31439 var positions = [];
31442 x : maxX - this.unitWidth * 2 - this.gutter,
31447 x : maxX - this.unitWidth,
31448 y : minY + (this.unitWidth + this.gutter) * 2
31452 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31456 Roo.each(eItems, function(b,k){
31458 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31464 getVerticalOneBoxColPositions : function(x, y, box)
31468 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31470 if(box[0].size == 'md-left'){
31474 if(box[0].size == 'md-right'){
31479 x : x + (this.unitWidth + this.gutter) * rand,
31486 getVerticalTwoBoxColPositions : function(x, y, box)
31490 if(box[0].size == 'xs'){
31494 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31498 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31512 x : x + (this.unitWidth + this.gutter) * 2,
31513 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31520 getVerticalThreeBoxColPositions : function(x, y, box)
31524 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31532 x : x + (this.unitWidth + this.gutter) * 1,
31537 x : x + (this.unitWidth + this.gutter) * 2,
31545 if(box[0].size == 'xs' && box[1].size == 'xs'){
31554 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31558 x : x + (this.unitWidth + this.gutter) * 1,
31572 x : x + (this.unitWidth + this.gutter) * 2,
31577 x : x + (this.unitWidth + this.gutter) * 2,
31578 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31585 getVerticalFourBoxColPositions : function(x, y, box)
31589 if(box[0].size == 'xs'){
31598 y : y + (this.unitHeight + this.gutter) * 1
31603 y : y + (this.unitHeight + this.gutter) * 2
31607 x : x + (this.unitWidth + this.gutter) * 1,
31621 x : x + (this.unitWidth + this.gutter) * 2,
31626 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31627 y : y + (this.unitHeight + this.gutter) * 1
31631 x : x + (this.unitWidth + this.gutter) * 2,
31632 y : y + (this.unitWidth + this.gutter) * 2
31639 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31643 if(box[0].size == 'md-left'){
31645 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31652 if(box[0].size == 'md-right'){
31654 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31655 y : minY + (this.unitWidth + this.gutter) * 1
31661 var rand = Math.floor(Math.random() * (4 - box[0].y));
31664 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31665 y : minY + (this.unitWidth + this.gutter) * rand
31672 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31676 if(box[0].size == 'xs'){
31679 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31684 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31685 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31693 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31698 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31699 y : minY + (this.unitWidth + this.gutter) * 2
31706 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31710 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31713 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31718 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31719 y : minY + (this.unitWidth + this.gutter) * 1
31723 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31724 y : minY + (this.unitWidth + this.gutter) * 2
31731 if(box[0].size == 'xs' && box[1].size == 'xs'){
31734 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31739 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31744 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31745 y : minY + (this.unitWidth + this.gutter) * 1
31753 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31758 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31759 y : minY + (this.unitWidth + this.gutter) * 2
31763 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31764 y : minY + (this.unitWidth + this.gutter) * 2
31771 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31775 if(box[0].size == 'xs'){
31778 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].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),
31788 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),
31793 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31794 y : minY + (this.unitWidth + this.gutter) * 1
31802 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31807 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].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),
31813 y : minY + (this.unitWidth + this.gutter) * 2
31817 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),
31818 y : minY + (this.unitWidth + this.gutter) * 2
31826 * remove a Masonry Brick
31827 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31829 removeBrick : function(brick_id)
31835 for (var i = 0; i<this.bricks.length; i++) {
31836 if (this.bricks[i].id == brick_id) {
31837 this.bricks.splice(i,1);
31838 this.el.dom.removeChild(Roo.get(brick_id).dom);
31845 * adds a Masonry Brick
31846 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31848 addBrick : function(cfg)
31850 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31851 //this.register(cn);
31852 cn.parentId = this.id;
31853 cn.onRender(this.el, null);
31858 * register a Masonry Brick
31859 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31862 register : function(brick)
31864 this.bricks.push(brick);
31865 brick.masonryId = this.id;
31869 * clear all the Masonry Brick
31871 clearAll : function()
31874 //this.getChildContainer().dom.innerHTML = "";
31875 this.el.dom.innerHTML = '';
31878 getSelected : function()
31880 if (!this.selectedBrick) {
31884 return this.selectedBrick;
31888 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31892 * register a Masonry Layout
31893 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31896 register : function(layout)
31898 this.groups[layout.id] = layout;
31901 * fetch a Masonry Layout based on the masonry layout ID
31902 * @param {string} the masonry layout to add
31903 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31906 get: function(layout_id) {
31907 if (typeof(this.groups[layout_id]) == 'undefined') {
31910 return this.groups[layout_id] ;
31922 * http://masonry.desandro.com
31924 * The idea is to render all the bricks based on vertical width...
31926 * The original code extends 'outlayer' - we might need to use that....
31932 * @class Roo.bootstrap.LayoutMasonryAuto
31933 * @extends Roo.bootstrap.Component
31934 * Bootstrap Layout Masonry class
31937 * Create a new Element
31938 * @param {Object} config The config object
31941 Roo.bootstrap.LayoutMasonryAuto = function(config){
31942 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31945 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31948 * @cfg {Boolean} isFitWidth - resize the width..
31950 isFitWidth : false, // options..
31952 * @cfg {Boolean} isOriginLeft = left align?
31954 isOriginLeft : true,
31956 * @cfg {Boolean} isOriginTop = top align?
31958 isOriginTop : false,
31960 * @cfg {Boolean} isLayoutInstant = no animation?
31962 isLayoutInstant : false, // needed?
31964 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31966 isResizingContainer : true,
31968 * @cfg {Number} columnWidth width of the columns
31974 * @cfg {Number} maxCols maximum number of columns
31979 * @cfg {Number} padHeight padding below box..
31985 * @cfg {Boolean} isAutoInitial defalut true
31988 isAutoInitial : true,
31994 initialColumnWidth : 0,
31995 currentSize : null,
31997 colYs : null, // array.
32004 bricks: null, //CompositeElement
32005 cols : 0, // array?
32006 // element : null, // wrapped now this.el
32007 _isLayoutInited : null,
32010 getAutoCreate : function(){
32014 cls: 'blog-masonary-wrapper ' + this.cls,
32016 cls : 'mas-boxes masonary'
32023 getChildContainer: function( )
32025 if (this.boxesEl) {
32026 return this.boxesEl;
32029 this.boxesEl = this.el.select('.mas-boxes').first();
32031 return this.boxesEl;
32035 initEvents : function()
32039 if(this.isAutoInitial){
32040 Roo.log('hook children rendered');
32041 this.on('childrenrendered', function() {
32042 Roo.log('children rendered');
32049 initial : function()
32051 this.reloadItems();
32053 this.currentSize = this.el.getBox(true);
32055 /// was window resize... - let's see if this works..
32056 Roo.EventManager.onWindowResize(this.resize, this);
32058 if(!this.isAutoInitial){
32063 this.layout.defer(500,this);
32066 reloadItems: function()
32068 this.bricks = this.el.select('.masonry-brick', true);
32070 this.bricks.each(function(b) {
32071 //Roo.log(b.getSize());
32072 if (!b.attr('originalwidth')) {
32073 b.attr('originalwidth', b.getSize().width);
32078 Roo.log(this.bricks.elements.length);
32081 resize : function()
32084 var cs = this.el.getBox(true);
32086 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32087 Roo.log("no change in with or X");
32090 this.currentSize = cs;
32094 layout : function()
32097 this._resetLayout();
32098 //this._manageStamps();
32100 // don't animate first layout
32101 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32102 this.layoutItems( isInstant );
32104 // flag for initalized
32105 this._isLayoutInited = true;
32108 layoutItems : function( isInstant )
32110 //var items = this._getItemsForLayout( this.items );
32111 // original code supports filtering layout items.. we just ignore it..
32113 this._layoutItems( this.bricks , isInstant );
32115 this._postLayout();
32117 _layoutItems : function ( items , isInstant)
32119 //this.fireEvent( 'layout', this, items );
32122 if ( !items || !items.elements.length ) {
32123 // no items, emit event with empty array
32128 items.each(function(item) {
32129 Roo.log("layout item");
32131 // get x/y object from method
32132 var position = this._getItemLayoutPosition( item );
32134 position.item = item;
32135 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32136 queue.push( position );
32139 this._processLayoutQueue( queue );
32141 /** Sets position of item in DOM
32142 * @param {Element} item
32143 * @param {Number} x - horizontal position
32144 * @param {Number} y - vertical position
32145 * @param {Boolean} isInstant - disables transitions
32147 _processLayoutQueue : function( queue )
32149 for ( var i=0, len = queue.length; i < len; i++ ) {
32150 var obj = queue[i];
32151 obj.item.position('absolute');
32152 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32158 * Any logic you want to do after each layout,
32159 * i.e. size the container
32161 _postLayout : function()
32163 this.resizeContainer();
32166 resizeContainer : function()
32168 if ( !this.isResizingContainer ) {
32171 var size = this._getContainerSize();
32173 this.el.setSize(size.width,size.height);
32174 this.boxesEl.setSize(size.width,size.height);
32180 _resetLayout : function()
32182 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32183 this.colWidth = this.el.getWidth();
32184 //this.gutter = this.el.getWidth();
32186 this.measureColumns();
32192 this.colYs.push( 0 );
32198 measureColumns : function()
32200 this.getContainerWidth();
32201 // if columnWidth is 0, default to outerWidth of first item
32202 if ( !this.columnWidth ) {
32203 var firstItem = this.bricks.first();
32204 Roo.log(firstItem);
32205 this.columnWidth = this.containerWidth;
32206 if (firstItem && firstItem.attr('originalwidth') ) {
32207 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32209 // columnWidth fall back to item of first element
32210 Roo.log("set column width?");
32211 this.initialColumnWidth = this.columnWidth ;
32213 // if first elem has no width, default to size of container
32218 if (this.initialColumnWidth) {
32219 this.columnWidth = this.initialColumnWidth;
32224 // column width is fixed at the top - however if container width get's smaller we should
32227 // this bit calcs how man columns..
32229 var columnWidth = this.columnWidth += this.gutter;
32231 // calculate columns
32232 var containerWidth = this.containerWidth + this.gutter;
32234 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32235 // fix rounding errors, typically with gutters
32236 var excess = columnWidth - containerWidth % columnWidth;
32239 // if overshoot is less than a pixel, round up, otherwise floor it
32240 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32241 cols = Math[ mathMethod ]( cols );
32242 this.cols = Math.max( cols, 1 );
32243 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32245 // padding positioning..
32246 var totalColWidth = this.cols * this.columnWidth;
32247 var padavail = this.containerWidth - totalColWidth;
32248 // so for 2 columns - we need 3 'pads'
32250 var padNeeded = (1+this.cols) * this.padWidth;
32252 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32254 this.columnWidth += padExtra
32255 //this.padWidth = Math.floor(padavail / ( this.cols));
32257 // adjust colum width so that padding is fixed??
32259 // we have 3 columns ... total = width * 3
32260 // we have X left over... that should be used by
32262 //if (this.expandC) {
32270 getContainerWidth : function()
32272 /* // container is parent if fit width
32273 var container = this.isFitWidth ? this.element.parentNode : this.element;
32274 // check that this.size and size are there
32275 // IE8 triggers resize on body size change, so they might not be
32277 var size = getSize( container ); //FIXME
32278 this.containerWidth = size && size.innerWidth; //FIXME
32281 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32285 _getItemLayoutPosition : function( item ) // what is item?
32287 // we resize the item to our columnWidth..
32289 item.setWidth(this.columnWidth);
32290 item.autoBoxAdjust = false;
32292 var sz = item.getSize();
32294 // how many columns does this brick span
32295 var remainder = this.containerWidth % this.columnWidth;
32297 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32298 // round if off by 1 pixel, otherwise use ceil
32299 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32300 colSpan = Math.min( colSpan, this.cols );
32302 // normally this should be '1' as we dont' currently allow multi width columns..
32304 var colGroup = this._getColGroup( colSpan );
32305 // get the minimum Y value from the columns
32306 var minimumY = Math.min.apply( Math, colGroup );
32307 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32309 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32311 // position the brick
32313 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32314 y: this.currentSize.y + minimumY + this.padHeight
32318 // apply setHeight to necessary columns
32319 var setHeight = minimumY + sz.height + this.padHeight;
32320 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32322 var setSpan = this.cols + 1 - colGroup.length;
32323 for ( var i = 0; i < setSpan; i++ ) {
32324 this.colYs[ shortColIndex + i ] = setHeight ;
32331 * @param {Number} colSpan - number of columns the element spans
32332 * @returns {Array} colGroup
32334 _getColGroup : function( colSpan )
32336 if ( colSpan < 2 ) {
32337 // if brick spans only one column, use all the column Ys
32342 // how many different places could this brick fit horizontally
32343 var groupCount = this.cols + 1 - colSpan;
32344 // for each group potential horizontal position
32345 for ( var i = 0; i < groupCount; i++ ) {
32346 // make an array of colY values for that one group
32347 var groupColYs = this.colYs.slice( i, i + colSpan );
32348 // and get the max value of the array
32349 colGroup[i] = Math.max.apply( Math, groupColYs );
32354 _manageStamp : function( stamp )
32356 var stampSize = stamp.getSize();
32357 var offset = stamp.getBox();
32358 // get the columns that this stamp affects
32359 var firstX = this.isOriginLeft ? offset.x : offset.right;
32360 var lastX = firstX + stampSize.width;
32361 var firstCol = Math.floor( firstX / this.columnWidth );
32362 firstCol = Math.max( 0, firstCol );
32364 var lastCol = Math.floor( lastX / this.columnWidth );
32365 // lastCol should not go over if multiple of columnWidth #425
32366 lastCol -= lastX % this.columnWidth ? 0 : 1;
32367 lastCol = Math.min( this.cols - 1, lastCol );
32369 // set colYs to bottom of the stamp
32370 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32373 for ( var i = firstCol; i <= lastCol; i++ ) {
32374 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32379 _getContainerSize : function()
32381 this.maxY = Math.max.apply( Math, this.colYs );
32386 if ( this.isFitWidth ) {
32387 size.width = this._getContainerFitWidth();
32393 _getContainerFitWidth : function()
32395 var unusedCols = 0;
32396 // count unused columns
32399 if ( this.colYs[i] !== 0 ) {
32404 // fit container to columns that have been used
32405 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32408 needsResizeLayout : function()
32410 var previousWidth = this.containerWidth;
32411 this.getContainerWidth();
32412 return previousWidth !== this.containerWidth;
32427 * @class Roo.bootstrap.MasonryBrick
32428 * @extends Roo.bootstrap.Component
32429 * Bootstrap MasonryBrick class
32432 * Create a new MasonryBrick
32433 * @param {Object} config The config object
32436 Roo.bootstrap.MasonryBrick = function(config){
32438 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32440 Roo.bootstrap.MasonryBrick.register(this);
32446 * When a MasonryBrick is clcik
32447 * @param {Roo.bootstrap.MasonryBrick} this
32448 * @param {Roo.EventObject} e
32454 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32457 * @cfg {String} title
32461 * @cfg {String} html
32465 * @cfg {String} bgimage
32469 * @cfg {String} videourl
32473 * @cfg {String} cls
32477 * @cfg {String} href
32481 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32486 * @cfg {String} placetitle (center|bottom)
32491 * @cfg {Boolean} isFitContainer defalut true
32493 isFitContainer : true,
32496 * @cfg {Boolean} preventDefault defalut false
32498 preventDefault : false,
32501 * @cfg {Boolean} inverse defalut false
32503 maskInverse : false,
32505 getAutoCreate : function()
32507 if(!this.isFitContainer){
32508 return this.getSplitAutoCreate();
32511 var cls = 'masonry-brick masonry-brick-full';
32513 if(this.href.length){
32514 cls += ' masonry-brick-link';
32517 if(this.bgimage.length){
32518 cls += ' masonry-brick-image';
32521 if(this.maskInverse){
32522 cls += ' mask-inverse';
32525 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32526 cls += ' enable-mask';
32530 cls += ' masonry-' + this.size + '-brick';
32533 if(this.placetitle.length){
32535 switch (this.placetitle) {
32537 cls += ' masonry-center-title';
32540 cls += ' masonry-bottom-title';
32547 if(!this.html.length && !this.bgimage.length){
32548 cls += ' masonry-center-title';
32551 if(!this.html.length && this.bgimage.length){
32552 cls += ' masonry-bottom-title';
32557 cls += ' ' + this.cls;
32561 tag: (this.href.length) ? 'a' : 'div',
32566 cls: 'masonry-brick-mask'
32570 cls: 'masonry-brick-paragraph',
32576 if(this.href.length){
32577 cfg.href = this.href;
32580 var cn = cfg.cn[1].cn;
32582 if(this.title.length){
32585 cls: 'masonry-brick-title',
32590 if(this.html.length){
32593 cls: 'masonry-brick-text',
32598 if (!this.title.length && !this.html.length) {
32599 cfg.cn[1].cls += ' hide';
32602 if(this.bgimage.length){
32605 cls: 'masonry-brick-image-view',
32610 if(this.videourl.length){
32611 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32612 // youtube support only?
32615 cls: 'masonry-brick-image-view',
32618 allowfullscreen : true
32626 getSplitAutoCreate : function()
32628 var cls = 'masonry-brick masonry-brick-split';
32630 if(this.href.length){
32631 cls += ' masonry-brick-link';
32634 if(this.bgimage.length){
32635 cls += ' masonry-brick-image';
32639 cls += ' masonry-' + this.size + '-brick';
32642 switch (this.placetitle) {
32644 cls += ' masonry-center-title';
32647 cls += ' masonry-bottom-title';
32650 if(!this.bgimage.length){
32651 cls += ' masonry-center-title';
32654 if(this.bgimage.length){
32655 cls += ' masonry-bottom-title';
32661 cls += ' ' + this.cls;
32665 tag: (this.href.length) ? 'a' : 'div',
32670 cls: 'masonry-brick-split-head',
32674 cls: 'masonry-brick-paragraph',
32681 cls: 'masonry-brick-split-body',
32687 if(this.href.length){
32688 cfg.href = this.href;
32691 if(this.title.length){
32692 cfg.cn[0].cn[0].cn.push({
32694 cls: 'masonry-brick-title',
32699 if(this.html.length){
32700 cfg.cn[1].cn.push({
32702 cls: 'masonry-brick-text',
32707 if(this.bgimage.length){
32708 cfg.cn[0].cn.push({
32710 cls: 'masonry-brick-image-view',
32715 if(this.videourl.length){
32716 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32717 // youtube support only?
32718 cfg.cn[0].cn.cn.push({
32720 cls: 'masonry-brick-image-view',
32723 allowfullscreen : true
32730 initEvents: function()
32732 switch (this.size) {
32765 this.el.on('touchstart', this.onTouchStart, this);
32766 this.el.on('touchmove', this.onTouchMove, this);
32767 this.el.on('touchend', this.onTouchEnd, this);
32768 this.el.on('contextmenu', this.onContextMenu, this);
32770 this.el.on('mouseenter' ,this.enter, this);
32771 this.el.on('mouseleave', this.leave, this);
32772 this.el.on('click', this.onClick, this);
32775 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32776 this.parent().bricks.push(this);
32781 onClick: function(e, el)
32783 var time = this.endTimer - this.startTimer;
32784 // Roo.log(e.preventDefault());
32787 e.preventDefault();
32792 if(!this.preventDefault){
32796 e.preventDefault();
32798 if (this.activeClass != '') {
32799 this.selectBrick();
32802 this.fireEvent('click', this, e);
32805 enter: function(e, el)
32807 e.preventDefault();
32809 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32813 if(this.bgimage.length && this.html.length){
32814 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32818 leave: function(e, el)
32820 e.preventDefault();
32822 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32826 if(this.bgimage.length && this.html.length){
32827 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32831 onTouchStart: function(e, el)
32833 // e.preventDefault();
32835 this.touchmoved = false;
32837 if(!this.isFitContainer){
32841 if(!this.bgimage.length || !this.html.length){
32845 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32847 this.timer = new Date().getTime();
32851 onTouchMove: function(e, el)
32853 this.touchmoved = true;
32856 onContextMenu : function(e,el)
32858 e.preventDefault();
32859 e.stopPropagation();
32863 onTouchEnd: function(e, el)
32865 // e.preventDefault();
32867 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32874 if(!this.bgimage.length || !this.html.length){
32876 if(this.href.length){
32877 window.location.href = this.href;
32883 if(!this.isFitContainer){
32887 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32889 window.location.href = this.href;
32892 //selection on single brick only
32893 selectBrick : function() {
32895 if (!this.parentId) {
32899 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32900 var index = m.selectedBrick.indexOf(this.id);
32903 m.selectedBrick.splice(index,1);
32904 this.el.removeClass(this.activeClass);
32908 for(var i = 0; i < m.selectedBrick.length; i++) {
32909 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32910 b.el.removeClass(b.activeClass);
32913 m.selectedBrick = [];
32915 m.selectedBrick.push(this.id);
32916 this.el.addClass(this.activeClass);
32920 isSelected : function(){
32921 return this.el.hasClass(this.activeClass);
32926 Roo.apply(Roo.bootstrap.MasonryBrick, {
32929 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32931 * register a Masonry Brick
32932 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32935 register : function(brick)
32937 //this.groups[brick.id] = brick;
32938 this.groups.add(brick.id, brick);
32941 * fetch a masonry brick based on the masonry brick ID
32942 * @param {string} the masonry brick to add
32943 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32946 get: function(brick_id)
32948 // if (typeof(this.groups[brick_id]) == 'undefined') {
32951 // return this.groups[brick_id] ;
32953 if(this.groups.key(brick_id)) {
32954 return this.groups.key(brick_id);
32972 * @class Roo.bootstrap.Brick
32973 * @extends Roo.bootstrap.Component
32974 * Bootstrap Brick class
32977 * Create a new Brick
32978 * @param {Object} config The config object
32981 Roo.bootstrap.Brick = function(config){
32982 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32988 * When a Brick is click
32989 * @param {Roo.bootstrap.Brick} this
32990 * @param {Roo.EventObject} e
32996 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32999 * @cfg {String} title
33003 * @cfg {String} html
33007 * @cfg {String} bgimage
33011 * @cfg {String} cls
33015 * @cfg {String} href
33019 * @cfg {String} video
33023 * @cfg {Boolean} square
33027 getAutoCreate : function()
33029 var cls = 'roo-brick';
33031 if(this.href.length){
33032 cls += ' roo-brick-link';
33035 if(this.bgimage.length){
33036 cls += ' roo-brick-image';
33039 if(!this.html.length && !this.bgimage.length){
33040 cls += ' roo-brick-center-title';
33043 if(!this.html.length && this.bgimage.length){
33044 cls += ' roo-brick-bottom-title';
33048 cls += ' ' + this.cls;
33052 tag: (this.href.length) ? 'a' : 'div',
33057 cls: 'roo-brick-paragraph',
33063 if(this.href.length){
33064 cfg.href = this.href;
33067 var cn = cfg.cn[0].cn;
33069 if(this.title.length){
33072 cls: 'roo-brick-title',
33077 if(this.html.length){
33080 cls: 'roo-brick-text',
33087 if(this.bgimage.length){
33090 cls: 'roo-brick-image-view',
33098 initEvents: function()
33100 if(this.title.length || this.html.length){
33101 this.el.on('mouseenter' ,this.enter, this);
33102 this.el.on('mouseleave', this.leave, this);
33105 Roo.EventManager.onWindowResize(this.resize, this);
33107 if(this.bgimage.length){
33108 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33109 this.imageEl.on('load', this.onImageLoad, this);
33116 onImageLoad : function()
33121 resize : function()
33123 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33125 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33127 if(this.bgimage.length){
33128 var image = this.el.select('.roo-brick-image-view', true).first();
33130 image.setWidth(paragraph.getWidth());
33133 image.setHeight(paragraph.getWidth());
33136 this.el.setHeight(image.getHeight());
33137 paragraph.setHeight(image.getHeight());
33143 enter: function(e, el)
33145 e.preventDefault();
33147 if(this.bgimage.length){
33148 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33149 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33153 leave: function(e, el)
33155 e.preventDefault();
33157 if(this.bgimage.length){
33158 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33159 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33174 * @class Roo.bootstrap.NumberField
33175 * @extends Roo.bootstrap.Input
33176 * Bootstrap NumberField class
33182 * Create a new NumberField
33183 * @param {Object} config The config object
33186 Roo.bootstrap.NumberField = function(config){
33187 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33190 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33193 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33195 allowDecimals : true,
33197 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33199 decimalSeparator : ".",
33201 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33203 decimalPrecision : 2,
33205 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33207 allowNegative : true,
33210 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33214 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33216 minValue : Number.NEGATIVE_INFINITY,
33218 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33220 maxValue : Number.MAX_VALUE,
33222 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33224 minText : "The minimum value for this field is {0}",
33226 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33228 maxText : "The maximum value for this field is {0}",
33230 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33231 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33233 nanText : "{0} is not a valid number",
33235 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33237 thousandsDelimiter : false,
33239 * @cfg {String} valueAlign alignment of value
33241 valueAlign : "left",
33243 getAutoCreate : function()
33245 var hiddenInput = {
33249 cls: 'hidden-number-input'
33253 hiddenInput.name = this.name;
33258 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33260 this.name = hiddenInput.name;
33262 if(cfg.cn.length > 0) {
33263 cfg.cn.push(hiddenInput);
33270 initEvents : function()
33272 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33274 var allowed = "0123456789";
33276 if(this.allowDecimals){
33277 allowed += this.decimalSeparator;
33280 if(this.allowNegative){
33284 if(this.thousandsDelimiter) {
33288 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33290 var keyPress = function(e){
33292 var k = e.getKey();
33294 var c = e.getCharCode();
33297 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33298 allowed.indexOf(String.fromCharCode(c)) === -1
33304 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33308 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33313 this.el.on("keypress", keyPress, this);
33316 validateValue : function(value)
33319 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33323 var num = this.parseValue(value);
33326 this.markInvalid(String.format(this.nanText, value));
33330 if(num < this.minValue){
33331 this.markInvalid(String.format(this.minText, this.minValue));
33335 if(num > this.maxValue){
33336 this.markInvalid(String.format(this.maxText, this.maxValue));
33343 getValue : function()
33345 var v = this.hiddenEl().getValue();
33347 return this.fixPrecision(this.parseValue(v));
33350 parseValue : function(value)
33352 if(this.thousandsDelimiter) {
33354 r = new RegExp(",", "g");
33355 value = value.replace(r, "");
33358 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33359 return isNaN(value) ? '' : value;
33362 fixPrecision : function(value)
33364 if(this.thousandsDelimiter) {
33366 r = new RegExp(",", "g");
33367 value = value.replace(r, "");
33370 var nan = isNaN(value);
33372 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33373 return nan ? '' : value;
33375 return parseFloat(value).toFixed(this.decimalPrecision);
33378 setValue : function(v)
33380 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33386 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33388 this.inputEl().dom.value = (v == '') ? '' :
33389 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33391 if(!this.allowZero && v === '0') {
33392 this.hiddenEl().dom.value = '';
33393 this.inputEl().dom.value = '';
33400 decimalPrecisionFcn : function(v)
33402 return Math.floor(v);
33405 beforeBlur : function()
33407 var v = this.parseValue(this.getRawValue());
33409 if(v || v === 0 || v === ''){
33414 hiddenEl : function()
33416 return this.el.select('input.hidden-number-input',true).first();
33428 * @class Roo.bootstrap.DocumentSlider
33429 * @extends Roo.bootstrap.Component
33430 * Bootstrap DocumentSlider class
33433 * Create a new DocumentViewer
33434 * @param {Object} config The config object
33437 Roo.bootstrap.DocumentSlider = function(config){
33438 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33445 * Fire after initEvent
33446 * @param {Roo.bootstrap.DocumentSlider} this
33451 * Fire after update
33452 * @param {Roo.bootstrap.DocumentSlider} this
33458 * @param {Roo.bootstrap.DocumentSlider} this
33464 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33470 getAutoCreate : function()
33474 cls : 'roo-document-slider',
33478 cls : 'roo-document-slider-header',
33482 cls : 'roo-document-slider-header-title'
33488 cls : 'roo-document-slider-body',
33492 cls : 'roo-document-slider-prev',
33496 cls : 'fa fa-chevron-left'
33502 cls : 'roo-document-slider-thumb',
33506 cls : 'roo-document-slider-image'
33512 cls : 'roo-document-slider-next',
33516 cls : 'fa fa-chevron-right'
33528 initEvents : function()
33530 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33531 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33533 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33534 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33536 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33537 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33539 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33540 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33542 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33543 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33545 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33546 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33548 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33549 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33551 this.thumbEl.on('click', this.onClick, this);
33553 this.prevIndicator.on('click', this.prev, this);
33555 this.nextIndicator.on('click', this.next, this);
33559 initial : function()
33561 if(this.files.length){
33562 this.indicator = 1;
33566 this.fireEvent('initial', this);
33569 update : function()
33571 this.imageEl.attr('src', this.files[this.indicator - 1]);
33573 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33575 this.prevIndicator.show();
33577 if(this.indicator == 1){
33578 this.prevIndicator.hide();
33581 this.nextIndicator.show();
33583 if(this.indicator == this.files.length){
33584 this.nextIndicator.hide();
33587 this.thumbEl.scrollTo('top');
33589 this.fireEvent('update', this);
33592 onClick : function(e)
33594 e.preventDefault();
33596 this.fireEvent('click', this);
33601 e.preventDefault();
33603 this.indicator = Math.max(1, this.indicator - 1);
33610 e.preventDefault();
33612 this.indicator = Math.min(this.files.length, this.indicator + 1);
33626 * @class Roo.bootstrap.RadioSet
33627 * @extends Roo.bootstrap.Input
33628 * Bootstrap RadioSet class
33629 * @cfg {String} indicatorpos (left|right) default left
33630 * @cfg {Boolean} inline (true|false) inline the element (default true)
33631 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33633 * Create a new RadioSet
33634 * @param {Object} config The config object
33637 Roo.bootstrap.RadioSet = function(config){
33639 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33643 Roo.bootstrap.RadioSet.register(this);
33648 * Fires when the element is checked or unchecked.
33649 * @param {Roo.bootstrap.RadioSet} this This radio
33650 * @param {Roo.bootstrap.Radio} item The checked item
33655 * Fires when the element is click.
33656 * @param {Roo.bootstrap.RadioSet} this This radio set
33657 * @param {Roo.bootstrap.Radio} item The checked item
33658 * @param {Roo.EventObject} e The event object
33665 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33673 indicatorpos : 'left',
33675 getAutoCreate : function()
33679 cls : 'roo-radio-set-label',
33683 html : this.fieldLabel
33688 if(this.indicatorpos == 'left'){
33691 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33692 tooltip : 'This field is required'
33697 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33698 tooltip : 'This field is required'
33704 cls : 'roo-radio-set-items'
33707 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33709 if (align === 'left' && this.fieldLabel.length) {
33712 cls : "roo-radio-set-right",
33718 if(this.labelWidth > 12){
33719 label.style = "width: " + this.labelWidth + 'px';
33722 if(this.labelWidth < 13 && this.labelmd == 0){
33723 this.labelmd = this.labelWidth;
33726 if(this.labellg > 0){
33727 label.cls += ' col-lg-' + this.labellg;
33728 items.cls += ' col-lg-' + (12 - this.labellg);
33731 if(this.labelmd > 0){
33732 label.cls += ' col-md-' + this.labelmd;
33733 items.cls += ' col-md-' + (12 - this.labelmd);
33736 if(this.labelsm > 0){
33737 label.cls += ' col-sm-' + this.labelsm;
33738 items.cls += ' col-sm-' + (12 - this.labelsm);
33741 if(this.labelxs > 0){
33742 label.cls += ' col-xs-' + this.labelxs;
33743 items.cls += ' col-xs-' + (12 - this.labelxs);
33749 cls : 'roo-radio-set',
33753 cls : 'roo-radio-set-input',
33756 value : this.value ? this.value : ''
33763 if(this.weight.length){
33764 cfg.cls += ' roo-radio-' + this.weight;
33768 cfg.cls += ' roo-radio-set-inline';
33772 ['xs','sm','md','lg'].map(function(size){
33773 if (settings[size]) {
33774 cfg.cls += ' col-' + size + '-' + settings[size];
33782 initEvents : function()
33784 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33785 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33787 if(!this.fieldLabel.length){
33788 this.labelEl.hide();
33791 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33792 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33794 this.indicator = this.indicatorEl();
33796 if(this.indicator){
33797 this.indicator.addClass('invisible');
33800 this.originalValue = this.getValue();
33804 inputEl: function ()
33806 return this.el.select('.roo-radio-set-input', true).first();
33809 getChildContainer : function()
33811 return this.itemsEl;
33814 register : function(item)
33816 this.radioes.push(item);
33820 validate : function()
33822 if(this.getVisibilityEl().hasClass('hidden')){
33828 Roo.each(this.radioes, function(i){
33837 if(this.allowBlank) {
33841 if(this.disabled || valid){
33846 this.markInvalid();
33851 markValid : function()
33853 if(this.labelEl.isVisible(true)){
33854 this.indicatorEl().removeClass('visible');
33855 this.indicatorEl().addClass('invisible');
33858 this.el.removeClass([this.invalidClass, this.validClass]);
33859 this.el.addClass(this.validClass);
33861 this.fireEvent('valid', this);
33864 markInvalid : function(msg)
33866 if(this.allowBlank || this.disabled){
33870 if(this.labelEl.isVisible(true)){
33871 this.indicatorEl().removeClass('invisible');
33872 this.indicatorEl().addClass('visible');
33875 this.el.removeClass([this.invalidClass, this.validClass]);
33876 this.el.addClass(this.invalidClass);
33878 this.fireEvent('invalid', this, msg);
33882 setValue : function(v, suppressEvent)
33884 if(this.value === v){
33891 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33894 Roo.each(this.radioes, function(i){
33896 i.el.removeClass('checked');
33899 Roo.each(this.radioes, function(i){
33901 if(i.value === v || i.value.toString() === v.toString()){
33903 i.el.addClass('checked');
33905 if(suppressEvent !== true){
33906 this.fireEvent('check', this, i);
33917 clearInvalid : function(){
33919 if(!this.el || this.preventMark){
33923 this.el.removeClass([this.invalidClass]);
33925 this.fireEvent('valid', this);
33930 Roo.apply(Roo.bootstrap.RadioSet, {
33934 register : function(set)
33936 this.groups[set.name] = set;
33939 get: function(name)
33941 if (typeof(this.groups[name]) == 'undefined') {
33945 return this.groups[name] ;
33951 * Ext JS Library 1.1.1
33952 * Copyright(c) 2006-2007, Ext JS, LLC.
33954 * Originally Released Under LGPL - original licence link has changed is not relivant.
33957 * <script type="text/javascript">
33962 * @class Roo.bootstrap.SplitBar
33963 * @extends Roo.util.Observable
33964 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33968 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33969 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33970 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33971 split.minSize = 100;
33972 split.maxSize = 600;
33973 split.animate = true;
33974 split.on('moved', splitterMoved);
33977 * Create a new SplitBar
33978 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33979 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33980 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33981 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33982 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33983 position of the SplitBar).
33985 Roo.bootstrap.SplitBar = function(cfg){
33990 // dragElement : elm
33991 // resizingElement: el,
33993 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33994 // placement : Roo.bootstrap.SplitBar.LEFT ,
33995 // existingProxy ???
33998 this.el = Roo.get(cfg.dragElement, true);
33999 this.el.dom.unselectable = "on";
34001 this.resizingEl = Roo.get(cfg.resizingElement, true);
34005 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34006 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34009 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34012 * The minimum size of the resizing element. (Defaults to 0)
34018 * The maximum size of the resizing element. (Defaults to 2000)
34021 this.maxSize = 2000;
34024 * Whether to animate the transition to the new size
34027 this.animate = false;
34030 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34033 this.useShim = false;
34038 if(!cfg.existingProxy){
34040 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34042 this.proxy = Roo.get(cfg.existingProxy).dom;
34045 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34048 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34051 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34054 this.dragSpecs = {};
34057 * @private The adapter to use to positon and resize elements
34059 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34060 this.adapter.init(this);
34062 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34064 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34065 this.el.addClass("roo-splitbar-h");
34068 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34069 this.el.addClass("roo-splitbar-v");
34075 * Fires when the splitter is moved (alias for {@link #event-moved})
34076 * @param {Roo.bootstrap.SplitBar} this
34077 * @param {Number} newSize the new width or height
34082 * Fires when the splitter is moved
34083 * @param {Roo.bootstrap.SplitBar} this
34084 * @param {Number} newSize the new width or height
34088 * @event beforeresize
34089 * Fires before the splitter is dragged
34090 * @param {Roo.bootstrap.SplitBar} this
34092 "beforeresize" : true,
34094 "beforeapply" : true
34097 Roo.util.Observable.call(this);
34100 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34101 onStartProxyDrag : function(x, y){
34102 this.fireEvent("beforeresize", this);
34104 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34106 o.enableDisplayMode("block");
34107 // all splitbars share the same overlay
34108 Roo.bootstrap.SplitBar.prototype.overlay = o;
34110 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34111 this.overlay.show();
34112 Roo.get(this.proxy).setDisplayed("block");
34113 var size = this.adapter.getElementSize(this);
34114 this.activeMinSize = this.getMinimumSize();;
34115 this.activeMaxSize = this.getMaximumSize();;
34116 var c1 = size - this.activeMinSize;
34117 var c2 = Math.max(this.activeMaxSize - size, 0);
34118 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34119 this.dd.resetConstraints();
34120 this.dd.setXConstraint(
34121 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34122 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34124 this.dd.setYConstraint(0, 0);
34126 this.dd.resetConstraints();
34127 this.dd.setXConstraint(0, 0);
34128 this.dd.setYConstraint(
34129 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34130 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34133 this.dragSpecs.startSize = size;
34134 this.dragSpecs.startPoint = [x, y];
34135 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34139 * @private Called after the drag operation by the DDProxy
34141 onEndProxyDrag : function(e){
34142 Roo.get(this.proxy).setDisplayed(false);
34143 var endPoint = Roo.lib.Event.getXY(e);
34145 this.overlay.hide();
34148 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34149 newSize = this.dragSpecs.startSize +
34150 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34151 endPoint[0] - this.dragSpecs.startPoint[0] :
34152 this.dragSpecs.startPoint[0] - endPoint[0]
34155 newSize = this.dragSpecs.startSize +
34156 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34157 endPoint[1] - this.dragSpecs.startPoint[1] :
34158 this.dragSpecs.startPoint[1] - endPoint[1]
34161 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34162 if(newSize != this.dragSpecs.startSize){
34163 if(this.fireEvent('beforeapply', this, newSize) !== false){
34164 this.adapter.setElementSize(this, newSize);
34165 this.fireEvent("moved", this, newSize);
34166 this.fireEvent("resize", this, newSize);
34172 * Get the adapter this SplitBar uses
34173 * @return The adapter object
34175 getAdapter : function(){
34176 return this.adapter;
34180 * Set the adapter this SplitBar uses
34181 * @param {Object} adapter A SplitBar adapter object
34183 setAdapter : function(adapter){
34184 this.adapter = adapter;
34185 this.adapter.init(this);
34189 * Gets the minimum size for the resizing element
34190 * @return {Number} The minimum size
34192 getMinimumSize : function(){
34193 return this.minSize;
34197 * Sets the minimum size for the resizing element
34198 * @param {Number} minSize The minimum size
34200 setMinimumSize : function(minSize){
34201 this.minSize = minSize;
34205 * Gets the maximum size for the resizing element
34206 * @return {Number} The maximum size
34208 getMaximumSize : function(){
34209 return this.maxSize;
34213 * Sets the maximum size for the resizing element
34214 * @param {Number} maxSize The maximum size
34216 setMaximumSize : function(maxSize){
34217 this.maxSize = maxSize;
34221 * Sets the initialize size for the resizing element
34222 * @param {Number} size The initial size
34224 setCurrentSize : function(size){
34225 var oldAnimate = this.animate;
34226 this.animate = false;
34227 this.adapter.setElementSize(this, size);
34228 this.animate = oldAnimate;
34232 * Destroy this splitbar.
34233 * @param {Boolean} removeEl True to remove the element
34235 destroy : function(removeEl){
34237 this.shim.remove();
34240 this.proxy.parentNode.removeChild(this.proxy);
34248 * @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.
34250 Roo.bootstrap.SplitBar.createProxy = function(dir){
34251 var proxy = new Roo.Element(document.createElement("div"));
34252 proxy.unselectable();
34253 var cls = 'roo-splitbar-proxy';
34254 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34255 document.body.appendChild(proxy.dom);
34260 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34261 * Default Adapter. It assumes the splitter and resizing element are not positioned
34262 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34264 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34267 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34268 // do nothing for now
34269 init : function(s){
34273 * Called before drag operations to get the current size of the resizing element.
34274 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34276 getElementSize : function(s){
34277 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34278 return s.resizingEl.getWidth();
34280 return s.resizingEl.getHeight();
34285 * Called after drag operations to set the size of the resizing element.
34286 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34287 * @param {Number} newSize The new size to set
34288 * @param {Function} onComplete A function to be invoked when resizing is complete
34290 setElementSize : function(s, newSize, onComplete){
34291 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34293 s.resizingEl.setWidth(newSize);
34295 onComplete(s, newSize);
34298 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34303 s.resizingEl.setHeight(newSize);
34305 onComplete(s, newSize);
34308 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34315 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34316 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34317 * Adapter that moves the splitter element to align with the resized sizing element.
34318 * Used with an absolute positioned SplitBar.
34319 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34320 * document.body, make sure you assign an id to the body element.
34322 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34323 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34324 this.container = Roo.get(container);
34327 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34328 init : function(s){
34329 this.basic.init(s);
34332 getElementSize : function(s){
34333 return this.basic.getElementSize(s);
34336 setElementSize : function(s, newSize, onComplete){
34337 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34340 moveSplitter : function(s){
34341 var yes = Roo.bootstrap.SplitBar;
34342 switch(s.placement){
34344 s.el.setX(s.resizingEl.getRight());
34347 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34350 s.el.setY(s.resizingEl.getBottom());
34353 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34360 * Orientation constant - Create a vertical SplitBar
34364 Roo.bootstrap.SplitBar.VERTICAL = 1;
34367 * Orientation constant - Create a horizontal SplitBar
34371 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34374 * Placement constant - The resizing element is to the left of the splitter element
34378 Roo.bootstrap.SplitBar.LEFT = 1;
34381 * Placement constant - The resizing element is to the right of the splitter element
34385 Roo.bootstrap.SplitBar.RIGHT = 2;
34388 * Placement constant - The resizing element is positioned above the splitter element
34392 Roo.bootstrap.SplitBar.TOP = 3;
34395 * Placement constant - The resizing element is positioned under splitter element
34399 Roo.bootstrap.SplitBar.BOTTOM = 4;
34400 Roo.namespace("Roo.bootstrap.layout");/*
34402 * Ext JS Library 1.1.1
34403 * Copyright(c) 2006-2007, Ext JS, LLC.
34405 * Originally Released Under LGPL - original licence link has changed is not relivant.
34408 * <script type="text/javascript">
34412 * @class Roo.bootstrap.layout.Manager
34413 * @extends Roo.bootstrap.Component
34414 * Base class for layout managers.
34416 Roo.bootstrap.layout.Manager = function(config)
34418 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34424 /** false to disable window resize monitoring @type Boolean */
34425 this.monitorWindowResize = true;
34430 * Fires when a layout is performed.
34431 * @param {Roo.LayoutManager} this
34435 * @event regionresized
34436 * Fires when the user resizes a region.
34437 * @param {Roo.LayoutRegion} region The resized region
34438 * @param {Number} newSize The new size (width for east/west, height for north/south)
34440 "regionresized" : true,
34442 * @event regioncollapsed
34443 * Fires when a region is collapsed.
34444 * @param {Roo.LayoutRegion} region The collapsed region
34446 "regioncollapsed" : true,
34448 * @event regionexpanded
34449 * Fires when a region is expanded.
34450 * @param {Roo.LayoutRegion} region The expanded region
34452 "regionexpanded" : true
34454 this.updating = false;
34457 this.el = Roo.get(config.el);
34463 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34468 monitorWindowResize : true,
34474 onRender : function(ct, position)
34477 this.el = Roo.get(ct);
34480 //this.fireEvent('render',this);
34484 initEvents: function()
34488 // ie scrollbar fix
34489 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34490 document.body.scroll = "no";
34491 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34492 this.el.position('relative');
34494 this.id = this.el.id;
34495 this.el.addClass("roo-layout-container");
34496 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34497 if(this.el.dom != document.body ) {
34498 this.el.on('resize', this.layout,this);
34499 this.el.on('show', this.layout,this);
34505 * Returns true if this layout is currently being updated
34506 * @return {Boolean}
34508 isUpdating : function(){
34509 return this.updating;
34513 * Suspend the LayoutManager from doing auto-layouts while
34514 * making multiple add or remove calls
34516 beginUpdate : function(){
34517 this.updating = true;
34521 * Restore auto-layouts and optionally disable the manager from performing a layout
34522 * @param {Boolean} noLayout true to disable a layout update
34524 endUpdate : function(noLayout){
34525 this.updating = false;
34531 layout: function(){
34535 onRegionResized : function(region, newSize){
34536 this.fireEvent("regionresized", region, newSize);
34540 onRegionCollapsed : function(region){
34541 this.fireEvent("regioncollapsed", region);
34544 onRegionExpanded : function(region){
34545 this.fireEvent("regionexpanded", region);
34549 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34550 * performs box-model adjustments.
34551 * @return {Object} The size as an object {width: (the width), height: (the height)}
34553 getViewSize : function()
34556 if(this.el.dom != document.body){
34557 size = this.el.getSize();
34559 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34561 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34562 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34567 * Returns the Element this layout is bound to.
34568 * @return {Roo.Element}
34570 getEl : function(){
34575 * Returns the specified region.
34576 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34577 * @return {Roo.LayoutRegion}
34579 getRegion : function(target){
34580 return this.regions[target.toLowerCase()];
34583 onWindowResize : function(){
34584 if(this.monitorWindowResize){
34591 * Ext JS Library 1.1.1
34592 * Copyright(c) 2006-2007, Ext JS, LLC.
34594 * Originally Released Under LGPL - original licence link has changed is not relivant.
34597 * <script type="text/javascript">
34600 * @class Roo.bootstrap.layout.Border
34601 * @extends Roo.bootstrap.layout.Manager
34602 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34603 * please see: examples/bootstrap/nested.html<br><br>
34605 <b>The container the layout is rendered into can be either the body element or any other element.
34606 If it is not the body element, the container needs to either be an absolute positioned element,
34607 or you will need to add "position:relative" to the css of the container. You will also need to specify
34608 the container size if it is not the body element.</b>
34611 * Create a new Border
34612 * @param {Object} config Configuration options
34614 Roo.bootstrap.layout.Border = function(config){
34615 config = config || {};
34616 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34620 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34621 if(config[region]){
34622 config[region].region = region;
34623 this.addRegion(config[region]);
34629 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34631 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34633 * Creates and adds a new region if it doesn't already exist.
34634 * @param {String} target The target region key (north, south, east, west or center).
34635 * @param {Object} config The regions config object
34636 * @return {BorderLayoutRegion} The new region
34638 addRegion : function(config)
34640 if(!this.regions[config.region]){
34641 var r = this.factory(config);
34642 this.bindRegion(r);
34644 return this.regions[config.region];
34648 bindRegion : function(r){
34649 this.regions[r.config.region] = r;
34651 r.on("visibilitychange", this.layout, this);
34652 r.on("paneladded", this.layout, this);
34653 r.on("panelremoved", this.layout, this);
34654 r.on("invalidated", this.layout, this);
34655 r.on("resized", this.onRegionResized, this);
34656 r.on("collapsed", this.onRegionCollapsed, this);
34657 r.on("expanded", this.onRegionExpanded, this);
34661 * Performs a layout update.
34663 layout : function()
34665 if(this.updating) {
34669 // render all the rebions if they have not been done alreayd?
34670 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34671 if(this.regions[region] && !this.regions[region].bodyEl){
34672 this.regions[region].onRender(this.el)
34676 var size = this.getViewSize();
34677 var w = size.width;
34678 var h = size.height;
34683 //var x = 0, y = 0;
34685 var rs = this.regions;
34686 var north = rs["north"];
34687 var south = rs["south"];
34688 var west = rs["west"];
34689 var east = rs["east"];
34690 var center = rs["center"];
34691 //if(this.hideOnLayout){ // not supported anymore
34692 //c.el.setStyle("display", "none");
34694 if(north && north.isVisible()){
34695 var b = north.getBox();
34696 var m = north.getMargins();
34697 b.width = w - (m.left+m.right);
34700 centerY = b.height + b.y + m.bottom;
34701 centerH -= centerY;
34702 north.updateBox(this.safeBox(b));
34704 if(south && south.isVisible()){
34705 var b = south.getBox();
34706 var m = south.getMargins();
34707 b.width = w - (m.left+m.right);
34709 var totalHeight = (b.height + m.top + m.bottom);
34710 b.y = h - totalHeight + m.top;
34711 centerH -= totalHeight;
34712 south.updateBox(this.safeBox(b));
34714 if(west && west.isVisible()){
34715 var b = west.getBox();
34716 var m = west.getMargins();
34717 b.height = centerH - (m.top+m.bottom);
34719 b.y = centerY + m.top;
34720 var totalWidth = (b.width + m.left + m.right);
34721 centerX += totalWidth;
34722 centerW -= totalWidth;
34723 west.updateBox(this.safeBox(b));
34725 if(east && east.isVisible()){
34726 var b = east.getBox();
34727 var m = east.getMargins();
34728 b.height = centerH - (m.top+m.bottom);
34729 var totalWidth = (b.width + m.left + m.right);
34730 b.x = w - totalWidth + m.left;
34731 b.y = centerY + m.top;
34732 centerW -= totalWidth;
34733 east.updateBox(this.safeBox(b));
34736 var m = center.getMargins();
34738 x: centerX + m.left,
34739 y: centerY + m.top,
34740 width: centerW - (m.left+m.right),
34741 height: centerH - (m.top+m.bottom)
34743 //if(this.hideOnLayout){
34744 //center.el.setStyle("display", "block");
34746 center.updateBox(this.safeBox(centerBox));
34749 this.fireEvent("layout", this);
34753 safeBox : function(box){
34754 box.width = Math.max(0, box.width);
34755 box.height = Math.max(0, box.height);
34760 * Adds a ContentPanel (or subclass) to this layout.
34761 * @param {String} target The target region key (north, south, east, west or center).
34762 * @param {Roo.ContentPanel} panel The panel to add
34763 * @return {Roo.ContentPanel} The added panel
34765 add : function(target, panel){
34767 target = target.toLowerCase();
34768 return this.regions[target].add(panel);
34772 * Remove a ContentPanel (or subclass) to this layout.
34773 * @param {String} target The target region key (north, south, east, west or center).
34774 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34775 * @return {Roo.ContentPanel} The removed panel
34777 remove : function(target, panel){
34778 target = target.toLowerCase();
34779 return this.regions[target].remove(panel);
34783 * Searches all regions for a panel with the specified id
34784 * @param {String} panelId
34785 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34787 findPanel : function(panelId){
34788 var rs = this.regions;
34789 for(var target in rs){
34790 if(typeof rs[target] != "function"){
34791 var p = rs[target].getPanel(panelId);
34801 * Searches all regions for a panel with the specified id and activates (shows) it.
34802 * @param {String/ContentPanel} panelId The panels id or the panel itself
34803 * @return {Roo.ContentPanel} The shown panel or null
34805 showPanel : function(panelId) {
34806 var rs = this.regions;
34807 for(var target in rs){
34808 var r = rs[target];
34809 if(typeof r != "function"){
34810 if(r.hasPanel(panelId)){
34811 return r.showPanel(panelId);
34819 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34820 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34823 restoreState : function(provider){
34825 provider = Roo.state.Manager;
34827 var sm = new Roo.LayoutStateManager();
34828 sm.init(this, provider);
34834 * Adds a xtype elements to the layout.
34838 xtype : 'ContentPanel',
34845 xtype : 'NestedLayoutPanel',
34851 items : [ ... list of content panels or nested layout panels.. ]
34855 * @param {Object} cfg Xtype definition of item to add.
34857 addxtype : function(cfg)
34859 // basically accepts a pannel...
34860 // can accept a layout region..!?!?
34861 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34864 // theory? children can only be panels??
34866 //if (!cfg.xtype.match(/Panel$/)) {
34871 if (typeof(cfg.region) == 'undefined') {
34872 Roo.log("Failed to add Panel, region was not set");
34876 var region = cfg.region;
34882 xitems = cfg.items;
34889 case 'Content': // ContentPanel (el, cfg)
34890 case 'Scroll': // ContentPanel (el, cfg)
34892 cfg.autoCreate = true;
34893 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34895 // var el = this.el.createChild();
34896 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34899 this.add(region, ret);
34903 case 'TreePanel': // our new panel!
34904 cfg.el = this.el.createChild();
34905 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34906 this.add(region, ret);
34911 // create a new Layout (which is a Border Layout...
34913 var clayout = cfg.layout;
34914 clayout.el = this.el.createChild();
34915 clayout.items = clayout.items || [];
34919 // replace this exitems with the clayout ones..
34920 xitems = clayout.items;
34922 // force background off if it's in center...
34923 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34924 cfg.background = false;
34926 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34929 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34930 //console.log('adding nested layout panel ' + cfg.toSource());
34931 this.add(region, ret);
34932 nb = {}; /// find first...
34937 // needs grid and region
34939 //var el = this.getRegion(region).el.createChild();
34941 *var el = this.el.createChild();
34942 // create the grid first...
34943 cfg.grid.container = el;
34944 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34947 if (region == 'center' && this.active ) {
34948 cfg.background = false;
34951 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34953 this.add(region, ret);
34955 if (cfg.background) {
34956 // render grid on panel activation (if panel background)
34957 ret.on('activate', function(gp) {
34958 if (!gp.grid.rendered) {
34959 // gp.grid.render(el);
34963 // cfg.grid.render(el);
34969 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34970 // it was the old xcomponent building that caused this before.
34971 // espeically if border is the top element in the tree.
34981 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34983 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34984 this.add(region, ret);
34988 throw "Can not add '" + cfg.xtype + "' to Border";
34994 this.beginUpdate();
34998 Roo.each(xitems, function(i) {
34999 region = nb && i.region ? i.region : false;
35001 var add = ret.addxtype(i);
35004 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35005 if (!i.background) {
35006 abn[region] = nb[region] ;
35013 // make the last non-background panel active..
35014 //if (nb) { Roo.log(abn); }
35017 for(var r in abn) {
35018 region = this.getRegion(r);
35020 // tried using nb[r], but it does not work..
35022 region.showPanel(abn[r]);
35033 factory : function(cfg)
35036 var validRegions = Roo.bootstrap.layout.Border.regions;
35038 var target = cfg.region;
35041 var r = Roo.bootstrap.layout;
35045 return new r.North(cfg);
35047 return new r.South(cfg);
35049 return new r.East(cfg);
35051 return new r.West(cfg);
35053 return new r.Center(cfg);
35055 throw 'Layout region "'+target+'" not supported.';
35062 * Ext JS Library 1.1.1
35063 * Copyright(c) 2006-2007, Ext JS, LLC.
35065 * Originally Released Under LGPL - original licence link has changed is not relivant.
35068 * <script type="text/javascript">
35072 * @class Roo.bootstrap.layout.Basic
35073 * @extends Roo.util.Observable
35074 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35075 * and does not have a titlebar, tabs or any other features. All it does is size and position
35076 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35077 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35078 * @cfg {string} region the region that it inhabits..
35079 * @cfg {bool} skipConfig skip config?
35083 Roo.bootstrap.layout.Basic = function(config){
35085 this.mgr = config.mgr;
35087 this.position = config.region;
35089 var skipConfig = config.skipConfig;
35093 * @scope Roo.BasicLayoutRegion
35097 * @event beforeremove
35098 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35099 * @param {Roo.LayoutRegion} this
35100 * @param {Roo.ContentPanel} panel The panel
35101 * @param {Object} e The cancel event object
35103 "beforeremove" : true,
35105 * @event invalidated
35106 * Fires when the layout for this region is changed.
35107 * @param {Roo.LayoutRegion} this
35109 "invalidated" : true,
35111 * @event visibilitychange
35112 * Fires when this region is shown or hidden
35113 * @param {Roo.LayoutRegion} this
35114 * @param {Boolean} visibility true or false
35116 "visibilitychange" : true,
35118 * @event paneladded
35119 * Fires when a panel is added.
35120 * @param {Roo.LayoutRegion} this
35121 * @param {Roo.ContentPanel} panel The panel
35123 "paneladded" : true,
35125 * @event panelremoved
35126 * Fires when a panel is removed.
35127 * @param {Roo.LayoutRegion} this
35128 * @param {Roo.ContentPanel} panel The panel
35130 "panelremoved" : true,
35132 * @event beforecollapse
35133 * Fires when this region before collapse.
35134 * @param {Roo.LayoutRegion} this
35136 "beforecollapse" : true,
35139 * Fires when this region is collapsed.
35140 * @param {Roo.LayoutRegion} this
35142 "collapsed" : true,
35145 * Fires when this region is expanded.
35146 * @param {Roo.LayoutRegion} this
35151 * Fires when this region is slid into view.
35152 * @param {Roo.LayoutRegion} this
35154 "slideshow" : true,
35157 * Fires when this region slides out of view.
35158 * @param {Roo.LayoutRegion} this
35160 "slidehide" : true,
35162 * @event panelactivated
35163 * Fires when a panel is activated.
35164 * @param {Roo.LayoutRegion} this
35165 * @param {Roo.ContentPanel} panel The activated panel
35167 "panelactivated" : true,
35170 * Fires when the user resizes this region.
35171 * @param {Roo.LayoutRegion} this
35172 * @param {Number} newSize The new size (width for east/west, height for north/south)
35176 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35177 this.panels = new Roo.util.MixedCollection();
35178 this.panels.getKey = this.getPanelId.createDelegate(this);
35180 this.activePanel = null;
35181 // ensure listeners are added...
35183 if (config.listeners || config.events) {
35184 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35185 listeners : config.listeners || {},
35186 events : config.events || {}
35190 if(skipConfig !== true){
35191 this.applyConfig(config);
35195 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35197 getPanelId : function(p){
35201 applyConfig : function(config){
35202 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35203 this.config = config;
35208 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35209 * the width, for horizontal (north, south) the height.
35210 * @param {Number} newSize The new width or height
35212 resizeTo : function(newSize){
35213 var el = this.el ? this.el :
35214 (this.activePanel ? this.activePanel.getEl() : null);
35216 switch(this.position){
35219 el.setWidth(newSize);
35220 this.fireEvent("resized", this, newSize);
35224 el.setHeight(newSize);
35225 this.fireEvent("resized", this, newSize);
35231 getBox : function(){
35232 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35235 getMargins : function(){
35236 return this.margins;
35239 updateBox : function(box){
35241 var el = this.activePanel.getEl();
35242 el.dom.style.left = box.x + "px";
35243 el.dom.style.top = box.y + "px";
35244 this.activePanel.setSize(box.width, box.height);
35248 * Returns the container element for this region.
35249 * @return {Roo.Element}
35251 getEl : function(){
35252 return this.activePanel;
35256 * Returns true if this region is currently visible.
35257 * @return {Boolean}
35259 isVisible : function(){
35260 return this.activePanel ? true : false;
35263 setActivePanel : function(panel){
35264 panel = this.getPanel(panel);
35265 if(this.activePanel && this.activePanel != panel){
35266 this.activePanel.setActiveState(false);
35267 this.activePanel.getEl().setLeftTop(-10000,-10000);
35269 this.activePanel = panel;
35270 panel.setActiveState(true);
35272 panel.setSize(this.box.width, this.box.height);
35274 this.fireEvent("panelactivated", this, panel);
35275 this.fireEvent("invalidated");
35279 * Show the specified panel.
35280 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35281 * @return {Roo.ContentPanel} The shown panel or null
35283 showPanel : function(panel){
35284 panel = this.getPanel(panel);
35286 this.setActivePanel(panel);
35292 * Get the active panel for this region.
35293 * @return {Roo.ContentPanel} The active panel or null
35295 getActivePanel : function(){
35296 return this.activePanel;
35300 * Add the passed ContentPanel(s)
35301 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35302 * @return {Roo.ContentPanel} The panel added (if only one was added)
35304 add : function(panel){
35305 if(arguments.length > 1){
35306 for(var i = 0, len = arguments.length; i < len; i++) {
35307 this.add(arguments[i]);
35311 if(this.hasPanel(panel)){
35312 this.showPanel(panel);
35315 var el = panel.getEl();
35316 if(el.dom.parentNode != this.mgr.el.dom){
35317 this.mgr.el.dom.appendChild(el.dom);
35319 if(panel.setRegion){
35320 panel.setRegion(this);
35322 this.panels.add(panel);
35323 el.setStyle("position", "absolute");
35324 if(!panel.background){
35325 this.setActivePanel(panel);
35326 if(this.config.initialSize && this.panels.getCount()==1){
35327 this.resizeTo(this.config.initialSize);
35330 this.fireEvent("paneladded", this, panel);
35335 * Returns true if the panel is in this region.
35336 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35337 * @return {Boolean}
35339 hasPanel : function(panel){
35340 if(typeof panel == "object"){ // must be panel obj
35341 panel = panel.getId();
35343 return this.getPanel(panel) ? true : false;
35347 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35348 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35349 * @param {Boolean} preservePanel Overrides the config preservePanel option
35350 * @return {Roo.ContentPanel} The panel that was removed
35352 remove : function(panel, preservePanel){
35353 panel = this.getPanel(panel);
35358 this.fireEvent("beforeremove", this, panel, e);
35359 if(e.cancel === true){
35362 var panelId = panel.getId();
35363 this.panels.removeKey(panelId);
35368 * Returns the panel specified or null if it's not in this region.
35369 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35370 * @return {Roo.ContentPanel}
35372 getPanel : function(id){
35373 if(typeof id == "object"){ // must be panel obj
35376 return this.panels.get(id);
35380 * Returns this regions position (north/south/east/west/center).
35383 getPosition: function(){
35384 return this.position;
35388 * Ext JS Library 1.1.1
35389 * Copyright(c) 2006-2007, Ext JS, LLC.
35391 * Originally Released Under LGPL - original licence link has changed is not relivant.
35394 * <script type="text/javascript">
35398 * @class Roo.bootstrap.layout.Region
35399 * @extends Roo.bootstrap.layout.Basic
35400 * This class represents a region in a layout manager.
35402 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35403 * @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})
35404 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35405 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35406 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35407 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35408 * @cfg {String} title The title for the region (overrides panel titles)
35409 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35410 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35411 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35412 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35413 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35414 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35415 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35416 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35417 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35418 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35420 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35421 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35422 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35423 * @cfg {Number} width For East/West panels
35424 * @cfg {Number} height For North/South panels
35425 * @cfg {Boolean} split To show the splitter
35426 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35428 * @cfg {string} cls Extra CSS classes to add to region
35430 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35431 * @cfg {string} region the region that it inhabits..
35434 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35435 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35437 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35438 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35439 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35441 Roo.bootstrap.layout.Region = function(config)
35443 this.applyConfig(config);
35445 var mgr = config.mgr;
35446 var pos = config.region;
35447 config.skipConfig = true;
35448 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35451 this.onRender(mgr.el);
35454 this.visible = true;
35455 this.collapsed = false;
35456 this.unrendered_panels = [];
35459 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35461 position: '', // set by wrapper (eg. north/south etc..)
35462 unrendered_panels : null, // unrendered panels.
35463 createBody : function(){
35464 /** This region's body element
35465 * @type Roo.Element */
35466 this.bodyEl = this.el.createChild({
35468 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35472 onRender: function(ctr, pos)
35474 var dh = Roo.DomHelper;
35475 /** This region's container element
35476 * @type Roo.Element */
35477 this.el = dh.append(ctr.dom, {
35479 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35481 /** This region's title element
35482 * @type Roo.Element */
35484 this.titleEl = dh.append(this.el.dom,
35487 unselectable: "on",
35488 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35490 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35491 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35494 this.titleEl.enableDisplayMode();
35495 /** This region's title text element
35496 * @type HTMLElement */
35497 this.titleTextEl = this.titleEl.dom.firstChild;
35498 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35500 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35501 this.closeBtn.enableDisplayMode();
35502 this.closeBtn.on("click", this.closeClicked, this);
35503 this.closeBtn.hide();
35505 this.createBody(this.config);
35506 if(this.config.hideWhenEmpty){
35508 this.on("paneladded", this.validateVisibility, this);
35509 this.on("panelremoved", this.validateVisibility, this);
35511 if(this.autoScroll){
35512 this.bodyEl.setStyle("overflow", "auto");
35514 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35516 //if(c.titlebar !== false){
35517 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35518 this.titleEl.hide();
35520 this.titleEl.show();
35521 if(this.config.title){
35522 this.titleTextEl.innerHTML = this.config.title;
35526 if(this.config.collapsed){
35527 this.collapse(true);
35529 if(this.config.hidden){
35533 if (this.unrendered_panels && this.unrendered_panels.length) {
35534 for (var i =0;i< this.unrendered_panels.length; i++) {
35535 this.add(this.unrendered_panels[i]);
35537 this.unrendered_panels = null;
35543 applyConfig : function(c)
35546 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35547 var dh = Roo.DomHelper;
35548 if(c.titlebar !== false){
35549 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35550 this.collapseBtn.on("click", this.collapse, this);
35551 this.collapseBtn.enableDisplayMode();
35553 if(c.showPin === true || this.showPin){
35554 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35555 this.stickBtn.enableDisplayMode();
35556 this.stickBtn.on("click", this.expand, this);
35557 this.stickBtn.hide();
35562 /** This region's collapsed element
35563 * @type Roo.Element */
35566 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35567 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35570 if(c.floatable !== false){
35571 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35572 this.collapsedEl.on("click", this.collapseClick, this);
35575 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35576 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35577 id: "message", unselectable: "on", style:{"float":"left"}});
35578 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35580 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35581 this.expandBtn.on("click", this.expand, this);
35585 if(this.collapseBtn){
35586 this.collapseBtn.setVisible(c.collapsible == true);
35589 this.cmargins = c.cmargins || this.cmargins ||
35590 (this.position == "west" || this.position == "east" ?
35591 {top: 0, left: 2, right:2, bottom: 0} :
35592 {top: 2, left: 0, right:0, bottom: 2});
35594 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35597 this.bottomTabs = c.tabPosition != "top";
35599 this.autoScroll = c.autoScroll || false;
35604 this.duration = c.duration || .30;
35605 this.slideDuration = c.slideDuration || .45;
35610 * Returns true if this region is currently visible.
35611 * @return {Boolean}
35613 isVisible : function(){
35614 return this.visible;
35618 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35619 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35621 //setCollapsedTitle : function(title){
35622 // title = title || " ";
35623 // if(this.collapsedTitleTextEl){
35624 // this.collapsedTitleTextEl.innerHTML = title;
35628 getBox : function(){
35630 // if(!this.collapsed){
35631 b = this.el.getBox(false, true);
35633 // b = this.collapsedEl.getBox(false, true);
35638 getMargins : function(){
35639 return this.margins;
35640 //return this.collapsed ? this.cmargins : this.margins;
35643 highlight : function(){
35644 this.el.addClass("x-layout-panel-dragover");
35647 unhighlight : function(){
35648 this.el.removeClass("x-layout-panel-dragover");
35651 updateBox : function(box)
35653 if (!this.bodyEl) {
35654 return; // not rendered yet..
35658 if(!this.collapsed){
35659 this.el.dom.style.left = box.x + "px";
35660 this.el.dom.style.top = box.y + "px";
35661 this.updateBody(box.width, box.height);
35663 this.collapsedEl.dom.style.left = box.x + "px";
35664 this.collapsedEl.dom.style.top = box.y + "px";
35665 this.collapsedEl.setSize(box.width, box.height);
35668 this.tabs.autoSizeTabs();
35672 updateBody : function(w, h)
35675 this.el.setWidth(w);
35676 w -= this.el.getBorderWidth("rl");
35677 if(this.config.adjustments){
35678 w += this.config.adjustments[0];
35681 if(h !== null && h > 0){
35682 this.el.setHeight(h);
35683 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35684 h -= this.el.getBorderWidth("tb");
35685 if(this.config.adjustments){
35686 h += this.config.adjustments[1];
35688 this.bodyEl.setHeight(h);
35690 h = this.tabs.syncHeight(h);
35693 if(this.panelSize){
35694 w = w !== null ? w : this.panelSize.width;
35695 h = h !== null ? h : this.panelSize.height;
35697 if(this.activePanel){
35698 var el = this.activePanel.getEl();
35699 w = w !== null ? w : el.getWidth();
35700 h = h !== null ? h : el.getHeight();
35701 this.panelSize = {width: w, height: h};
35702 this.activePanel.setSize(w, h);
35704 if(Roo.isIE && this.tabs){
35705 this.tabs.el.repaint();
35710 * Returns the container element for this region.
35711 * @return {Roo.Element}
35713 getEl : function(){
35718 * Hides this region.
35721 //if(!this.collapsed){
35722 this.el.dom.style.left = "-2000px";
35725 // this.collapsedEl.dom.style.left = "-2000px";
35726 // this.collapsedEl.hide();
35728 this.visible = false;
35729 this.fireEvent("visibilitychange", this, false);
35733 * Shows this region if it was previously hidden.
35736 //if(!this.collapsed){
35739 // this.collapsedEl.show();
35741 this.visible = true;
35742 this.fireEvent("visibilitychange", this, true);
35745 closeClicked : function(){
35746 if(this.activePanel){
35747 this.remove(this.activePanel);
35751 collapseClick : function(e){
35753 e.stopPropagation();
35756 e.stopPropagation();
35762 * Collapses this region.
35763 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35766 collapse : function(skipAnim, skipCheck = false){
35767 if(this.collapsed) {
35771 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35773 this.collapsed = true;
35775 this.split.el.hide();
35777 if(this.config.animate && skipAnim !== true){
35778 this.fireEvent("invalidated", this);
35779 this.animateCollapse();
35781 this.el.setLocation(-20000,-20000);
35783 this.collapsedEl.show();
35784 this.fireEvent("collapsed", this);
35785 this.fireEvent("invalidated", this);
35791 animateCollapse : function(){
35796 * Expands this region if it was previously collapsed.
35797 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35798 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35801 expand : function(e, skipAnim){
35803 e.stopPropagation();
35805 if(!this.collapsed || this.el.hasActiveFx()) {
35809 this.afterSlideIn();
35812 this.collapsed = false;
35813 if(this.config.animate && skipAnim !== true){
35814 this.animateExpand();
35818 this.split.el.show();
35820 this.collapsedEl.setLocation(-2000,-2000);
35821 this.collapsedEl.hide();
35822 this.fireEvent("invalidated", this);
35823 this.fireEvent("expanded", this);
35827 animateExpand : function(){
35831 initTabs : function()
35833 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35835 var ts = new Roo.bootstrap.panel.Tabs({
35836 el: this.bodyEl.dom,
35837 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35838 disableTooltips: this.config.disableTabTips,
35839 toolbar : this.config.toolbar
35842 if(this.config.hideTabs){
35843 ts.stripWrap.setDisplayed(false);
35846 ts.resizeTabs = this.config.resizeTabs === true;
35847 ts.minTabWidth = this.config.minTabWidth || 40;
35848 ts.maxTabWidth = this.config.maxTabWidth || 250;
35849 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35850 ts.monitorResize = false;
35851 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35852 ts.bodyEl.addClass('roo-layout-tabs-body');
35853 this.panels.each(this.initPanelAsTab, this);
35856 initPanelAsTab : function(panel){
35857 var ti = this.tabs.addTab(
35861 this.config.closeOnTab && panel.isClosable(),
35864 if(panel.tabTip !== undefined){
35865 ti.setTooltip(panel.tabTip);
35867 ti.on("activate", function(){
35868 this.setActivePanel(panel);
35871 if(this.config.closeOnTab){
35872 ti.on("beforeclose", function(t, e){
35874 this.remove(panel);
35878 panel.tabItem = ti;
35883 updatePanelTitle : function(panel, title)
35885 if(this.activePanel == panel){
35886 this.updateTitle(title);
35889 var ti = this.tabs.getTab(panel.getEl().id);
35891 if(panel.tabTip !== undefined){
35892 ti.setTooltip(panel.tabTip);
35897 updateTitle : function(title){
35898 if(this.titleTextEl && !this.config.title){
35899 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35903 setActivePanel : function(panel)
35905 panel = this.getPanel(panel);
35906 if(this.activePanel && this.activePanel != panel){
35907 if(this.activePanel.setActiveState(false) === false){
35911 this.activePanel = panel;
35912 panel.setActiveState(true);
35913 if(this.panelSize){
35914 panel.setSize(this.panelSize.width, this.panelSize.height);
35917 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35919 this.updateTitle(panel.getTitle());
35921 this.fireEvent("invalidated", this);
35923 this.fireEvent("panelactivated", this, panel);
35927 * Shows the specified panel.
35928 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35929 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35931 showPanel : function(panel)
35933 panel = this.getPanel(panel);
35936 var tab = this.tabs.getTab(panel.getEl().id);
35937 if(tab.isHidden()){
35938 this.tabs.unhideTab(tab.id);
35942 this.setActivePanel(panel);
35949 * Get the active panel for this region.
35950 * @return {Roo.ContentPanel} The active panel or null
35952 getActivePanel : function(){
35953 return this.activePanel;
35956 validateVisibility : function(){
35957 if(this.panels.getCount() < 1){
35958 this.updateTitle(" ");
35959 this.closeBtn.hide();
35962 if(!this.isVisible()){
35969 * Adds the passed ContentPanel(s) to this region.
35970 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35971 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35973 add : function(panel)
35975 if(arguments.length > 1){
35976 for(var i = 0, len = arguments.length; i < len; i++) {
35977 this.add(arguments[i]);
35982 // if we have not been rendered yet, then we can not really do much of this..
35983 if (!this.bodyEl) {
35984 this.unrendered_panels.push(panel);
35991 if(this.hasPanel(panel)){
35992 this.showPanel(panel);
35995 panel.setRegion(this);
35996 this.panels.add(panel);
35997 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35998 // sinle panel - no tab...?? would it not be better to render it with the tabs,
35999 // and hide them... ???
36000 this.bodyEl.dom.appendChild(panel.getEl().dom);
36001 if(panel.background !== true){
36002 this.setActivePanel(panel);
36004 this.fireEvent("paneladded", this, panel);
36011 this.initPanelAsTab(panel);
36015 if(panel.background !== true){
36016 this.tabs.activate(panel.getEl().id);
36018 this.fireEvent("paneladded", this, panel);
36023 * Hides the tab for the specified panel.
36024 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36026 hidePanel : function(panel){
36027 if(this.tabs && (panel = this.getPanel(panel))){
36028 this.tabs.hideTab(panel.getEl().id);
36033 * Unhides the tab for a previously hidden panel.
36034 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36036 unhidePanel : function(panel){
36037 if(this.tabs && (panel = this.getPanel(panel))){
36038 this.tabs.unhideTab(panel.getEl().id);
36042 clearPanels : function(){
36043 while(this.panels.getCount() > 0){
36044 this.remove(this.panels.first());
36049 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36050 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36051 * @param {Boolean} preservePanel Overrides the config preservePanel option
36052 * @return {Roo.ContentPanel} The panel that was removed
36054 remove : function(panel, preservePanel)
36056 panel = this.getPanel(panel);
36061 this.fireEvent("beforeremove", this, panel, e);
36062 if(e.cancel === true){
36065 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36066 var panelId = panel.getId();
36067 this.panels.removeKey(panelId);
36069 document.body.appendChild(panel.getEl().dom);
36072 this.tabs.removeTab(panel.getEl().id);
36073 }else if (!preservePanel){
36074 this.bodyEl.dom.removeChild(panel.getEl().dom);
36076 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36077 var p = this.panels.first();
36078 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36079 tempEl.appendChild(p.getEl().dom);
36080 this.bodyEl.update("");
36081 this.bodyEl.dom.appendChild(p.getEl().dom);
36083 this.updateTitle(p.getTitle());
36085 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36086 this.setActivePanel(p);
36088 panel.setRegion(null);
36089 if(this.activePanel == panel){
36090 this.activePanel = null;
36092 if(this.config.autoDestroy !== false && preservePanel !== true){
36093 try{panel.destroy();}catch(e){}
36095 this.fireEvent("panelremoved", this, panel);
36100 * Returns the TabPanel component used by this region
36101 * @return {Roo.TabPanel}
36103 getTabs : function(){
36107 createTool : function(parentEl, className){
36108 var btn = Roo.DomHelper.append(parentEl, {
36110 cls: "x-layout-tools-button",
36113 cls: "roo-layout-tools-button-inner " + className,
36117 btn.addClassOnOver("roo-layout-tools-button-over");
36122 * Ext JS Library 1.1.1
36123 * Copyright(c) 2006-2007, Ext JS, LLC.
36125 * Originally Released Under LGPL - original licence link has changed is not relivant.
36128 * <script type="text/javascript">
36134 * @class Roo.SplitLayoutRegion
36135 * @extends Roo.LayoutRegion
36136 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36138 Roo.bootstrap.layout.Split = function(config){
36139 this.cursor = config.cursor;
36140 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36143 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36145 splitTip : "Drag to resize.",
36146 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36147 useSplitTips : false,
36149 applyConfig : function(config){
36150 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36153 onRender : function(ctr,pos) {
36155 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36156 if(!this.config.split){
36161 var splitEl = Roo.DomHelper.append(ctr.dom, {
36163 id: this.el.id + "-split",
36164 cls: "roo-layout-split roo-layout-split-"+this.position,
36167 /** The SplitBar for this region
36168 * @type Roo.SplitBar */
36169 // does not exist yet...
36170 Roo.log([this.position, this.orientation]);
36172 this.split = new Roo.bootstrap.SplitBar({
36173 dragElement : splitEl,
36174 resizingElement: this.el,
36175 orientation : this.orientation
36178 this.split.on("moved", this.onSplitMove, this);
36179 this.split.useShim = this.config.useShim === true;
36180 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36181 if(this.useSplitTips){
36182 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36184 //if(config.collapsible){
36185 // this.split.el.on("dblclick", this.collapse, this);
36188 if(typeof this.config.minSize != "undefined"){
36189 this.split.minSize = this.config.minSize;
36191 if(typeof this.config.maxSize != "undefined"){
36192 this.split.maxSize = this.config.maxSize;
36194 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36195 this.hideSplitter();
36200 getHMaxSize : function(){
36201 var cmax = this.config.maxSize || 10000;
36202 var center = this.mgr.getRegion("center");
36203 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36206 getVMaxSize : function(){
36207 var cmax = this.config.maxSize || 10000;
36208 var center = this.mgr.getRegion("center");
36209 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36212 onSplitMove : function(split, newSize){
36213 this.fireEvent("resized", this, newSize);
36217 * Returns the {@link Roo.SplitBar} for this region.
36218 * @return {Roo.SplitBar}
36220 getSplitBar : function(){
36225 this.hideSplitter();
36226 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36229 hideSplitter : function(){
36231 this.split.el.setLocation(-2000,-2000);
36232 this.split.el.hide();
36238 this.split.el.show();
36240 Roo.bootstrap.layout.Split.superclass.show.call(this);
36243 beforeSlide: function(){
36244 if(Roo.isGecko){// firefox overflow auto bug workaround
36245 this.bodyEl.clip();
36247 this.tabs.bodyEl.clip();
36249 if(this.activePanel){
36250 this.activePanel.getEl().clip();
36252 if(this.activePanel.beforeSlide){
36253 this.activePanel.beforeSlide();
36259 afterSlide : function(){
36260 if(Roo.isGecko){// firefox overflow auto bug workaround
36261 this.bodyEl.unclip();
36263 this.tabs.bodyEl.unclip();
36265 if(this.activePanel){
36266 this.activePanel.getEl().unclip();
36267 if(this.activePanel.afterSlide){
36268 this.activePanel.afterSlide();
36274 initAutoHide : function(){
36275 if(this.autoHide !== false){
36276 if(!this.autoHideHd){
36277 var st = new Roo.util.DelayedTask(this.slideIn, this);
36278 this.autoHideHd = {
36279 "mouseout": function(e){
36280 if(!e.within(this.el, true)){
36284 "mouseover" : function(e){
36290 this.el.on(this.autoHideHd);
36294 clearAutoHide : function(){
36295 if(this.autoHide !== false){
36296 this.el.un("mouseout", this.autoHideHd.mouseout);
36297 this.el.un("mouseover", this.autoHideHd.mouseover);
36301 clearMonitor : function(){
36302 Roo.get(document).un("click", this.slideInIf, this);
36305 // these names are backwards but not changed for compat
36306 slideOut : function(){
36307 if(this.isSlid || this.el.hasActiveFx()){
36310 this.isSlid = true;
36311 if(this.collapseBtn){
36312 this.collapseBtn.hide();
36314 this.closeBtnState = this.closeBtn.getStyle('display');
36315 this.closeBtn.hide();
36317 this.stickBtn.show();
36320 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36321 this.beforeSlide();
36322 this.el.setStyle("z-index", 10001);
36323 this.el.slideIn(this.getSlideAnchor(), {
36324 callback: function(){
36326 this.initAutoHide();
36327 Roo.get(document).on("click", this.slideInIf, this);
36328 this.fireEvent("slideshow", this);
36335 afterSlideIn : function(){
36336 this.clearAutoHide();
36337 this.isSlid = false;
36338 this.clearMonitor();
36339 this.el.setStyle("z-index", "");
36340 if(this.collapseBtn){
36341 this.collapseBtn.show();
36343 this.closeBtn.setStyle('display', this.closeBtnState);
36345 this.stickBtn.hide();
36347 this.fireEvent("slidehide", this);
36350 slideIn : function(cb){
36351 if(!this.isSlid || this.el.hasActiveFx()){
36355 this.isSlid = false;
36356 this.beforeSlide();
36357 this.el.slideOut(this.getSlideAnchor(), {
36358 callback: function(){
36359 this.el.setLeftTop(-10000, -10000);
36361 this.afterSlideIn();
36369 slideInIf : function(e){
36370 if(!e.within(this.el)){
36375 animateCollapse : function(){
36376 this.beforeSlide();
36377 this.el.setStyle("z-index", 20000);
36378 var anchor = this.getSlideAnchor();
36379 this.el.slideOut(anchor, {
36380 callback : function(){
36381 this.el.setStyle("z-index", "");
36382 this.collapsedEl.slideIn(anchor, {duration:.3});
36384 this.el.setLocation(-10000,-10000);
36386 this.fireEvent("collapsed", this);
36393 animateExpand : function(){
36394 this.beforeSlide();
36395 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36396 this.el.setStyle("z-index", 20000);
36397 this.collapsedEl.hide({
36400 this.el.slideIn(this.getSlideAnchor(), {
36401 callback : function(){
36402 this.el.setStyle("z-index", "");
36405 this.split.el.show();
36407 this.fireEvent("invalidated", this);
36408 this.fireEvent("expanded", this);
36436 getAnchor : function(){
36437 return this.anchors[this.position];
36440 getCollapseAnchor : function(){
36441 return this.canchors[this.position];
36444 getSlideAnchor : function(){
36445 return this.sanchors[this.position];
36448 getAlignAdj : function(){
36449 var cm = this.cmargins;
36450 switch(this.position){
36466 getExpandAdj : function(){
36467 var c = this.collapsedEl, cm = this.cmargins;
36468 switch(this.position){
36470 return [-(cm.right+c.getWidth()+cm.left), 0];
36473 return [cm.right+c.getWidth()+cm.left, 0];
36476 return [0, -(cm.top+cm.bottom+c.getHeight())];
36479 return [0, cm.top+cm.bottom+c.getHeight()];
36485 * Ext JS Library 1.1.1
36486 * Copyright(c) 2006-2007, Ext JS, LLC.
36488 * Originally Released Under LGPL - original licence link has changed is not relivant.
36491 * <script type="text/javascript">
36494 * These classes are private internal classes
36496 Roo.bootstrap.layout.Center = function(config){
36497 config.region = "center";
36498 Roo.bootstrap.layout.Region.call(this, config);
36499 this.visible = true;
36500 this.minWidth = config.minWidth || 20;
36501 this.minHeight = config.minHeight || 20;
36504 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36506 // center panel can't be hidden
36510 // center panel can't be hidden
36513 getMinWidth: function(){
36514 return this.minWidth;
36517 getMinHeight: function(){
36518 return this.minHeight;
36531 Roo.bootstrap.layout.North = function(config)
36533 config.region = 'north';
36534 config.cursor = 'n-resize';
36536 Roo.bootstrap.layout.Split.call(this, config);
36540 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36541 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36542 this.split.el.addClass("roo-layout-split-v");
36544 var size = config.initialSize || config.height;
36545 if(typeof size != "undefined"){
36546 this.el.setHeight(size);
36549 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36551 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36555 getBox : function(){
36556 if(this.collapsed){
36557 return this.collapsedEl.getBox();
36559 var box = this.el.getBox();
36561 box.height += this.split.el.getHeight();
36566 updateBox : function(box){
36567 if(this.split && !this.collapsed){
36568 box.height -= this.split.el.getHeight();
36569 this.split.el.setLeft(box.x);
36570 this.split.el.setTop(box.y+box.height);
36571 this.split.el.setWidth(box.width);
36573 if(this.collapsed){
36574 this.updateBody(box.width, null);
36576 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36584 Roo.bootstrap.layout.South = function(config){
36585 config.region = 'south';
36586 config.cursor = 's-resize';
36587 Roo.bootstrap.layout.Split.call(this, config);
36589 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36590 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36591 this.split.el.addClass("roo-layout-split-v");
36593 var size = config.initialSize || config.height;
36594 if(typeof size != "undefined"){
36595 this.el.setHeight(size);
36599 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36600 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36601 getBox : function(){
36602 if(this.collapsed){
36603 return this.collapsedEl.getBox();
36605 var box = this.el.getBox();
36607 var sh = this.split.el.getHeight();
36614 updateBox : function(box){
36615 if(this.split && !this.collapsed){
36616 var sh = this.split.el.getHeight();
36619 this.split.el.setLeft(box.x);
36620 this.split.el.setTop(box.y-sh);
36621 this.split.el.setWidth(box.width);
36623 if(this.collapsed){
36624 this.updateBody(box.width, null);
36626 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36630 Roo.bootstrap.layout.East = function(config){
36631 config.region = "east";
36632 config.cursor = "e-resize";
36633 Roo.bootstrap.layout.Split.call(this, config);
36635 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36636 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36637 this.split.el.addClass("roo-layout-split-h");
36639 var size = config.initialSize || config.width;
36640 if(typeof size != "undefined"){
36641 this.el.setWidth(size);
36644 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36645 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36646 getBox : function(){
36647 if(this.collapsed){
36648 return this.collapsedEl.getBox();
36650 var box = this.el.getBox();
36652 var sw = this.split.el.getWidth();
36659 updateBox : function(box){
36660 if(this.split && !this.collapsed){
36661 var sw = this.split.el.getWidth();
36663 this.split.el.setLeft(box.x);
36664 this.split.el.setTop(box.y);
36665 this.split.el.setHeight(box.height);
36668 if(this.collapsed){
36669 this.updateBody(null, box.height);
36671 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36675 Roo.bootstrap.layout.West = function(config){
36676 config.region = "west";
36677 config.cursor = "w-resize";
36679 Roo.bootstrap.layout.Split.call(this, config);
36681 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36682 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36683 this.split.el.addClass("roo-layout-split-h");
36687 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36688 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36690 onRender: function(ctr, pos)
36692 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36693 var size = this.config.initialSize || this.config.width;
36694 if(typeof size != "undefined"){
36695 this.el.setWidth(size);
36699 getBox : function(){
36700 if(this.collapsed){
36701 return this.collapsedEl.getBox();
36703 var box = this.el.getBox();
36705 box.width += this.split.el.getWidth();
36710 updateBox : function(box){
36711 if(this.split && !this.collapsed){
36712 var sw = this.split.el.getWidth();
36714 this.split.el.setLeft(box.x+box.width);
36715 this.split.el.setTop(box.y);
36716 this.split.el.setHeight(box.height);
36718 if(this.collapsed){
36719 this.updateBody(null, box.height);
36721 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36724 Roo.namespace("Roo.bootstrap.panel");/*
36726 * Ext JS Library 1.1.1
36727 * Copyright(c) 2006-2007, Ext JS, LLC.
36729 * Originally Released Under LGPL - original licence link has changed is not relivant.
36732 * <script type="text/javascript">
36735 * @class Roo.ContentPanel
36736 * @extends Roo.util.Observable
36737 * A basic ContentPanel element.
36738 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36739 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36740 * @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
36741 * @cfg {Boolean} closable True if the panel can be closed/removed
36742 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36743 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36744 * @cfg {Toolbar} toolbar A toolbar for this panel
36745 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36746 * @cfg {String} title The title for this panel
36747 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36748 * @cfg {String} url Calls {@link #setUrl} with this value
36749 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36750 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36751 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36752 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36753 * @cfg {Boolean} badges render the badges
36756 * Create a new ContentPanel.
36757 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36758 * @param {String/Object} config A string to set only the title or a config object
36759 * @param {String} content (optional) Set the HTML content for this panel
36760 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36762 Roo.bootstrap.panel.Content = function( config){
36764 this.tpl = config.tpl || false;
36766 var el = config.el;
36767 var content = config.content;
36769 if(config.autoCreate){ // xtype is available if this is called from factory
36772 this.el = Roo.get(el);
36773 if(!this.el && config && config.autoCreate){
36774 if(typeof config.autoCreate == "object"){
36775 if(!config.autoCreate.id){
36776 config.autoCreate.id = config.id||el;
36778 this.el = Roo.DomHelper.append(document.body,
36779 config.autoCreate, true);
36781 var elcfg = { tag: "div",
36782 cls: "roo-layout-inactive-content",
36786 elcfg.html = config.html;
36790 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36793 this.closable = false;
36794 this.loaded = false;
36795 this.active = false;
36798 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36800 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36802 this.wrapEl = this.el; //this.el.wrap();
36804 if (config.toolbar.items) {
36805 ti = config.toolbar.items ;
36806 delete config.toolbar.items ;
36810 this.toolbar.render(this.wrapEl, 'before');
36811 for(var i =0;i < ti.length;i++) {
36812 // Roo.log(['add child', items[i]]);
36813 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36815 this.toolbar.items = nitems;
36816 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36817 delete config.toolbar;
36821 // xtype created footer. - not sure if will work as we normally have to render first..
36822 if (this.footer && !this.footer.el && this.footer.xtype) {
36823 if (!this.wrapEl) {
36824 this.wrapEl = this.el.wrap();
36827 this.footer.container = this.wrapEl.createChild();
36829 this.footer = Roo.factory(this.footer, Roo);
36834 if(typeof config == "string"){
36835 this.title = config;
36837 Roo.apply(this, config);
36841 this.resizeEl = Roo.get(this.resizeEl, true);
36843 this.resizeEl = this.el;
36845 // handle view.xtype
36853 * Fires when this panel is activated.
36854 * @param {Roo.ContentPanel} this
36858 * @event deactivate
36859 * Fires when this panel is activated.
36860 * @param {Roo.ContentPanel} this
36862 "deactivate" : true,
36866 * Fires when this panel is resized if fitToFrame is true.
36867 * @param {Roo.ContentPanel} this
36868 * @param {Number} width The width after any component adjustments
36869 * @param {Number} height The height after any component adjustments
36875 * Fires when this tab is created
36876 * @param {Roo.ContentPanel} this
36887 if(this.autoScroll){
36888 this.resizeEl.setStyle("overflow", "auto");
36890 // fix randome scrolling
36891 //this.el.on('scroll', function() {
36892 // Roo.log('fix random scolling');
36893 // this.scrollTo('top',0);
36896 content = content || this.content;
36898 this.setContent(content);
36900 if(config && config.url){
36901 this.setUrl(this.url, this.params, this.loadOnce);
36906 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36908 if (this.view && typeof(this.view.xtype) != 'undefined') {
36909 this.view.el = this.el.appendChild(document.createElement("div"));
36910 this.view = Roo.factory(this.view);
36911 this.view.render && this.view.render(false, '');
36915 this.fireEvent('render', this);
36918 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36922 setRegion : function(region){
36923 this.region = region;
36924 this.setActiveClass(region && !this.background);
36928 setActiveClass: function(state)
36931 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36932 this.el.setStyle('position','relative');
36934 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36935 this.el.setStyle('position', 'absolute');
36940 * Returns the toolbar for this Panel if one was configured.
36941 * @return {Roo.Toolbar}
36943 getToolbar : function(){
36944 return this.toolbar;
36947 setActiveState : function(active)
36949 this.active = active;
36950 this.setActiveClass(active);
36952 if(this.fireEvent("deactivate", this) === false){
36957 this.fireEvent("activate", this);
36961 * Updates this panel's element
36962 * @param {String} content The new content
36963 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36965 setContent : function(content, loadScripts){
36966 this.el.update(content, loadScripts);
36969 ignoreResize : function(w, h){
36970 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36973 this.lastSize = {width: w, height: h};
36978 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36979 * @return {Roo.UpdateManager} The UpdateManager
36981 getUpdateManager : function(){
36982 return this.el.getUpdateManager();
36985 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36986 * @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:
36989 url: "your-url.php",
36990 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36991 callback: yourFunction,
36992 scope: yourObject, //(optional scope)
36995 text: "Loading...",
37000 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37001 * 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.
37002 * @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}
37003 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37004 * @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.
37005 * @return {Roo.ContentPanel} this
37008 var um = this.el.getUpdateManager();
37009 um.update.apply(um, arguments);
37015 * 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.
37016 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37017 * @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)
37018 * @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)
37019 * @return {Roo.UpdateManager} The UpdateManager
37021 setUrl : function(url, params, loadOnce){
37022 if(this.refreshDelegate){
37023 this.removeListener("activate", this.refreshDelegate);
37025 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37026 this.on("activate", this.refreshDelegate);
37027 return this.el.getUpdateManager();
37030 _handleRefresh : function(url, params, loadOnce){
37031 if(!loadOnce || !this.loaded){
37032 var updater = this.el.getUpdateManager();
37033 updater.update(url, params, this._setLoaded.createDelegate(this));
37037 _setLoaded : function(){
37038 this.loaded = true;
37042 * Returns this panel's id
37045 getId : function(){
37050 * Returns this panel's element - used by regiosn to add.
37051 * @return {Roo.Element}
37053 getEl : function(){
37054 return this.wrapEl || this.el;
37059 adjustForComponents : function(width, height)
37061 //Roo.log('adjustForComponents ');
37062 if(this.resizeEl != this.el){
37063 width -= this.el.getFrameWidth('lr');
37064 height -= this.el.getFrameWidth('tb');
37067 var te = this.toolbar.getEl();
37068 te.setWidth(width);
37069 height -= te.getHeight();
37072 var te = this.footer.getEl();
37073 te.setWidth(width);
37074 height -= te.getHeight();
37078 if(this.adjustments){
37079 width += this.adjustments[0];
37080 height += this.adjustments[1];
37082 return {"width": width, "height": height};
37085 setSize : function(width, height){
37086 if(this.fitToFrame && !this.ignoreResize(width, height)){
37087 if(this.fitContainer && this.resizeEl != this.el){
37088 this.el.setSize(width, height);
37090 var size = this.adjustForComponents(width, height);
37091 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37092 this.fireEvent('resize', this, size.width, size.height);
37097 * Returns this panel's title
37100 getTitle : function(){
37102 if (typeof(this.title) != 'object') {
37107 for (var k in this.title) {
37108 if (!this.title.hasOwnProperty(k)) {
37112 if (k.indexOf('-') >= 0) {
37113 var s = k.split('-');
37114 for (var i = 0; i<s.length; i++) {
37115 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37118 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37125 * Set this panel's title
37126 * @param {String} title
37128 setTitle : function(title){
37129 this.title = title;
37131 this.region.updatePanelTitle(this, title);
37136 * Returns true is this panel was configured to be closable
37137 * @return {Boolean}
37139 isClosable : function(){
37140 return this.closable;
37143 beforeSlide : function(){
37145 this.resizeEl.clip();
37148 afterSlide : function(){
37150 this.resizeEl.unclip();
37154 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37155 * Will fail silently if the {@link #setUrl} method has not been called.
37156 * This does not activate the panel, just updates its content.
37158 refresh : function(){
37159 if(this.refreshDelegate){
37160 this.loaded = false;
37161 this.refreshDelegate();
37166 * Destroys this panel
37168 destroy : function(){
37169 this.el.removeAllListeners();
37170 var tempEl = document.createElement("span");
37171 tempEl.appendChild(this.el.dom);
37172 tempEl.innerHTML = "";
37178 * form - if the content panel contains a form - this is a reference to it.
37179 * @type {Roo.form.Form}
37183 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37184 * This contains a reference to it.
37190 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37200 * @param {Object} cfg Xtype definition of item to add.
37204 getChildContainer: function () {
37205 return this.getEl();
37210 var ret = new Roo.factory(cfg);
37215 if (cfg.xtype.match(/^Form$/)) {
37218 //if (this.footer) {
37219 // el = this.footer.container.insertSibling(false, 'before');
37221 el = this.el.createChild();
37224 this.form = new Roo.form.Form(cfg);
37227 if ( this.form.allItems.length) {
37228 this.form.render(el.dom);
37232 // should only have one of theses..
37233 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37234 // views.. should not be just added - used named prop 'view''
37236 cfg.el = this.el.appendChild(document.createElement("div"));
37239 var ret = new Roo.factory(cfg);
37241 ret.render && ret.render(false, ''); // render blank..
37251 * @class Roo.bootstrap.panel.Grid
37252 * @extends Roo.bootstrap.panel.Content
37254 * Create a new GridPanel.
37255 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37256 * @param {Object} config A the config object
37262 Roo.bootstrap.panel.Grid = function(config)
37266 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37267 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37269 config.el = this.wrapper;
37270 //this.el = this.wrapper;
37272 if (config.container) {
37273 // ctor'ed from a Border/panel.grid
37276 this.wrapper.setStyle("overflow", "hidden");
37277 this.wrapper.addClass('roo-grid-container');
37282 if(config.toolbar){
37283 var tool_el = this.wrapper.createChild();
37284 this.toolbar = Roo.factory(config.toolbar);
37286 if (config.toolbar.items) {
37287 ti = config.toolbar.items ;
37288 delete config.toolbar.items ;
37292 this.toolbar.render(tool_el);
37293 for(var i =0;i < ti.length;i++) {
37294 // Roo.log(['add child', items[i]]);
37295 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37297 this.toolbar.items = nitems;
37299 delete config.toolbar;
37302 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37303 config.grid.scrollBody = true;;
37304 config.grid.monitorWindowResize = false; // turn off autosizing
37305 config.grid.autoHeight = false;
37306 config.grid.autoWidth = false;
37308 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37310 if (config.background) {
37311 // render grid on panel activation (if panel background)
37312 this.on('activate', function(gp) {
37313 if (!gp.grid.rendered) {
37314 gp.grid.render(this.wrapper);
37315 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37320 this.grid.render(this.wrapper);
37321 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37324 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37325 // ??? needed ??? config.el = this.wrapper;
37330 // xtype created footer. - not sure if will work as we normally have to render first..
37331 if (this.footer && !this.footer.el && this.footer.xtype) {
37333 var ctr = this.grid.getView().getFooterPanel(true);
37334 this.footer.dataSource = this.grid.dataSource;
37335 this.footer = Roo.factory(this.footer, Roo);
37336 this.footer.render(ctr);
37346 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37347 getId : function(){
37348 return this.grid.id;
37352 * Returns the grid for this panel
37353 * @return {Roo.bootstrap.Table}
37355 getGrid : function(){
37359 setSize : function(width, height){
37360 if(!this.ignoreResize(width, height)){
37361 var grid = this.grid;
37362 var size = this.adjustForComponents(width, height);
37363 var gridel = grid.getGridEl();
37364 gridel.setSize(size.width, size.height);
37366 var thd = grid.getGridEl().select('thead',true).first();
37367 var tbd = grid.getGridEl().select('tbody', true).first();
37369 tbd.setSize(width, height - thd.getHeight());
37378 beforeSlide : function(){
37379 this.grid.getView().scroller.clip();
37382 afterSlide : function(){
37383 this.grid.getView().scroller.unclip();
37386 destroy : function(){
37387 this.grid.destroy();
37389 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37394 * @class Roo.bootstrap.panel.Nest
37395 * @extends Roo.bootstrap.panel.Content
37397 * Create a new Panel, that can contain a layout.Border.
37400 * @param {Roo.BorderLayout} layout The layout for this panel
37401 * @param {String/Object} config A string to set only the title or a config object
37403 Roo.bootstrap.panel.Nest = function(config)
37405 // construct with only one argument..
37406 /* FIXME - implement nicer consturctors
37407 if (layout.layout) {
37409 layout = config.layout;
37410 delete config.layout;
37412 if (layout.xtype && !layout.getEl) {
37413 // then layout needs constructing..
37414 layout = Roo.factory(layout, Roo);
37418 config.el = config.layout.getEl();
37420 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37422 config.layout.monitorWindowResize = false; // turn off autosizing
37423 this.layout = config.layout;
37424 this.layout.getEl().addClass("roo-layout-nested-layout");
37431 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37433 setSize : function(width, height){
37434 if(!this.ignoreResize(width, height)){
37435 var size = this.adjustForComponents(width, height);
37436 var el = this.layout.getEl();
37437 if (size.height < 1) {
37438 el.setWidth(size.width);
37440 el.setSize(size.width, size.height);
37442 var touch = el.dom.offsetWidth;
37443 this.layout.layout();
37444 // ie requires a double layout on the first pass
37445 if(Roo.isIE && !this.initialized){
37446 this.initialized = true;
37447 this.layout.layout();
37452 // activate all subpanels if not currently active..
37454 setActiveState : function(active){
37455 this.active = active;
37456 this.setActiveClass(active);
37459 this.fireEvent("deactivate", this);
37463 this.fireEvent("activate", this);
37464 // not sure if this should happen before or after..
37465 if (!this.layout) {
37466 return; // should not happen..
37469 for (var r in this.layout.regions) {
37470 reg = this.layout.getRegion(r);
37471 if (reg.getActivePanel()) {
37472 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37473 reg.setActivePanel(reg.getActivePanel());
37476 if (!reg.panels.length) {
37479 reg.showPanel(reg.getPanel(0));
37488 * Returns the nested BorderLayout for this panel
37489 * @return {Roo.BorderLayout}
37491 getLayout : function(){
37492 return this.layout;
37496 * Adds a xtype elements to the layout of the nested panel
37500 xtype : 'ContentPanel',
37507 xtype : 'NestedLayoutPanel',
37513 items : [ ... list of content panels or nested layout panels.. ]
37517 * @param {Object} cfg Xtype definition of item to add.
37519 addxtype : function(cfg) {
37520 return this.layout.addxtype(cfg);
37525 * Ext JS Library 1.1.1
37526 * Copyright(c) 2006-2007, Ext JS, LLC.
37528 * Originally Released Under LGPL - original licence link has changed is not relivant.
37531 * <script type="text/javascript">
37534 * @class Roo.TabPanel
37535 * @extends Roo.util.Observable
37536 * A lightweight tab container.
37540 // basic tabs 1, built from existing content
37541 var tabs = new Roo.TabPanel("tabs1");
37542 tabs.addTab("script", "View Script");
37543 tabs.addTab("markup", "View Markup");
37544 tabs.activate("script");
37546 // more advanced tabs, built from javascript
37547 var jtabs = new Roo.TabPanel("jtabs");
37548 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37550 // set up the UpdateManager
37551 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37552 var updater = tab2.getUpdateManager();
37553 updater.setDefaultUrl("ajax1.htm");
37554 tab2.on('activate', updater.refresh, updater, true);
37556 // Use setUrl for Ajax loading
37557 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37558 tab3.setUrl("ajax2.htm", null, true);
37561 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37564 jtabs.activate("jtabs-1");
37567 * Create a new TabPanel.
37568 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37569 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37571 Roo.bootstrap.panel.Tabs = function(config){
37573 * The container element for this TabPanel.
37574 * @type Roo.Element
37576 this.el = Roo.get(config.el);
37579 if(typeof config == "boolean"){
37580 this.tabPosition = config ? "bottom" : "top";
37582 Roo.apply(this, config);
37586 if(this.tabPosition == "bottom"){
37587 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37588 this.el.addClass("roo-tabs-bottom");
37590 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37591 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37592 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37594 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37596 if(this.tabPosition != "bottom"){
37597 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37598 * @type Roo.Element
37600 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37601 this.el.addClass("roo-tabs-top");
37605 this.bodyEl.setStyle("position", "relative");
37607 this.active = null;
37608 this.activateDelegate = this.activate.createDelegate(this);
37613 * Fires when the active tab changes
37614 * @param {Roo.TabPanel} this
37615 * @param {Roo.TabPanelItem} activePanel The new active tab
37619 * @event beforetabchange
37620 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37621 * @param {Roo.TabPanel} this
37622 * @param {Object} e Set cancel to true on this object to cancel the tab change
37623 * @param {Roo.TabPanelItem} tab The tab being changed to
37625 "beforetabchange" : true
37628 Roo.EventManager.onWindowResize(this.onResize, this);
37629 this.cpad = this.el.getPadding("lr");
37630 this.hiddenCount = 0;
37633 // toolbar on the tabbar support...
37634 if (this.toolbar) {
37635 alert("no toolbar support yet");
37636 this.toolbar = false;
37638 var tcfg = this.toolbar;
37639 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37640 this.toolbar = new Roo.Toolbar(tcfg);
37641 if (Roo.isSafari) {
37642 var tbl = tcfg.container.child('table', true);
37643 tbl.setAttribute('width', '100%');
37651 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37654 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37656 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37658 tabPosition : "top",
37660 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37662 currentTabWidth : 0,
37664 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37668 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37672 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37674 preferredTabWidth : 175,
37676 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37678 resizeTabs : false,
37680 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37682 monitorResize : true,
37684 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37689 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37690 * @param {String} id The id of the div to use <b>or create</b>
37691 * @param {String} text The text for the tab
37692 * @param {String} content (optional) Content to put in the TabPanelItem body
37693 * @param {Boolean} closable (optional) True to create a close icon on the tab
37694 * @return {Roo.TabPanelItem} The created TabPanelItem
37696 addTab : function(id, text, content, closable, tpl)
37698 var item = new Roo.bootstrap.panel.TabItem({
37702 closable : closable,
37705 this.addTabItem(item);
37707 item.setContent(content);
37713 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37714 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37715 * @return {Roo.TabPanelItem}
37717 getTab : function(id){
37718 return this.items[id];
37722 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37723 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37725 hideTab : function(id){
37726 var t = this.items[id];
37729 this.hiddenCount++;
37730 this.autoSizeTabs();
37735 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37736 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37738 unhideTab : function(id){
37739 var t = this.items[id];
37741 t.setHidden(false);
37742 this.hiddenCount--;
37743 this.autoSizeTabs();
37748 * Adds an existing {@link Roo.TabPanelItem}.
37749 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37751 addTabItem : function(item){
37752 this.items[item.id] = item;
37753 this.items.push(item);
37754 // if(this.resizeTabs){
37755 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37756 // this.autoSizeTabs();
37758 // item.autoSize();
37763 * Removes a {@link Roo.TabPanelItem}.
37764 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37766 removeTab : function(id){
37767 var items = this.items;
37768 var tab = items[id];
37769 if(!tab) { return; }
37770 var index = items.indexOf(tab);
37771 if(this.active == tab && items.length > 1){
37772 var newTab = this.getNextAvailable(index);
37777 this.stripEl.dom.removeChild(tab.pnode.dom);
37778 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37779 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37781 items.splice(index, 1);
37782 delete this.items[tab.id];
37783 tab.fireEvent("close", tab);
37784 tab.purgeListeners();
37785 this.autoSizeTabs();
37788 getNextAvailable : function(start){
37789 var items = this.items;
37791 // look for a next tab that will slide over to
37792 // replace the one being removed
37793 while(index < items.length){
37794 var item = items[++index];
37795 if(item && !item.isHidden()){
37799 // if one isn't found select the previous tab (on the left)
37802 var item = items[--index];
37803 if(item && !item.isHidden()){
37811 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37812 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37814 disableTab : function(id){
37815 var tab = this.items[id];
37816 if(tab && this.active != tab){
37822 * Enables a {@link Roo.TabPanelItem} that is disabled.
37823 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37825 enableTab : function(id){
37826 var tab = this.items[id];
37831 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37832 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37833 * @return {Roo.TabPanelItem} The TabPanelItem.
37835 activate : function(id){
37836 var tab = this.items[id];
37840 if(tab == this.active || tab.disabled){
37844 this.fireEvent("beforetabchange", this, e, tab);
37845 if(e.cancel !== true && !tab.disabled){
37847 this.active.hide();
37849 this.active = this.items[id];
37850 this.active.show();
37851 this.fireEvent("tabchange", this, this.active);
37857 * Gets the active {@link Roo.TabPanelItem}.
37858 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37860 getActiveTab : function(){
37861 return this.active;
37865 * Updates the tab body element to fit the height of the container element
37866 * for overflow scrolling
37867 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37869 syncHeight : function(targetHeight){
37870 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37871 var bm = this.bodyEl.getMargins();
37872 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37873 this.bodyEl.setHeight(newHeight);
37877 onResize : function(){
37878 if(this.monitorResize){
37879 this.autoSizeTabs();
37884 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37886 beginUpdate : function(){
37887 this.updating = true;
37891 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37893 endUpdate : function(){
37894 this.updating = false;
37895 this.autoSizeTabs();
37899 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37901 autoSizeTabs : function(){
37902 var count = this.items.length;
37903 var vcount = count - this.hiddenCount;
37904 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37907 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37908 var availWidth = Math.floor(w / vcount);
37909 var b = this.stripBody;
37910 if(b.getWidth() > w){
37911 var tabs = this.items;
37912 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37913 if(availWidth < this.minTabWidth){
37914 /*if(!this.sleft){ // incomplete scrolling code
37915 this.createScrollButtons();
37918 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37921 if(this.currentTabWidth < this.preferredTabWidth){
37922 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37928 * Returns the number of tabs in this TabPanel.
37931 getCount : function(){
37932 return this.items.length;
37936 * Resizes all the tabs to the passed width
37937 * @param {Number} The new width
37939 setTabWidth : function(width){
37940 this.currentTabWidth = width;
37941 for(var i = 0, len = this.items.length; i < len; i++) {
37942 if(!this.items[i].isHidden()) {
37943 this.items[i].setWidth(width);
37949 * Destroys this TabPanel
37950 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37952 destroy : function(removeEl){
37953 Roo.EventManager.removeResizeListener(this.onResize, this);
37954 for(var i = 0, len = this.items.length; i < len; i++){
37955 this.items[i].purgeListeners();
37957 if(removeEl === true){
37958 this.el.update("");
37963 createStrip : function(container)
37965 var strip = document.createElement("nav");
37966 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37967 container.appendChild(strip);
37971 createStripList : function(strip)
37973 // div wrapper for retard IE
37974 // returns the "tr" element.
37975 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37976 //'<div class="x-tabs-strip-wrap">'+
37977 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37978 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37979 return strip.firstChild; //.firstChild.firstChild.firstChild;
37981 createBody : function(container)
37983 var body = document.createElement("div");
37984 Roo.id(body, "tab-body");
37985 //Roo.fly(body).addClass("x-tabs-body");
37986 Roo.fly(body).addClass("tab-content");
37987 container.appendChild(body);
37990 createItemBody :function(bodyEl, id){
37991 var body = Roo.getDom(id);
37993 body = document.createElement("div");
37996 //Roo.fly(body).addClass("x-tabs-item-body");
37997 Roo.fly(body).addClass("tab-pane");
37998 bodyEl.insertBefore(body, bodyEl.firstChild);
38002 createStripElements : function(stripEl, text, closable, tpl)
38004 var td = document.createElement("li"); // was td..
38007 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38010 stripEl.appendChild(td);
38012 td.className = "x-tabs-closable";
38013 if(!this.closeTpl){
38014 this.closeTpl = new Roo.Template(
38015 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38016 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38017 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38020 var el = this.closeTpl.overwrite(td, {"text": text});
38021 var close = el.getElementsByTagName("div")[0];
38022 var inner = el.getElementsByTagName("em")[0];
38023 return {"el": el, "close": close, "inner": inner};
38026 // not sure what this is..
38027 // if(!this.tabTpl){
38028 //this.tabTpl = new Roo.Template(
38029 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38030 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38032 // this.tabTpl = new Roo.Template(
38033 // '<a href="#">' +
38034 // '<span unselectable="on"' +
38035 // (this.disableTooltips ? '' : ' title="{text}"') +
38036 // ' >{text}</span></a>'
38042 var template = tpl || this.tabTpl || false;
38046 template = new Roo.Template(
38048 '<span unselectable="on"' +
38049 (this.disableTooltips ? '' : ' title="{text}"') +
38050 ' >{text}</span></a>'
38054 switch (typeof(template)) {
38058 template = new Roo.Template(template);
38064 var el = template.overwrite(td, {"text": text});
38066 var inner = el.getElementsByTagName("span")[0];
38068 return {"el": el, "inner": inner};
38076 * @class Roo.TabPanelItem
38077 * @extends Roo.util.Observable
38078 * Represents an individual item (tab plus body) in a TabPanel.
38079 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38080 * @param {String} id The id of this TabPanelItem
38081 * @param {String} text The text for the tab of this TabPanelItem
38082 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38084 Roo.bootstrap.panel.TabItem = function(config){
38086 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38087 * @type Roo.TabPanel
38089 this.tabPanel = config.panel;
38091 * The id for this TabPanelItem
38094 this.id = config.id;
38096 this.disabled = false;
38098 this.text = config.text;
38100 this.loaded = false;
38101 this.closable = config.closable;
38104 * The body element for this TabPanelItem.
38105 * @type Roo.Element
38107 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38108 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38109 this.bodyEl.setStyle("display", "block");
38110 this.bodyEl.setStyle("zoom", "1");
38111 //this.hideAction();
38113 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38115 this.el = Roo.get(els.el);
38116 this.inner = Roo.get(els.inner, true);
38117 this.textEl = Roo.get(this.el.dom.firstChild, true);
38118 this.pnode = Roo.get(els.el.parentNode, true);
38119 // this.el.on("mousedown", this.onTabMouseDown, this);
38120 this.el.on("click", this.onTabClick, this);
38122 if(config.closable){
38123 var c = Roo.get(els.close, true);
38124 c.dom.title = this.closeText;
38125 c.addClassOnOver("close-over");
38126 c.on("click", this.closeClick, this);
38132 * Fires when this tab becomes the active tab.
38133 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38134 * @param {Roo.TabPanelItem} this
38138 * @event beforeclose
38139 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38140 * @param {Roo.TabPanelItem} this
38141 * @param {Object} e Set cancel to true on this object to cancel the close.
38143 "beforeclose": true,
38146 * Fires when this tab is closed.
38147 * @param {Roo.TabPanelItem} this
38151 * @event deactivate
38152 * Fires when this tab is no longer the active tab.
38153 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38154 * @param {Roo.TabPanelItem} this
38156 "deactivate" : true
38158 this.hidden = false;
38160 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38163 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38165 purgeListeners : function(){
38166 Roo.util.Observable.prototype.purgeListeners.call(this);
38167 this.el.removeAllListeners();
38170 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38173 this.pnode.addClass("active");
38176 this.tabPanel.stripWrap.repaint();
38178 this.fireEvent("activate", this.tabPanel, this);
38182 * Returns true if this tab is the active tab.
38183 * @return {Boolean}
38185 isActive : function(){
38186 return this.tabPanel.getActiveTab() == this;
38190 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38193 this.pnode.removeClass("active");
38195 this.fireEvent("deactivate", this.tabPanel, this);
38198 hideAction : function(){
38199 this.bodyEl.hide();
38200 this.bodyEl.setStyle("position", "absolute");
38201 this.bodyEl.setLeft("-20000px");
38202 this.bodyEl.setTop("-20000px");
38205 showAction : function(){
38206 this.bodyEl.setStyle("position", "relative");
38207 this.bodyEl.setTop("");
38208 this.bodyEl.setLeft("");
38209 this.bodyEl.show();
38213 * Set the tooltip for the tab.
38214 * @param {String} tooltip The tab's tooltip
38216 setTooltip : function(text){
38217 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38218 this.textEl.dom.qtip = text;
38219 this.textEl.dom.removeAttribute('title');
38221 this.textEl.dom.title = text;
38225 onTabClick : function(e){
38226 e.preventDefault();
38227 this.tabPanel.activate(this.id);
38230 onTabMouseDown : function(e){
38231 e.preventDefault();
38232 this.tabPanel.activate(this.id);
38235 getWidth : function(){
38236 return this.inner.getWidth();
38239 setWidth : function(width){
38240 var iwidth = width - this.pnode.getPadding("lr");
38241 this.inner.setWidth(iwidth);
38242 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38243 this.pnode.setWidth(width);
38247 * Show or hide the tab
38248 * @param {Boolean} hidden True to hide or false to show.
38250 setHidden : function(hidden){
38251 this.hidden = hidden;
38252 this.pnode.setStyle("display", hidden ? "none" : "");
38256 * Returns true if this tab is "hidden"
38257 * @return {Boolean}
38259 isHidden : function(){
38260 return this.hidden;
38264 * Returns the text for this tab
38267 getText : function(){
38271 autoSize : function(){
38272 //this.el.beginMeasure();
38273 this.textEl.setWidth(1);
38275 * #2804 [new] Tabs in Roojs
38276 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38278 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38279 //this.el.endMeasure();
38283 * Sets the text for the tab (Note: this also sets the tooltip text)
38284 * @param {String} text The tab's text and tooltip
38286 setText : function(text){
38288 this.textEl.update(text);
38289 this.setTooltip(text);
38290 //if(!this.tabPanel.resizeTabs){
38291 // this.autoSize();
38295 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38297 activate : function(){
38298 this.tabPanel.activate(this.id);
38302 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38304 disable : function(){
38305 if(this.tabPanel.active != this){
38306 this.disabled = true;
38307 this.pnode.addClass("disabled");
38312 * Enables this TabPanelItem if it was previously disabled.
38314 enable : function(){
38315 this.disabled = false;
38316 this.pnode.removeClass("disabled");
38320 * Sets the content for this TabPanelItem.
38321 * @param {String} content The content
38322 * @param {Boolean} loadScripts true to look for and load scripts
38324 setContent : function(content, loadScripts){
38325 this.bodyEl.update(content, loadScripts);
38329 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38330 * @return {Roo.UpdateManager} The UpdateManager
38332 getUpdateManager : function(){
38333 return this.bodyEl.getUpdateManager();
38337 * Set a URL to be used to load the content for this TabPanelItem.
38338 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38339 * @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)
38340 * @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)
38341 * @return {Roo.UpdateManager} The UpdateManager
38343 setUrl : function(url, params, loadOnce){
38344 if(this.refreshDelegate){
38345 this.un('activate', this.refreshDelegate);
38347 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38348 this.on("activate", this.refreshDelegate);
38349 return this.bodyEl.getUpdateManager();
38353 _handleRefresh : function(url, params, loadOnce){
38354 if(!loadOnce || !this.loaded){
38355 var updater = this.bodyEl.getUpdateManager();
38356 updater.update(url, params, this._setLoaded.createDelegate(this));
38361 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38362 * Will fail silently if the setUrl method has not been called.
38363 * This does not activate the panel, just updates its content.
38365 refresh : function(){
38366 if(this.refreshDelegate){
38367 this.loaded = false;
38368 this.refreshDelegate();
38373 _setLoaded : function(){
38374 this.loaded = true;
38378 closeClick : function(e){
38381 this.fireEvent("beforeclose", this, o);
38382 if(o.cancel !== true){
38383 this.tabPanel.removeTab(this.id);
38387 * The text displayed in the tooltip for the close icon.
38390 closeText : "Close this tab"
38393 * This script refer to:
38394 * Title: International Telephone Input
38395 * Author: Jack O'Connor
38396 * Code version: v12.1.12
38397 * Availability: https://github.com/jackocnr/intl-tel-input.git
38400 Roo.bootstrap.PhoneInputData = function() {
38403 "Afghanistan (افغانستان)",
38408 "Albania (Shqipëri)",
38413 "Algeria (الجزائر)",
38438 "Antigua and Barbuda",
38448 "Armenia (Հայաստան)",
38464 "Austria (Österreich)",
38469 "Azerbaijan (Azərbaycan)",
38479 "Bahrain (البحرين)",
38484 "Bangladesh (বাংলাদেশ)",
38494 "Belarus (Беларусь)",
38499 "Belgium (België)",
38529 "Bosnia and Herzegovina (Босна и Херцеговина)",
38544 "British Indian Ocean Territory",
38549 "British Virgin Islands",
38559 "Bulgaria (България)",
38569 "Burundi (Uburundi)",
38574 "Cambodia (កម្ពុជា)",
38579 "Cameroon (Cameroun)",
38588 ["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"]
38591 "Cape Verde (Kabu Verdi)",
38596 "Caribbean Netherlands",
38607 "Central African Republic (République centrafricaine)",
38627 "Christmas Island",
38633 "Cocos (Keeling) Islands",
38644 "Comoros (جزر القمر)",
38649 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38654 "Congo (Republic) (Congo-Brazzaville)",
38674 "Croatia (Hrvatska)",
38695 "Czech Republic (Česká republika)",
38700 "Denmark (Danmark)",
38715 "Dominican Republic (República Dominicana)",
38719 ["809", "829", "849"]
38737 "Equatorial Guinea (Guinea Ecuatorial)",
38757 "Falkland Islands (Islas Malvinas)",
38762 "Faroe Islands (Føroyar)",
38783 "French Guiana (Guyane française)",
38788 "French Polynesia (Polynésie française)",
38803 "Georgia (საქართველო)",
38808 "Germany (Deutschland)",
38828 "Greenland (Kalaallit Nunaat)",
38865 "Guinea-Bissau (Guiné Bissau)",
38890 "Hungary (Magyarország)",
38895 "Iceland (Ísland)",
38915 "Iraq (العراق)",
38931 "Israel (ישראל)",
38958 "Jordan (الأردن)",
38963 "Kazakhstan (Казахстан)",
38984 "Kuwait (الكويت)",
38989 "Kyrgyzstan (Кыргызстан)",
38999 "Latvia (Latvija)",
39004 "Lebanon (لبنان)",
39019 "Libya (ليبيا)",
39029 "Lithuania (Lietuva)",
39044 "Macedonia (FYROM) (Македонија)",
39049 "Madagascar (Madagasikara)",
39079 "Marshall Islands",
39089 "Mauritania (موريتانيا)",
39094 "Mauritius (Moris)",
39115 "Moldova (Republica Moldova)",
39125 "Mongolia (Монгол)",
39130 "Montenegro (Crna Gora)",
39140 "Morocco (المغرب)",
39146 "Mozambique (Moçambique)",
39151 "Myanmar (Burma) (မြန်မာ)",
39156 "Namibia (Namibië)",
39171 "Netherlands (Nederland)",
39176 "New Caledonia (Nouvelle-Calédonie)",
39211 "North Korea (조선 민주주의 인민 공화국)",
39216 "Northern Mariana Islands",
39232 "Pakistan (پاکستان)",
39242 "Palestine (فلسطين)",
39252 "Papua New Guinea",
39294 "Réunion (La Réunion)",
39300 "Romania (România)",
39316 "Saint Barthélemy",
39327 "Saint Kitts and Nevis",
39337 "Saint Martin (Saint-Martin (partie française))",
39343 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39348 "Saint Vincent and the Grenadines",
39363 "São Tomé and Príncipe (São Tomé e Príncipe)",
39368 "Saudi Arabia (المملكة العربية السعودية)",
39373 "Senegal (Sénégal)",
39403 "Slovakia (Slovensko)",
39408 "Slovenia (Slovenija)",
39418 "Somalia (Soomaaliya)",
39428 "South Korea (대한민국)",
39433 "South Sudan (جنوب السودان)",
39443 "Sri Lanka (ශ්රී ලංකාව)",
39448 "Sudan (السودان)",
39458 "Svalbard and Jan Mayen",
39469 "Sweden (Sverige)",
39474 "Switzerland (Schweiz)",
39479 "Syria (سوريا)",
39524 "Trinidad and Tobago",
39529 "Tunisia (تونس)",
39534 "Turkey (Türkiye)",
39544 "Turks and Caicos Islands",
39554 "U.S. Virgin Islands",
39564 "Ukraine (Україна)",
39569 "United Arab Emirates (الإمارات العربية المتحدة)",
39591 "Uzbekistan (Oʻzbekiston)",
39601 "Vatican City (Città del Vaticano)",
39612 "Vietnam (Việt Nam)",
39617 "Wallis and Futuna (Wallis-et-Futuna)",
39622 "Western Sahara (الصحراء الغربية)",
39628 "Yemen (اليمن)",
39652 * This script refer to:
39653 * Title: International Telephone Input
39654 * Author: Jack O'Connor
39655 * Code version: v12.1.12
39656 * Availability: https://github.com/jackocnr/intl-tel-input.git
39660 * @class Roo.bootstrap.PhoneInput
39661 * @extends Roo.bootstrap.TriggerField
39662 * An input with International dial-code selection
39664 * @cfg {String} defaultDialCode default '+852'
39665 * @cfg {Array} preferedCountries default []
39668 * Create a new PhoneInput.
39669 * @param {Object} config Configuration options
39672 Roo.bootstrap.PhoneInput = function(config) {
39673 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39676 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39678 listWidth: undefined,
39680 selectedClass: 'active',
39682 invalidClass : "has-warning",
39684 validClass: 'has-success',
39686 allowed: '0123456789',
39689 * @cfg {String} defaultDialCode The default dial code when initializing the input
39691 defaultDialCode: '+852',
39694 * @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
39696 preferedCountries: false,
39698 getAutoCreate : function()
39700 var data = Roo.bootstrap.PhoneInputData();
39701 var align = this.labelAlign || this.parentLabelAlign();
39704 this.allCountries = [];
39705 this.dialCodeMapping = [];
39707 for (var i = 0; i < data.length; i++) {
39709 this.allCountries[i] = {
39713 priority: c[3] || 0,
39714 areaCodes: c[4] || null
39716 this.dialCodeMapping[c[2]] = {
39719 priority: c[3] || 0,
39720 areaCodes: c[4] || null
39732 cls : 'form-control tel-input',
39733 autocomplete: 'new-password'
39736 var hiddenInput = {
39739 cls: 'hidden-tel-input'
39743 hiddenInput.name = this.name;
39746 if (this.disabled) {
39747 input.disabled = true;
39750 var flag_container = {
39767 cls: this.hasFeedback ? 'has-feedback' : '',
39773 cls: 'dial-code-holder',
39780 cls: 'roo-select2-container input-group',
39787 if (this.fieldLabel.length) {
39790 tooltip: 'This field is required'
39796 cls: 'control-label',
39802 html: this.fieldLabel
39805 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39811 if(this.indicatorpos == 'right') {
39812 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39819 if(align == 'left') {
39827 if(this.labelWidth > 12){
39828 label.style = "width: " + this.labelWidth + 'px';
39830 if(this.labelWidth < 13 && this.labelmd == 0){
39831 this.labelmd = this.labelWidth;
39833 if(this.labellg > 0){
39834 label.cls += ' col-lg-' + this.labellg;
39835 input.cls += ' col-lg-' + (12 - this.labellg);
39837 if(this.labelmd > 0){
39838 label.cls += ' col-md-' + this.labelmd;
39839 container.cls += ' col-md-' + (12 - this.labelmd);
39841 if(this.labelsm > 0){
39842 label.cls += ' col-sm-' + this.labelsm;
39843 container.cls += ' col-sm-' + (12 - this.labelsm);
39845 if(this.labelxs > 0){
39846 label.cls += ' col-xs-' + this.labelxs;
39847 container.cls += ' col-xs-' + (12 - this.labelxs);
39857 var settings = this;
39859 ['xs','sm','md','lg'].map(function(size){
39860 if (settings[size]) {
39861 cfg.cls += ' col-' + size + '-' + settings[size];
39865 this.store = new Roo.data.Store({
39866 proxy : new Roo.data.MemoryProxy({}),
39867 reader : new Roo.data.JsonReader({
39878 'name' : 'dialCode',
39882 'name' : 'priority',
39886 'name' : 'areaCodes',
39893 if(!this.preferedCountries) {
39894 this.preferedCountries = [
39901 var p = this.preferedCountries.reverse();
39904 for (var i = 0; i < p.length; i++) {
39905 for (var j = 0; j < this.allCountries.length; j++) {
39906 if(this.allCountries[j].iso2 == p[i]) {
39907 var t = this.allCountries[j];
39908 this.allCountries.splice(j,1);
39909 this.allCountries.unshift(t);
39915 this.store.proxy.data = {
39917 data: this.allCountries
39923 initEvents : function()
39926 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39928 this.indicator = this.indicatorEl();
39929 this.flag = this.flagEl();
39930 this.dialCodeHolder = this.dialCodeHolderEl();
39932 this.trigger = this.el.select('div.flag-box',true).first();
39933 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39938 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39939 _this.list.setWidth(lw);
39942 this.list.on('mouseover', this.onViewOver, this);
39943 this.list.on('mousemove', this.onViewMove, this);
39944 this.inputEl().on("keyup", this.onKeyUp, this);
39946 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39948 this.view = new Roo.View(this.list, this.tpl, {
39949 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39952 this.view.on('click', this.onViewClick, this);
39953 this.setValue(this.defaultDialCode);
39956 onTriggerClick : function(e)
39958 Roo.log('trigger click');
39963 if(this.isExpanded()){
39965 this.hasFocus = false;
39967 this.store.load({});
39968 this.hasFocus = true;
39973 isExpanded : function()
39975 return this.list.isVisible();
39978 collapse : function()
39980 if(!this.isExpanded()){
39984 Roo.get(document).un('mousedown', this.collapseIf, this);
39985 Roo.get(document).un('mousewheel', this.collapseIf, this);
39986 this.fireEvent('collapse', this);
39990 expand : function()
39994 if(this.isExpanded() || !this.hasFocus){
39998 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39999 this.list.setWidth(lw);
40002 this.restrictHeight();
40004 Roo.get(document).on('mousedown', this.collapseIf, this);
40005 Roo.get(document).on('mousewheel', this.collapseIf, this);
40007 this.fireEvent('expand', this);
40010 restrictHeight : function()
40012 this.list.alignTo(this.inputEl(), this.listAlign);
40013 this.list.alignTo(this.inputEl(), this.listAlign);
40016 onViewOver : function(e, t)
40018 if(this.inKeyMode){
40021 var item = this.view.findItemFromChild(t);
40024 var index = this.view.indexOf(item);
40025 this.select(index, false);
40030 onViewClick : function(view, doFocus, el, e)
40032 var index = this.view.getSelectedIndexes()[0];
40034 var r = this.store.getAt(index);
40037 this.onSelect(r, index);
40039 if(doFocus !== false && !this.blockFocus){
40040 this.inputEl().focus();
40044 onViewMove : function(e, t)
40046 this.inKeyMode = false;
40049 select : function(index, scrollIntoView)
40051 this.selectedIndex = index;
40052 this.view.select(index);
40053 if(scrollIntoView !== false){
40054 var el = this.view.getNode(index);
40056 this.list.scrollChildIntoView(el, false);
40061 createList : function()
40063 this.list = Roo.get(document.body).createChild({
40065 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40066 style: 'display:none'
40069 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40072 collapseIf : function(e)
40074 var in_combo = e.within(this.el);
40075 var in_list = e.within(this.list);
40076 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40078 if (in_combo || in_list || is_list) {
40084 onSelect : function(record, index)
40086 if(this.fireEvent('beforeselect', this, record, index) !== false){
40088 this.setFlagClass(record.data.iso2);
40089 this.setDialCode(record.data.dialCode);
40090 this.hasFocus = false;
40092 this.fireEvent('select', this, record, index);
40096 flagEl : function()
40098 var flag = this.el.select('div.flag',true).first();
40105 dialCodeHolderEl : function()
40107 var d = this.el.select('input.dial-code-holder',true).first();
40114 setDialCode : function(v)
40116 this.dialCodeHolder.dom.value = '+'+v;
40119 setFlagClass : function(n)
40121 this.flag.dom.className = 'flag '+n;
40124 getValue : function()
40126 var v = this.inputEl().getValue();
40127 if(this.dialCodeHolder) {
40128 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40133 setValue : function(v)
40135 var d = this.getDialCode(v);
40137 //invalid dial code
40138 if(v.length == 0 || !d || d.length == 0) {
40140 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40141 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40147 this.setFlagClass(this.dialCodeMapping[d].iso2);
40148 this.setDialCode(d);
40149 this.inputEl().dom.value = v.replace('+'+d,'');
40150 this.hiddenEl().dom.value = this.getValue();
40155 getDialCode : function(v)
40159 if (v.length == 0) {
40160 return this.dialCodeHolder.dom.value;
40164 if (v.charAt(0) != "+") {
40167 var numericChars = "";
40168 for (var i = 1; i < v.length; i++) {
40169 var c = v.charAt(i);
40172 if (this.dialCodeMapping[numericChars]) {
40173 dialCode = v.substr(1, i);
40175 if (numericChars.length == 4) {
40185 this.setValue(this.defaultDialCode);
40189 hiddenEl : function()
40191 return this.el.select('input.hidden-tel-input',true).first();
40194 onKeyUp : function(e){
40196 var k = e.getKey();
40197 var c = e.getCharCode();
40200 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40201 this.allowed.indexOf(String.fromCharCode(c)) === -1
40206 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40209 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40213 this.setValue(this.getValue());
40218 * @class Roo.bootstrap.MoneyField
40219 * @extends Roo.bootstrap.ComboBox
40220 * Bootstrap MoneyField class
40223 * Create a new MoneyField.
40224 * @param {Object} config Configuration options
40227 Roo.bootstrap.MoneyField = function(config) {
40229 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40233 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40236 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40238 allowDecimals : true,
40240 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40242 decimalSeparator : ".",
40244 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40246 decimalPrecision : 0,
40248 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40250 allowNegative : true,
40252 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40256 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40258 minValue : Number.NEGATIVE_INFINITY,
40260 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40262 maxValue : Number.MAX_VALUE,
40264 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40266 minText : "The minimum value for this field is {0}",
40268 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40270 maxText : "The maximum value for this field is {0}",
40272 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40273 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40275 nanText : "{0} is not a valid number",
40277 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40281 * @cfg {String} defaults currency of the MoneyField
40282 * value should be in lkey
40284 defaultCurrency : false,
40286 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40288 thousandsDelimiter : false,
40298 getAutoCreate : function()
40300 var align = this.labelAlign || this.parentLabelAlign();
40312 cls : 'form-control roo-money-amount-input',
40313 autocomplete: 'new-password'
40316 var hiddenInput = {
40320 cls: 'hidden-number-input'
40324 hiddenInput.name = this.name;
40327 if (this.disabled) {
40328 input.disabled = true;
40331 var clg = 12 - this.inputlg;
40332 var cmd = 12 - this.inputmd;
40333 var csm = 12 - this.inputsm;
40334 var cxs = 12 - this.inputxs;
40338 cls : 'row roo-money-field',
40342 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40346 cls: 'roo-select2-container input-group',
40350 cls : 'form-control roo-money-currency-input',
40351 autocomplete: 'new-password',
40353 name : this.currencyName
40357 cls : 'input-group-addon',
40371 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40375 cls: this.hasFeedback ? 'has-feedback' : '',
40386 if (this.fieldLabel.length) {
40389 tooltip: 'This field is required'
40395 cls: 'control-label',
40401 html: this.fieldLabel
40404 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40410 if(this.indicatorpos == 'right') {
40411 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40418 if(align == 'left') {
40426 if(this.labelWidth > 12){
40427 label.style = "width: " + this.labelWidth + 'px';
40429 if(this.labelWidth < 13 && this.labelmd == 0){
40430 this.labelmd = this.labelWidth;
40432 if(this.labellg > 0){
40433 label.cls += ' col-lg-' + this.labellg;
40434 input.cls += ' col-lg-' + (12 - this.labellg);
40436 if(this.labelmd > 0){
40437 label.cls += ' col-md-' + this.labelmd;
40438 container.cls += ' col-md-' + (12 - this.labelmd);
40440 if(this.labelsm > 0){
40441 label.cls += ' col-sm-' + this.labelsm;
40442 container.cls += ' col-sm-' + (12 - this.labelsm);
40444 if(this.labelxs > 0){
40445 label.cls += ' col-xs-' + this.labelxs;
40446 container.cls += ' col-xs-' + (12 - this.labelxs);
40457 var settings = this;
40459 ['xs','sm','md','lg'].map(function(size){
40460 if (settings[size]) {
40461 cfg.cls += ' col-' + size + '-' + settings[size];
40468 initEvents : function()
40470 this.indicator = this.indicatorEl();
40472 this.initCurrencyEvent();
40474 this.initNumberEvent();
40477 initCurrencyEvent : function()
40480 throw "can not find store for combo";
40483 this.store = Roo.factory(this.store, Roo.data);
40484 this.store.parent = this;
40488 this.triggerEl = this.el.select('.input-group-addon', true).first();
40490 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40495 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40496 _this.list.setWidth(lw);
40499 this.list.on('mouseover', this.onViewOver, this);
40500 this.list.on('mousemove', this.onViewMove, this);
40501 this.list.on('scroll', this.onViewScroll, this);
40504 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40507 this.view = new Roo.View(this.list, this.tpl, {
40508 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40511 this.view.on('click', this.onViewClick, this);
40513 this.store.on('beforeload', this.onBeforeLoad, this);
40514 this.store.on('load', this.onLoad, this);
40515 this.store.on('loadexception', this.onLoadException, this);
40517 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40518 "up" : function(e){
40519 this.inKeyMode = true;
40523 "down" : function(e){
40524 if(!this.isExpanded()){
40525 this.onTriggerClick();
40527 this.inKeyMode = true;
40532 "enter" : function(e){
40535 if(this.fireEvent("specialkey", this, e)){
40536 this.onViewClick(false);
40542 "esc" : function(e){
40546 "tab" : function(e){
40549 if(this.fireEvent("specialkey", this, e)){
40550 this.onViewClick(false);
40558 doRelay : function(foo, bar, hname){
40559 if(hname == 'down' || this.scope.isExpanded()){
40560 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40568 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40572 initNumberEvent : function(e)
40574 this.inputEl().on("keydown" , this.fireKey, this);
40575 this.inputEl().on("focus", this.onFocus, this);
40576 this.inputEl().on("blur", this.onBlur, this);
40578 this.inputEl().relayEvent('keyup', this);
40580 if(this.indicator){
40581 this.indicator.addClass('invisible');
40584 this.originalValue = this.getValue();
40586 if(this.validationEvent == 'keyup'){
40587 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40588 this.inputEl().on('keyup', this.filterValidation, this);
40590 else if(this.validationEvent !== false){
40591 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40594 if(this.selectOnFocus){
40595 this.on("focus", this.preFocus, this);
40598 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40599 this.inputEl().on("keypress", this.filterKeys, this);
40601 this.inputEl().relayEvent('keypress', this);
40604 var allowed = "0123456789";
40606 if(this.allowDecimals){
40607 allowed += this.decimalSeparator;
40610 if(this.allowNegative){
40614 if(this.thousandsDelimiter) {
40618 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40620 var keyPress = function(e){
40622 var k = e.getKey();
40624 var c = e.getCharCode();
40627 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40628 allowed.indexOf(String.fromCharCode(c)) === -1
40634 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40638 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40643 this.inputEl().on("keypress", keyPress, this);
40647 onTriggerClick : function(e)
40654 this.loadNext = false;
40656 if(this.isExpanded()){
40661 this.hasFocus = true;
40663 if(this.triggerAction == 'all') {
40664 this.doQuery(this.allQuery, true);
40668 this.doQuery(this.getRawValue());
40671 getCurrency : function()
40673 var v = this.currencyEl().getValue();
40678 restrictHeight : function()
40680 this.list.alignTo(this.currencyEl(), this.listAlign);
40681 this.list.alignTo(this.currencyEl(), this.listAlign);
40684 onViewClick : function(view, doFocus, el, e)
40686 var index = this.view.getSelectedIndexes()[0];
40688 var r = this.store.getAt(index);
40691 this.onSelect(r, index);
40695 onSelect : function(record, index){
40697 if(this.fireEvent('beforeselect', this, record, index) !== false){
40699 this.setFromCurrencyData(index > -1 ? record.data : false);
40703 this.fireEvent('select', this, record, index);
40707 setFromCurrencyData : function(o)
40711 this.lastCurrency = o;
40713 if (this.currencyField) {
40714 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40716 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40719 this.lastSelectionText = currency;
40721 //setting default currency
40722 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40723 this.setCurrency(this.defaultCurrency);
40727 this.setCurrency(currency);
40730 setFromData : function(o)
40734 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40736 this.setFromCurrencyData(c);
40741 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40743 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40746 this.setValue(value);
40750 setCurrency : function(v)
40752 this.currencyValue = v;
40755 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40760 setValue : function(v)
40762 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40768 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40770 this.inputEl().dom.value = (v == '') ? '' :
40771 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40773 if(!this.allowZero && v === '0') {
40774 this.hiddenEl().dom.value = '';
40775 this.inputEl().dom.value = '';
40782 getRawValue : function()
40784 var v = this.inputEl().getValue();
40789 getValue : function()
40791 return this.fixPrecision(this.parseValue(this.getRawValue()));
40794 parseValue : function(value)
40796 if(this.thousandsDelimiter) {
40798 r = new RegExp(",", "g");
40799 value = value.replace(r, "");
40802 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40803 return isNaN(value) ? '' : value;
40807 fixPrecision : function(value)
40809 if(this.thousandsDelimiter) {
40811 r = new RegExp(",", "g");
40812 value = value.replace(r, "");
40815 var nan = isNaN(value);
40817 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40818 return nan ? '' : value;
40820 return parseFloat(value).toFixed(this.decimalPrecision);
40823 decimalPrecisionFcn : function(v)
40825 return Math.floor(v);
40828 validateValue : function(value)
40830 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40834 var num = this.parseValue(value);
40837 this.markInvalid(String.format(this.nanText, value));
40841 if(num < this.minValue){
40842 this.markInvalid(String.format(this.minText, this.minValue));
40846 if(num > this.maxValue){
40847 this.markInvalid(String.format(this.maxText, this.maxValue));
40854 validate : function()
40856 if(this.disabled || this.allowBlank){
40861 var currency = this.getCurrency();
40863 if(this.validateValue(this.getRawValue()) && currency.length){
40868 this.markInvalid();
40872 getName: function()
40877 beforeBlur : function()
40883 var v = this.parseValue(this.getRawValue());
40890 onBlur : function()
40894 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40895 //this.el.removeClass(this.focusClass);
40898 this.hasFocus = false;
40900 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40904 var v = this.getValue();
40906 if(String(v) !== String(this.startValue)){
40907 this.fireEvent('change', this, v, this.startValue);
40910 this.fireEvent("blur", this);
40913 inputEl : function()
40915 return this.el.select('.roo-money-amount-input', true).first();
40918 currencyEl : function()
40920 return this.el.select('.roo-money-currency-input', true).first();
40923 hiddenEl : function()
40925 return this.el.select('input.hidden-number-input',true).first();