4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
407 * Hide a component - adds 'hidden' class
411 if(!this.getVisibilityEl()){
415 this.getVisibilityEl().addClass('hidden');
428 * @class Roo.bootstrap.Body
429 * @extends Roo.bootstrap.Component
430 * Bootstrap Body class
434 * @param {Object} config The config object
437 Roo.bootstrap.Body = function(config){
439 config = config || {};
441 Roo.bootstrap.Body.superclass.constructor.call(this, config);
442 this.el = Roo.get(config.el ? config.el : document.body );
443 if (this.cls && this.cls.length) {
444 Roo.get(document.body).addClass(this.cls);
448 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
450 is_body : true,// just to make sure it's constructed?
455 onRender : function(ct, position)
457 /* Roo.log("Roo.bootstrap.Body - onRender");
458 if (this.cls && this.cls.length) {
459 Roo.get(document.body).addClass(this.cls);
478 * @class Roo.bootstrap.ButtonGroup
479 * @extends Roo.bootstrap.Component
480 * Bootstrap ButtonGroup class
481 * @cfg {String} size lg | sm | xs (default empty normal)
482 * @cfg {String} align vertical | justified (default none)
483 * @cfg {String} direction up | down (default down)
484 * @cfg {Boolean} toolbar false | true
485 * @cfg {Boolean} btn true | false
490 * @param {Object} config The config object
493 Roo.bootstrap.ButtonGroup = function(config){
494 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
497 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
505 getAutoCreate : function(){
511 cfg.html = this.html || cfg.html;
522 if (['vertical','justified'].indexOf(this.align)!==-1) {
523 cfg.cls = 'btn-group-' + this.align;
525 if (this.align == 'justified') {
526 console.log(this.items);
530 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
531 cfg.cls += ' btn-group-' + this.size;
534 if (this.direction == 'up') {
535 cfg.cls += ' dropup' ;
551 * @class Roo.bootstrap.Button
552 * @extends Roo.bootstrap.Component
553 * Bootstrap Button class
554 * @cfg {String} html The button content
555 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
556 * @cfg {String} size ( lg | sm | xs)
557 * @cfg {String} tag ( a | input | submit)
558 * @cfg {String} href empty or href
559 * @cfg {Boolean} disabled default false;
560 * @cfg {Boolean} isClose default false;
561 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
562 * @cfg {String} badge text for badge
563 * @cfg {String} theme (default|glow)
564 * @cfg {Boolean} inverse dark themed version
565 * @cfg {Boolean} toggle is it a slidy toggle button
566 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
567 * @cfg {String} ontext text for on slidy toggle state
568 * @cfg {String} offtext text for off slidy toggle state
569 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
570 * @cfg {Boolean} removeClass remove the standard class..
571 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
574 * Create a new button
575 * @param {Object} config The config object
579 Roo.bootstrap.Button = function(config){
580 Roo.bootstrap.Button.superclass.constructor.call(this, config);
581 this.weightClass = ["btn-default",
593 * When a butotn is pressed
594 * @param {Roo.bootstrap.Button} btn
595 * @param {Roo.EventObject} e
600 * After the button has been toggles
601 * @param {Roo.bootstrap.Button} btn
602 * @param {Roo.EventObject} e
603 * @param {boolean} pressed (also available as button.pressed)
609 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
627 preventDefault: true,
635 getAutoCreate : function(){
643 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
644 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
651 if (this.toggle == true) {
654 cls: 'slider-frame roo-button',
659 'data-off-text':'OFF',
660 cls: 'slider-button',
666 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667 cfg.cls += ' '+this.weight;
676 cfg["aria-hidden"] = true;
678 cfg.html = "×";
684 if (this.theme==='default') {
685 cfg.cls = 'btn roo-button';
687 //if (this.parentType != 'Navbar') {
688 this.weight = this.weight.length ? this.weight : 'default';
690 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
692 cfg.cls += ' btn-' + this.weight;
694 } else if (this.theme==='glow') {
697 cfg.cls = 'btn-glow roo-button';
699 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701 cfg.cls += ' ' + this.weight;
707 this.cls += ' inverse';
711 if (this.active || this.pressed === true) {
712 cfg.cls += ' active';
716 cfg.disabled = 'disabled';
720 Roo.log('changing to ul' );
722 this.glyphicon = 'caret';
725 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
727 //gsRoo.log(this.parentType);
728 if (this.parentType === 'Navbar' && !this.parent().bar) {
729 Roo.log('changing to li?');
738 href : this.href || '#'
741 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
742 cfg.cls += ' dropdown';
749 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
751 if (this.glyphicon) {
752 cfg.html = ' ' + cfg.html;
757 cls: 'glyphicon glyphicon-' + this.glyphicon
767 // cfg.cls='btn roo-button';
771 var value = cfg.html;
776 cls: 'glyphicon glyphicon-' + this.glyphicon,
795 cfg.cls += ' dropdown';
796 cfg.html = typeof(cfg.html) != 'undefined' ?
797 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
800 if (cfg.tag !== 'a' && this.href !== '') {
801 throw "Tag must be a to set href.";
802 } else if (this.href.length > 0) {
803 cfg.href = this.href;
806 if(this.removeClass){
811 cfg.target = this.target;
816 initEvents: function() {
817 // Roo.log('init events?');
818 // Roo.log(this.el.dom);
821 if (typeof (this.menu) != 'undefined') {
822 this.menu.parentType = this.xtype;
823 this.menu.triggerEl = this.el;
824 this.addxtype(Roo.apply({}, this.menu));
828 if (this.el.hasClass('roo-button')) {
829 this.el.on('click', this.onClick, this);
831 this.el.select('.roo-button').on('click', this.onClick, this);
834 if(this.removeClass){
835 this.el.on('click', this.onClick, this);
838 this.el.enableDisplayMode();
841 onClick : function(e)
847 Roo.log('button on click ');
848 if(this.preventDefault){
852 if (this.pressed === true || this.pressed === false) {
853 this.toggleActive(e);
857 this.fireEvent('click', this, e);
861 * Enables this button
865 this.disabled = false;
866 this.el.removeClass('disabled');
870 * Disable this button
874 this.disabled = true;
875 this.el.addClass('disabled');
878 * sets the active state on/off,
879 * @param {Boolean} state (optional) Force a particular state
881 setActive : function(v) {
883 this.el[v ? 'addClass' : 'removeClass']('active');
887 * toggles the current active state
889 toggleActive : function(e)
891 this.setActive(!this.pressed);
892 this.fireEvent('toggle', this, e, !this.pressed);
895 * get the current active state
896 * @return {boolean} true if it's active
898 isActive : function()
900 return this.el.hasClass('active');
903 * set the text of the first selected button
905 setText : function(str)
907 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
910 * get the text of the first selected button
914 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
925 setWeight : function(str)
927 this.el.removeClass(this.weightClass);
928 this.el.addClass('btn-' + str);
942 * @class Roo.bootstrap.Column
943 * @extends Roo.bootstrap.Component
944 * Bootstrap Column class
945 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
946 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
947 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
948 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
949 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
955 * @cfg {Boolean} hidden (true|false) hide the element
956 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
957 * @cfg {String} fa (ban|check|...) font awesome icon
958 * @cfg {Number} fasize (1|2|....) font awsome size
960 * @cfg {String} icon (info-sign|check|...) glyphicon name
962 * @cfg {String} html content of column.
965 * Create a new Column
966 * @param {Object} config The config object
969 Roo.bootstrap.Column = function(config){
970 Roo.bootstrap.Column.superclass.constructor.call(this, config);
973 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
991 getAutoCreate : function(){
992 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1000 ['xs','sm','md','lg'].map(function(size){
1001 //Roo.log( size + ':' + settings[size]);
1003 if (settings[size+'off'] !== false) {
1004 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1007 if (settings[size] === false) {
1011 if (!settings[size]) { // 0 = hidden
1012 cfg.cls += ' hidden-' + size;
1015 cfg.cls += ' col-' + size + '-' + settings[size];
1020 cfg.cls += ' hidden';
1023 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1024 cfg.cls +=' alert alert-' + this.alert;
1028 if (this.html.length) {
1029 cfg.html = this.html;
1033 if (this.fasize > 1) {
1034 fasize = ' fa-' + this.fasize + 'x';
1036 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1041 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1060 * @class Roo.bootstrap.Container
1061 * @extends Roo.bootstrap.Component
1062 * Bootstrap Container class
1063 * @cfg {Boolean} jumbotron is it a jumbotron element
1064 * @cfg {String} html content of element
1065 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1066 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1067 * @cfg {String} header content of header (for panel)
1068 * @cfg {String} footer content of footer (for panel)
1069 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1070 * @cfg {String} tag (header|aside|section) type of HTML tag.
1071 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1072 * @cfg {String} fa font awesome icon
1073 * @cfg {String} icon (info-sign|check|...) glyphicon name
1074 * @cfg {Boolean} hidden (true|false) hide the element
1075 * @cfg {Boolean} expandable (true|false) default false
1076 * @cfg {Boolean} expanded (true|false) default true
1077 * @cfg {String} rheader contet on the right of header
1078 * @cfg {Boolean} clickable (true|false) default false
1082 * Create a new Container
1083 * @param {Object} config The config object
1086 Roo.bootstrap.Container = function(config){
1087 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1093 * After the panel has been expand
1095 * @param {Roo.bootstrap.Container} this
1100 * After the panel has been collapsed
1102 * @param {Roo.bootstrap.Container} this
1107 * When a element is chick
1108 * @param {Roo.bootstrap.Container} this
1109 * @param {Roo.EventObject} e
1115 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1133 getChildContainer : function() {
1139 if (this.panel.length) {
1140 return this.el.select('.panel-body',true).first();
1147 getAutoCreate : function(){
1150 tag : this.tag || 'div',
1154 if (this.jumbotron) {
1155 cfg.cls = 'jumbotron';
1160 // - this is applied by the parent..
1162 // cfg.cls = this.cls + '';
1165 if (this.sticky.length) {
1167 var bd = Roo.get(document.body);
1168 if (!bd.hasClass('bootstrap-sticky')) {
1169 bd.addClass('bootstrap-sticky');
1170 Roo.select('html',true).setStyle('height', '100%');
1173 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1177 if (this.well.length) {
1178 switch (this.well) {
1181 cfg.cls +=' well well-' +this.well;
1190 cfg.cls += ' hidden';
1194 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1195 cfg.cls +=' alert alert-' + this.alert;
1200 if (this.panel.length) {
1201 cfg.cls += ' panel panel-' + this.panel;
1203 if (this.header.length) {
1207 if(this.expandable){
1209 cfg.cls = cfg.cls + ' expandable';
1213 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1221 cls : 'panel-title',
1222 html : (this.expandable ? ' ' : '') + this.header
1226 cls: 'panel-header-right',
1232 cls : 'panel-heading',
1233 style : this.expandable ? 'cursor: pointer' : '',
1241 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1246 if (this.footer.length) {
1248 cls : 'panel-footer',
1257 body.html = this.html || cfg.html;
1258 // prefix with the icons..
1260 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1263 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1268 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1269 cfg.cls = 'container';
1275 initEvents: function()
1277 if(this.expandable){
1278 var headerEl = this.headerEl();
1281 headerEl.on('click', this.onToggleClick, this);
1286 this.el.on('click', this.onClick, this);
1291 onToggleClick : function()
1293 var headerEl = this.headerEl();
1309 if(this.fireEvent('expand', this)) {
1311 this.expanded = true;
1313 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1315 this.el.select('.panel-body',true).first().removeClass('hide');
1317 var toggleEl = this.toggleEl();
1323 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1328 collapse : function()
1330 if(this.fireEvent('collapse', this)) {
1332 this.expanded = false;
1334 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1335 this.el.select('.panel-body',true).first().addClass('hide');
1337 var toggleEl = this.toggleEl();
1343 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1347 toggleEl : function()
1349 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1353 return this.el.select('.panel-heading .fa',true).first();
1356 headerEl : function()
1358 if(!this.el || !this.panel.length || !this.header.length){
1362 return this.el.select('.panel-heading',true).first()
1367 if(!this.el || !this.panel.length){
1371 return this.el.select('.panel-body',true).first()
1374 titleEl : function()
1376 if(!this.el || !this.panel.length || !this.header.length){
1380 return this.el.select('.panel-title',true).first();
1383 setTitle : function(v)
1385 var titleEl = this.titleEl();
1391 titleEl.dom.innerHTML = v;
1394 getTitle : function()
1397 var titleEl = this.titleEl();
1403 return titleEl.dom.innerHTML;
1406 setRightTitle : function(v)
1408 var t = this.el.select('.panel-header-right',true).first();
1414 t.dom.innerHTML = v;
1417 onClick : function(e)
1421 this.fireEvent('click', this, e);
1434 * @class Roo.bootstrap.Img
1435 * @extends Roo.bootstrap.Component
1436 * Bootstrap Img class
1437 * @cfg {Boolean} imgResponsive false | true
1438 * @cfg {String} border rounded | circle | thumbnail
1439 * @cfg {String} src image source
1440 * @cfg {String} alt image alternative text
1441 * @cfg {String} href a tag href
1442 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1443 * @cfg {String} xsUrl xs image source
1444 * @cfg {String} smUrl sm image source
1445 * @cfg {String} mdUrl md image source
1446 * @cfg {String} lgUrl lg image source
1449 * Create a new Input
1450 * @param {Object} config The config object
1453 Roo.bootstrap.Img = function(config){
1454 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1460 * The img click event for the img.
1461 * @param {Roo.EventObject} e
1467 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1469 imgResponsive: true,
1479 getAutoCreate : function()
1481 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1482 return this.createSingleImg();
1487 cls: 'roo-image-responsive-group',
1492 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1494 if(!_this[size + 'Url']){
1500 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1501 html: _this.html || cfg.html,
1502 src: _this[size + 'Url']
1505 img.cls += ' roo-image-responsive-' + size;
1507 var s = ['xs', 'sm', 'md', 'lg'];
1509 s.splice(s.indexOf(size), 1);
1511 Roo.each(s, function(ss){
1512 img.cls += ' hidden-' + ss;
1515 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1516 cfg.cls += ' img-' + _this.border;
1520 cfg.alt = _this.alt;
1533 a.target = _this.target;
1537 cfg.cn.push((_this.href) ? a : img);
1544 createSingleImg : function()
1548 cls: (this.imgResponsive) ? 'img-responsive' : '',
1550 src : 'about:blank' // just incase src get's set to undefined?!?
1553 cfg.html = this.html || cfg.html;
1555 cfg.src = this.src || cfg.src;
1557 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1558 cfg.cls += ' img-' + this.border;
1575 a.target = this.target;
1580 return (this.href) ? a : cfg;
1583 initEvents: function()
1586 this.el.on('click', this.onClick, this);
1591 onClick : function(e)
1593 Roo.log('img onclick');
1594 this.fireEvent('click', this, e);
1597 * Sets the url of the image - used to update it
1598 * @param {String} url the url of the image
1601 setSrc : function(url)
1605 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1606 this.el.dom.src = url;
1610 this.el.select('img', true).first().dom.src = url;
1626 * @class Roo.bootstrap.Link
1627 * @extends Roo.bootstrap.Component
1628 * Bootstrap Link Class
1629 * @cfg {String} alt image alternative text
1630 * @cfg {String} href a tag href
1631 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1632 * @cfg {String} html the content of the link.
1633 * @cfg {String} anchor name for the anchor link
1634 * @cfg {String} fa - favicon
1636 * @cfg {Boolean} preventDefault (true | false) default false
1640 * Create a new Input
1641 * @param {Object} config The config object
1644 Roo.bootstrap.Link = function(config){
1645 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1651 * The img click event for the img.
1652 * @param {Roo.EventObject} e
1658 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1662 preventDefault: false,
1668 getAutoCreate : function()
1670 var html = this.html || '';
1672 if (this.fa !== false) {
1673 html = '<i class="fa fa-' + this.fa + '"></i>';
1678 // anchor's do not require html/href...
1679 if (this.anchor === false) {
1681 cfg.href = this.href || '#';
1683 cfg.name = this.anchor;
1684 if (this.html !== false || this.fa !== false) {
1687 if (this.href !== false) {
1688 cfg.href = this.href;
1692 if(this.alt !== false){
1697 if(this.target !== false) {
1698 cfg.target = this.target;
1704 initEvents: function() {
1706 if(!this.href || this.preventDefault){
1707 this.el.on('click', this.onClick, this);
1711 onClick : function(e)
1713 if(this.preventDefault){
1716 //Roo.log('img onclick');
1717 this.fireEvent('click', this, e);
1730 * @class Roo.bootstrap.Header
1731 * @extends Roo.bootstrap.Component
1732 * Bootstrap Header class
1733 * @cfg {String} html content of header
1734 * @cfg {Number} level (1|2|3|4|5|6) default 1
1737 * Create a new Header
1738 * @param {Object} config The config object
1742 Roo.bootstrap.Header = function(config){
1743 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1746 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1754 getAutoCreate : function(){
1759 tag: 'h' + (1 *this.level),
1760 html: this.html || ''
1772 * Ext JS Library 1.1.1
1773 * Copyright(c) 2006-2007, Ext JS, LLC.
1775 * Originally Released Under LGPL - original licence link has changed is not relivant.
1778 * <script type="text/javascript">
1782 * @class Roo.bootstrap.MenuMgr
1783 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1786 Roo.bootstrap.MenuMgr = function(){
1787 var menus, active, groups = {}, attached = false, lastShow = new Date();
1789 // private - called when first menu is created
1792 active = new Roo.util.MixedCollection();
1793 Roo.get(document).addKeyListener(27, function(){
1794 if(active.length > 0){
1802 if(active && active.length > 0){
1803 var c = active.clone();
1813 if(active.length < 1){
1814 Roo.get(document).un("mouseup", onMouseDown);
1822 var last = active.last();
1823 lastShow = new Date();
1826 Roo.get(document).on("mouseup", onMouseDown);
1831 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1832 m.parentMenu.activeChild = m;
1833 }else if(last && last.isVisible()){
1834 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1839 function onBeforeHide(m){
1841 m.activeChild.hide();
1843 if(m.autoHideTimer){
1844 clearTimeout(m.autoHideTimer);
1845 delete m.autoHideTimer;
1850 function onBeforeShow(m){
1851 var pm = m.parentMenu;
1852 if(!pm && !m.allowOtherMenus){
1854 }else if(pm && pm.activeChild && active != m){
1855 pm.activeChild.hide();
1859 // private this should really trigger on mouseup..
1860 function onMouseDown(e){
1861 Roo.log("on Mouse Up");
1863 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1864 Roo.log("MenuManager hideAll");
1873 function onBeforeCheck(mi, state){
1875 var g = groups[mi.group];
1876 for(var i = 0, l = g.length; i < l; i++){
1878 g[i].setChecked(false);
1887 * Hides all menus that are currently visible
1889 hideAll : function(){
1894 register : function(menu){
1898 menus[menu.id] = menu;
1899 menu.on("beforehide", onBeforeHide);
1900 menu.on("hide", onHide);
1901 menu.on("beforeshow", onBeforeShow);
1902 menu.on("show", onShow);
1904 if(g && menu.events["checkchange"]){
1908 groups[g].push(menu);
1909 menu.on("checkchange", onCheck);
1914 * Returns a {@link Roo.menu.Menu} object
1915 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1916 * be used to generate and return a new Menu instance.
1918 get : function(menu){
1919 if(typeof menu == "string"){ // menu id
1921 }else if(menu.events){ // menu instance
1924 /*else if(typeof menu.length == 'number'){ // array of menu items?
1925 return new Roo.bootstrap.Menu({items:menu});
1926 }else{ // otherwise, must be a config
1927 return new Roo.bootstrap.Menu(menu);
1934 unregister : function(menu){
1935 delete menus[menu.id];
1936 menu.un("beforehide", onBeforeHide);
1937 menu.un("hide", onHide);
1938 menu.un("beforeshow", onBeforeShow);
1939 menu.un("show", onShow);
1941 if(g && menu.events["checkchange"]){
1942 groups[g].remove(menu);
1943 menu.un("checkchange", onCheck);
1948 registerCheckable : function(menuItem){
1949 var g = menuItem.group;
1954 groups[g].push(menuItem);
1955 menuItem.on("beforecheckchange", onBeforeCheck);
1960 unregisterCheckable : function(menuItem){
1961 var g = menuItem.group;
1963 groups[g].remove(menuItem);
1964 menuItem.un("beforecheckchange", onBeforeCheck);
1976 * @class Roo.bootstrap.Menu
1977 * @extends Roo.bootstrap.Component
1978 * Bootstrap Menu class - container for MenuItems
1979 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1980 * @cfg {bool} hidden if the menu should be hidden when rendered.
1981 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1982 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1986 * @param {Object} config The config object
1990 Roo.bootstrap.Menu = function(config){
1991 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1992 if (this.registerMenu && this.type != 'treeview') {
1993 Roo.bootstrap.MenuMgr.register(this);
1998 * Fires before this menu is displayed
1999 * @param {Roo.menu.Menu} this
2004 * Fires before this menu is hidden
2005 * @param {Roo.menu.Menu} this
2010 * Fires after this menu is displayed
2011 * @param {Roo.menu.Menu} this
2016 * Fires after this menu is hidden
2017 * @param {Roo.menu.Menu} this
2022 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2023 * @param {Roo.menu.Menu} this
2024 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2025 * @param {Roo.EventObject} e
2030 * Fires when the mouse is hovering over this menu
2031 * @param {Roo.menu.Menu} this
2032 * @param {Roo.EventObject} e
2033 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038 * Fires when the mouse exits this menu
2039 * @param {Roo.menu.Menu} this
2040 * @param {Roo.EventObject} e
2041 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2046 * Fires when a menu item contained in this menu is clicked
2047 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2048 * @param {Roo.EventObject} e
2052 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2055 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2059 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2062 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2064 registerMenu : true,
2066 menuItems :false, // stores the menu items..
2076 getChildContainer : function() {
2080 getAutoCreate : function(){
2082 //if (['right'].indexOf(this.align)!==-1) {
2083 // cfg.cn[1].cls += ' pull-right'
2089 cls : 'dropdown-menu' ,
2090 style : 'z-index:1000'
2094 if (this.type === 'submenu') {
2095 cfg.cls = 'submenu active';
2097 if (this.type === 'treeview') {
2098 cfg.cls = 'treeview-menu';
2103 initEvents : function() {
2105 // Roo.log("ADD event");
2106 // Roo.log(this.triggerEl.dom);
2108 this.triggerEl.on('click', this.onTriggerClick, this);
2110 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2112 this.triggerEl.addClass('dropdown-toggle');
2115 this.el.on('touchstart' , this.onTouch, this);
2117 this.el.on('click' , this.onClick, this);
2119 this.el.on("mouseover", this.onMouseOver, this);
2120 this.el.on("mouseout", this.onMouseOut, this);
2124 findTargetItem : function(e)
2126 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2130 //Roo.log(t); Roo.log(t.id);
2132 //Roo.log(this.menuitems);
2133 return this.menuitems.get(t.id);
2135 //return this.items.get(t.menuItemId);
2141 onTouch : function(e)
2143 Roo.log("menu.onTouch");
2144 //e.stopEvent(); this make the user popdown broken
2148 onClick : function(e)
2150 Roo.log("menu.onClick");
2152 var t = this.findTargetItem(e);
2153 if(!t || t.isContainer){
2158 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2159 if(t == this.activeItem && t.shouldDeactivate(e)){
2160 this.activeItem.deactivate();
2161 delete this.activeItem;
2165 this.setActiveItem(t, true);
2173 Roo.log('pass click event');
2177 this.fireEvent("click", this, t, e);
2181 if(!t.href.length || t.href == '#'){
2182 (function() { _this.hide(); }).defer(100);
2187 onMouseOver : function(e){
2188 var t = this.findTargetItem(e);
2191 // if(t.canActivate && !t.disabled){
2192 // this.setActiveItem(t, true);
2196 this.fireEvent("mouseover", this, e, t);
2198 isVisible : function(){
2199 return !this.hidden;
2201 onMouseOut : function(e){
2202 var t = this.findTargetItem(e);
2205 // if(t == this.activeItem && t.shouldDeactivate(e)){
2206 // this.activeItem.deactivate();
2207 // delete this.activeItem;
2210 this.fireEvent("mouseout", this, e, t);
2215 * Displays this menu relative to another element
2216 * @param {String/HTMLElement/Roo.Element} element The element to align to
2217 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2218 * the element (defaults to this.defaultAlign)
2219 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2221 show : function(el, pos, parentMenu){
2222 this.parentMenu = parentMenu;
2226 this.fireEvent("beforeshow", this);
2227 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2230 * Displays this menu at a specific xy position
2231 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2232 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2234 showAt : function(xy, parentMenu, /* private: */_e){
2235 this.parentMenu = parentMenu;
2240 this.fireEvent("beforeshow", this);
2241 //xy = this.el.adjustForConstraints(xy);
2245 this.hideMenuItems();
2246 this.hidden = false;
2247 this.triggerEl.addClass('open');
2249 // reassign x when hitting right
2250 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2251 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2254 // reassign y when hitting bottom
2255 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2256 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2259 // but the list may align on trigger left or trigger top... should it be a properity?
2261 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2266 this.fireEvent("show", this);
2272 this.doFocus.defer(50, this);
2276 doFocus : function(){
2278 this.focusEl.focus();
2283 * Hides this menu and optionally all parent menus
2284 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2286 hide : function(deep)
2289 this.hideMenuItems();
2290 if(this.el && this.isVisible()){
2291 this.fireEvent("beforehide", this);
2292 if(this.activeItem){
2293 this.activeItem.deactivate();
2294 this.activeItem = null;
2296 this.triggerEl.removeClass('open');;
2298 this.fireEvent("hide", this);
2300 if(deep === true && this.parentMenu){
2301 this.parentMenu.hide(true);
2305 onTriggerClick : function(e)
2307 Roo.log('trigger click');
2309 var target = e.getTarget();
2311 Roo.log(target.nodeName.toLowerCase());
2313 if(target.nodeName.toLowerCase() === 'i'){
2319 onTriggerPress : function(e)
2321 Roo.log('trigger press');
2322 //Roo.log(e.getTarget());
2323 // Roo.log(this.triggerEl.dom);
2325 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2326 var pel = Roo.get(e.getTarget());
2327 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2328 Roo.log('is treeview or dropdown?');
2332 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2336 if (this.isVisible()) {
2341 this.show(this.triggerEl, false, false);
2344 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2351 hideMenuItems : function()
2353 Roo.log("hide Menu Items");
2357 //$(backdrop).remove()
2358 this.el.select('.open',true).each(function(aa) {
2360 aa.removeClass('open');
2361 //var parent = getParent($(this))
2362 //var relatedTarget = { relatedTarget: this }
2364 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2365 //if (e.isDefaultPrevented()) return
2366 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2369 addxtypeChild : function (tree, cntr) {
2370 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2372 this.menuitems.add(comp);
2384 this.getEl().dom.innerHTML = '';
2385 this.menuitems.clear();
2399 * @class Roo.bootstrap.MenuItem
2400 * @extends Roo.bootstrap.Component
2401 * Bootstrap MenuItem class
2402 * @cfg {String} html the menu label
2403 * @cfg {String} href the link
2404 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2405 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2406 * @cfg {Boolean} active used on sidebars to highlight active itesm
2407 * @cfg {String} fa favicon to show on left of menu item.
2408 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2412 * Create a new MenuItem
2413 * @param {Object} config The config object
2417 Roo.bootstrap.MenuItem = function(config){
2418 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2423 * The raw click event for the entire grid.
2424 * @param {Roo.bootstrap.MenuItem} this
2425 * @param {Roo.EventObject} e
2431 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2435 preventDefault: false,
2436 isContainer : false,
2440 getAutoCreate : function(){
2442 if(this.isContainer){
2445 cls: 'dropdown-menu-item'
2459 if (this.fa !== false) {
2462 cls : 'fa fa-' + this.fa
2471 cls: 'dropdown-menu-item',
2474 if (this.parent().type == 'treeview') {
2475 cfg.cls = 'treeview-menu';
2478 cfg.cls += ' active';
2483 anc.href = this.href || cfg.cn[0].href ;
2484 ctag.html = this.html || cfg.cn[0].html ;
2488 initEvents: function()
2490 if (this.parent().type == 'treeview') {
2491 this.el.select('a').on('click', this.onClick, this);
2495 this.menu.parentType = this.xtype;
2496 this.menu.triggerEl = this.el;
2497 this.menu = this.addxtype(Roo.apply({}, this.menu));
2501 onClick : function(e)
2503 Roo.log('item on click ');
2505 if(this.preventDefault){
2508 //this.parent().hideMenuItems();
2510 this.fireEvent('click', this, e);
2529 * @class Roo.bootstrap.MenuSeparator
2530 * @extends Roo.bootstrap.Component
2531 * Bootstrap MenuSeparator class
2534 * Create a new MenuItem
2535 * @param {Object} config The config object
2539 Roo.bootstrap.MenuSeparator = function(config){
2540 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2543 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2545 getAutoCreate : function(){
2564 * @class Roo.bootstrap.Modal
2565 * @extends Roo.bootstrap.Component
2566 * Bootstrap Modal class
2567 * @cfg {String} title Title of dialog
2568 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2569 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2570 * @cfg {Boolean} specificTitle default false
2571 * @cfg {Array} buttons Array of buttons or standard button set..
2572 * @cfg {String} buttonPosition (left|right|center) default right
2573 * @cfg {Boolean} animate default true
2574 * @cfg {Boolean} allow_close default true
2575 * @cfg {Boolean} fitwindow default false
2576 * @cfg {String} size (sm|lg) default empty
2580 * Create a new Modal Dialog
2581 * @param {Object} config The config object
2584 Roo.bootstrap.Modal = function(config){
2585 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2590 * The raw btnclick event for the button
2591 * @param {Roo.EventObject} e
2596 * Fire when dialog resize
2597 * @param {Roo.bootstrap.Modal} this
2598 * @param {Roo.EventObject} e
2602 this.buttons = this.buttons || [];
2605 this.tmpl = Roo.factory(this.tmpl);
2610 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2612 title : 'test dialog',
2622 specificTitle: false,
2624 buttonPosition: 'right',
2643 onRender : function(ct, position)
2645 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2648 var cfg = Roo.apply({}, this.getAutoCreate());
2651 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653 //if (!cfg.name.length) {
2657 cfg.cls += ' ' + this.cls;
2660 cfg.style = this.style;
2662 this.el = Roo.get(document.body).createChild(cfg, position);
2664 //var type = this.el.dom.type;
2667 if(this.tabIndex !== undefined){
2668 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2671 this.dialogEl = this.el.select('.modal-dialog',true).first();
2672 this.bodyEl = this.el.select('.modal-body',true).first();
2673 this.closeEl = this.el.select('.modal-header .close', true).first();
2674 this.headerEl = this.el.select('.modal-header',true).first();
2675 this.titleEl = this.el.select('.modal-title',true).first();
2676 this.footerEl = this.el.select('.modal-footer',true).first();
2678 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680 //this.el.addClass("x-dlg-modal");
2682 if (this.buttons.length) {
2683 Roo.each(this.buttons, function(bb) {
2684 var b = Roo.apply({}, bb);
2685 b.xns = b.xns || Roo.bootstrap;
2686 b.xtype = b.xtype || 'Button';
2687 if (typeof(b.listeners) == 'undefined') {
2688 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2691 var btn = Roo.factory(b);
2693 btn.render(this.el.select('.modal-footer div').first());
2697 // render the children.
2700 if(typeof(this.items) != 'undefined'){
2701 var items = this.items;
2704 for(var i =0;i < items.length;i++) {
2705 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2709 this.items = nitems;
2711 // where are these used - they used to be body/close/footer
2715 //this.el.addClass([this.fieldClass, this.cls]);
2719 getAutoCreate : function(){
2724 html : this.html || ''
2729 cls : 'modal-title',
2733 if(this.specificTitle){
2739 if (this.allow_close) {
2751 if(this.size.length){
2752 size = 'modal-' + this.size;
2759 cls: "modal-dialog " + size,
2762 cls : "modal-content",
2765 cls : 'modal-header',
2770 cls : 'modal-footer',
2774 cls: 'btn-' + this.buttonPosition
2791 modal.cls += ' fade';
2797 getChildContainer : function() {
2802 getButtonContainer : function() {
2803 return this.el.select('.modal-footer div',true).first();
2806 initEvents : function()
2808 if (this.allow_close) {
2809 this.closeEl.on('click', this.hide, this);
2811 Roo.EventManager.onWindowResize(this.resize, this, true);
2818 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2819 if (this.fitwindow) {
2820 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2821 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2826 setSize : function(w,h)
2836 if (!this.rendered) {
2840 //this.el.setStyle('display', 'block');
2841 this.el.removeClass('hideing');
2842 this.el.addClass('show');
2844 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2847 this.el.addClass('in');
2850 this.el.addClass('in');
2854 // not sure how we can show data in here..
2856 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2859 Roo.get(document.body).addClass("x-body-masked");
2861 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2862 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2863 this.maskEl.addClass('show');
2867 this.fireEvent('show', this);
2869 // set zindex here - otherwise it appears to be ignored...
2870 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2873 this.items.forEach( function(e) {
2874 e.layout ? e.layout() : false;
2882 if(this.fireEvent("beforehide", this) !== false){
2883 this.maskEl.removeClass('show');
2884 Roo.get(document.body).removeClass("x-body-masked");
2885 this.el.removeClass('in');
2886 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2888 if(this.animate){ // why
2889 this.el.addClass('hideing');
2891 if (!this.el.hasClass('hideing')) {
2892 return; // it's been shown again...
2894 this.el.removeClass('show');
2895 this.el.removeClass('hideing');
2899 this.el.removeClass('show');
2901 this.fireEvent('hide', this);
2904 isVisible : function()
2907 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2911 addButton : function(str, cb)
2915 var b = Roo.apply({}, { html : str } );
2916 b.xns = b.xns || Roo.bootstrap;
2917 b.xtype = b.xtype || 'Button';
2918 if (typeof(b.listeners) == 'undefined') {
2919 b.listeners = { click : cb.createDelegate(this) };
2922 var btn = Roo.factory(b);
2924 btn.render(this.el.select('.modal-footer div').first());
2930 setDefaultButton : function(btn)
2932 //this.el.select('.modal-footer').()
2936 resizeTo: function(w,h)
2940 this.dialogEl.setWidth(w);
2941 if (this.diff === false) {
2942 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2945 this.bodyEl.setHeight(h-this.diff);
2947 this.fireEvent('resize', this);
2950 setContentSize : function(w, h)
2954 onButtonClick: function(btn,e)
2957 this.fireEvent('btnclick', btn.name, e);
2960 * Set the title of the Dialog
2961 * @param {String} str new Title
2963 setTitle: function(str) {
2964 this.titleEl.dom.innerHTML = str;
2967 * Set the body of the Dialog
2968 * @param {String} str new Title
2970 setBody: function(str) {
2971 this.bodyEl.dom.innerHTML = str;
2974 * Set the body of the Dialog using the template
2975 * @param {Obj} data - apply this data to the template and replace the body contents.
2977 applyBody: function(obj)
2980 Roo.log("Error - using apply Body without a template");
2983 this.tmpl.overwrite(this.bodyEl, obj);
2989 Roo.apply(Roo.bootstrap.Modal, {
2991 * Button config that displays a single OK button
3000 * Button config that displays Yes and No buttons
3016 * Button config that displays OK and Cancel buttons
3031 * Button config that displays Yes, No and Cancel buttons
3055 * messagebox - can be used as a replace
3059 * @class Roo.MessageBox
3060 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3064 Roo.Msg.alert('Status', 'Changes saved successfully.');
3066 // Prompt for user data:
3067 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3069 // process text value...
3073 // Show a dialog using config options:
3075 title:'Save Changes?',
3076 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3077 buttons: Roo.Msg.YESNOCANCEL,
3084 Roo.bootstrap.MessageBox = function(){
3085 var dlg, opt, mask, waitTimer;
3086 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3087 var buttons, activeTextEl, bwidth;
3091 var handleButton = function(button){
3093 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3097 var handleHide = function(){
3099 dlg.el.removeClass(opt.cls);
3102 // Roo.TaskMgr.stop(waitTimer);
3103 // waitTimer = null;
3108 var updateButtons = function(b){
3111 buttons["ok"].hide();
3112 buttons["cancel"].hide();
3113 buttons["yes"].hide();
3114 buttons["no"].hide();
3115 //dlg.footer.dom.style.display = 'none';
3118 dlg.footerEl.dom.style.display = '';
3119 for(var k in buttons){
3120 if(typeof buttons[k] != "function"){
3123 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3124 width += buttons[k].el.getWidth()+15;
3134 var handleEsc = function(d, k, e){
3135 if(opt && opt.closable !== false){
3145 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3146 * @return {Roo.BasicDialog} The BasicDialog element
3148 getDialog : function(){
3150 dlg = new Roo.bootstrap.Modal( {
3153 //constraintoviewport:false,
3155 //collapsible : false,
3160 //buttonAlign:"center",
3161 closeClick : function(){
3162 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3165 handleButton("cancel");
3170 dlg.on("hide", handleHide);
3172 //dlg.addKeyListener(27, handleEsc);
3174 this.buttons = buttons;
3175 var bt = this.buttonText;
3176 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3177 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3178 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3179 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3181 bodyEl = dlg.bodyEl.createChild({
3183 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3184 '<textarea class="roo-mb-textarea"></textarea>' +
3185 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3187 msgEl = bodyEl.dom.firstChild;
3188 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3189 textboxEl.enableDisplayMode();
3190 textboxEl.addKeyListener([10,13], function(){
3191 if(dlg.isVisible() && opt && opt.buttons){
3194 }else if(opt.buttons.yes){
3195 handleButton("yes");
3199 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3200 textareaEl.enableDisplayMode();
3201 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3202 progressEl.enableDisplayMode();
3204 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3205 var pf = progressEl.dom.firstChild;
3207 pp = Roo.get(pf.firstChild);
3208 pp.setHeight(pf.offsetHeight);
3216 * Updates the message box body text
3217 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3218 * the XHTML-compliant non-breaking space character '&#160;')
3219 * @return {Roo.MessageBox} This message box
3221 updateText : function(text)
3223 if(!dlg.isVisible() && !opt.width){
3224 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3225 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3227 msgEl.innerHTML = text || ' ';
3229 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3230 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3232 Math.min(opt.width || cw , this.maxWidth),
3233 Math.max(opt.minWidth || this.minWidth, bwidth)
3236 activeTextEl.setWidth(w);
3238 if(dlg.isVisible()){
3239 dlg.fixedcenter = false;
3241 // to big, make it scroll. = But as usual stupid IE does not support
3244 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3245 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3246 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3248 bodyEl.dom.style.height = '';
3249 bodyEl.dom.style.overflowY = '';
3252 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3254 bodyEl.dom.style.overflowX = '';
3257 dlg.setContentSize(w, bodyEl.getHeight());
3258 if(dlg.isVisible()){
3259 dlg.fixedcenter = true;
3265 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3266 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3267 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3268 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3269 * @return {Roo.MessageBox} This message box
3271 updateProgress : function(value, text){
3273 this.updateText(text);
3276 if (pp) { // weird bug on my firefox - for some reason this is not defined
3277 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3278 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3284 * Returns true if the message box is currently displayed
3285 * @return {Boolean} True if the message box is visible, else false
3287 isVisible : function(){
3288 return dlg && dlg.isVisible();
3292 * Hides the message box if it is displayed
3295 if(this.isVisible()){
3301 * Displays a new message box, or reinitializes an existing message box, based on the config options
3302 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3303 * The following config object properties are supported:
3305 Property Type Description
3306 ---------- --------------- ------------------------------------------------------------------------------------
3307 animEl String/Element An id or Element from which the message box should animate as it opens and
3308 closes (defaults to undefined)
3309 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3310 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3311 closable Boolean False to hide the top-right close button (defaults to true). Note that
3312 progress and wait dialogs will ignore this property and always hide the
3313 close button as they can only be closed programmatically.
3314 cls String A custom CSS class to apply to the message box element
3315 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3316 displayed (defaults to 75)
3317 fn Function A callback function to execute after closing the dialog. The arguments to the
3318 function will be btn (the name of the button that was clicked, if applicable,
3319 e.g. "ok"), and text (the value of the active text field, if applicable).
3320 Progress and wait dialogs will ignore this option since they do not respond to
3321 user actions and can only be closed programmatically, so any required function
3322 should be called by the same code after it closes the dialog.
3323 icon String A CSS class that provides a background image to be used as an icon for
3324 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3325 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3326 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3327 modal Boolean False to allow user interaction with the page while the message box is
3328 displayed (defaults to true)
3329 msg String A string that will replace the existing message box body text (defaults
3330 to the XHTML-compliant non-breaking space character ' ')
3331 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3332 progress Boolean True to display a progress bar (defaults to false)
3333 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3334 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3335 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3336 title String The title text
3337 value String The string value to set into the active textbox element if displayed
3338 wait Boolean True to display a progress bar (defaults to false)
3339 width Number The width of the dialog in pixels
3346 msg: 'Please enter your address:',
3348 buttons: Roo.MessageBox.OKCANCEL,
3351 animEl: 'addAddressBtn'
3354 * @param {Object} config Configuration options
3355 * @return {Roo.MessageBox} This message box
3357 show : function(options)
3360 // this causes nightmares if you show one dialog after another
3361 // especially on callbacks..
3363 if(this.isVisible()){
3366 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3367 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3368 Roo.log("New Dialog Message:" + options.msg )
3369 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3370 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3373 var d = this.getDialog();
3375 d.setTitle(opt.title || " ");
3376 d.closeEl.setDisplayed(opt.closable !== false);
3377 activeTextEl = textboxEl;
3378 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3383 textareaEl.setHeight(typeof opt.multiline == "number" ?
3384 opt.multiline : this.defaultTextHeight);
3385 activeTextEl = textareaEl;
3394 progressEl.setDisplayed(opt.progress === true);
3395 this.updateProgress(0);
3396 activeTextEl.dom.value = opt.value || "";
3398 dlg.setDefaultButton(activeTextEl);
3400 var bs = opt.buttons;
3404 }else if(bs && bs.yes){
3405 db = buttons["yes"];
3407 dlg.setDefaultButton(db);
3409 bwidth = updateButtons(opt.buttons);
3410 this.updateText(opt.msg);
3412 d.el.addClass(opt.cls);
3414 d.proxyDrag = opt.proxyDrag === true;
3415 d.modal = opt.modal !== false;
3416 d.mask = opt.modal !== false ? mask : false;
3418 // force it to the end of the z-index stack so it gets a cursor in FF
3419 document.body.appendChild(dlg.el.dom);
3420 d.animateTarget = null;
3421 d.show(options.animEl);
3427 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3428 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3429 * and closing the message box when the process is complete.
3430 * @param {String} title The title bar text
3431 * @param {String} msg The message box body text
3432 * @return {Roo.MessageBox} This message box
3434 progress : function(title, msg){
3441 minWidth: this.minProgressWidth,
3448 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3449 * If a callback function is passed it will be called after the user clicks the button, and the
3450 * id of the button that was clicked will be passed as the only parameter to the callback
3451 * (could also be the top-right close button).
3452 * @param {String} title The title bar text
3453 * @param {String} msg The message box body text
3454 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3455 * @param {Object} scope (optional) The scope of the callback function
3456 * @return {Roo.MessageBox} This message box
3458 alert : function(title, msg, fn, scope)
3473 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3474 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3475 * You are responsible for closing the message box when the process is complete.
3476 * @param {String} msg The message box body text
3477 * @param {String} title (optional) The title bar text
3478 * @return {Roo.MessageBox} This message box
3480 wait : function(msg, title){
3491 waitTimer = Roo.TaskMgr.start({
3493 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3501 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3502 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3503 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3504 * @param {String} title The title bar text
3505 * @param {String} msg The message box body text
3506 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3507 * @param {Object} scope (optional) The scope of the callback function
3508 * @return {Roo.MessageBox} This message box
3510 confirm : function(title, msg, fn, scope){
3514 buttons: this.YESNO,
3523 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3524 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3525 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3526 * (could also be the top-right close button) and the text that was entered will be passed as the two
3527 * parameters to the callback.
3528 * @param {String} title The title bar text
3529 * @param {String} msg The message box body text
3530 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3531 * @param {Object} scope (optional) The scope of the callback function
3532 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3533 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3534 * @return {Roo.MessageBox} This message box
3536 prompt : function(title, msg, fn, scope, multiline){
3540 buttons: this.OKCANCEL,
3545 multiline: multiline,
3552 * Button config that displays a single OK button
3557 * Button config that displays Yes and No buttons
3560 YESNO : {yes:true, no:true},
3562 * Button config that displays OK and Cancel buttons
3565 OKCANCEL : {ok:true, cancel:true},
3567 * Button config that displays Yes, No and Cancel buttons
3570 YESNOCANCEL : {yes:true, no:true, cancel:true},
3573 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3576 defaultTextHeight : 75,
3578 * The maximum width in pixels of the message box (defaults to 600)
3583 * The minimum width in pixels of the message box (defaults to 100)
3588 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3589 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3592 minProgressWidth : 250,
3594 * An object containing the default button text strings that can be overriden for localized language support.
3595 * Supported properties are: ok, cancel, yes and no.
3596 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3609 * Shorthand for {@link Roo.MessageBox}
3611 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3612 Roo.Msg = Roo.Msg || Roo.MessageBox;
3621 * @class Roo.bootstrap.Navbar
3622 * @extends Roo.bootstrap.Component
3623 * Bootstrap Navbar class
3626 * Create a new Navbar
3627 * @param {Object} config The config object
3631 Roo.bootstrap.Navbar = function(config){
3632 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3636 * @event beforetoggle
3637 * Fire before toggle the menu
3638 * @param {Roo.EventObject} e
3640 "beforetoggle" : true
3644 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3653 getAutoCreate : function(){
3656 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3660 initEvents :function ()
3662 //Roo.log(this.el.select('.navbar-toggle',true));
3663 this.el.select('.navbar-toggle',true).on('click', function() {
3664 if(this.fireEvent('beforetoggle', this) !== false){
3665 this.el.select('.navbar-collapse',true).toggleClass('in');
3675 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3677 var size = this.el.getSize();
3678 this.maskEl.setSize(size.width, size.height);
3679 this.maskEl.enableDisplayMode("block");
3688 getChildContainer : function()
3690 if (this.el.select('.collapse').getCount()) {
3691 return this.el.select('.collapse',true).first();
3724 * @class Roo.bootstrap.NavSimplebar
3725 * @extends Roo.bootstrap.Navbar
3726 * Bootstrap Sidebar class
3728 * @cfg {Boolean} inverse is inverted color
3730 * @cfg {String} type (nav | pills | tabs)
3731 * @cfg {Boolean} arrangement stacked | justified
3732 * @cfg {String} align (left | right) alignment
3734 * @cfg {Boolean} main (true|false) main nav bar? default false
3735 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3737 * @cfg {String} tag (header|footer|nav|div) default is nav
3743 * Create a new Sidebar
3744 * @param {Object} config The config object
3748 Roo.bootstrap.NavSimplebar = function(config){
3749 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3752 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3768 getAutoCreate : function(){
3772 tag : this.tag || 'div',
3785 this.type = this.type || 'nav';
3786 if (['tabs','pills'].indexOf(this.type)!==-1) {
3787 cfg.cn[0].cls += ' nav-' + this.type
3791 if (this.type!=='nav') {
3792 Roo.log('nav type must be nav/tabs/pills')
3794 cfg.cn[0].cls += ' navbar-nav'
3800 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3801 cfg.cn[0].cls += ' nav-' + this.arrangement;
3805 if (this.align === 'right') {
3806 cfg.cn[0].cls += ' navbar-right';
3810 cfg.cls += ' navbar-inverse';
3837 * @class Roo.bootstrap.NavHeaderbar
3838 * @extends Roo.bootstrap.NavSimplebar
3839 * Bootstrap Sidebar class
3841 * @cfg {String} brand what is brand
3842 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3843 * @cfg {String} brand_href href of the brand
3844 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3845 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3846 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3847 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3850 * Create a new Sidebar
3851 * @param {Object} config The config object
3855 Roo.bootstrap.NavHeaderbar = function(config){
3856 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3860 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3867 desktopCenter : false,
3870 getAutoCreate : function(){
3873 tag: this.nav || 'nav',
3880 if (this.desktopCenter) {
3881 cn.push({cls : 'container', cn : []});
3888 cls: 'navbar-header',
3893 cls: 'navbar-toggle',
3894 'data-toggle': 'collapse',
3899 html: 'Toggle navigation'
3921 cls: 'collapse navbar-collapse',
3925 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3927 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3928 cfg.cls += ' navbar-' + this.position;
3930 // tag can override this..
3932 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3935 if (this.brand !== '') {
3938 href: this.brand_href ? this.brand_href : '#',
3939 cls: 'navbar-brand',
3947 cfg.cls += ' main-nav';
3955 getHeaderChildContainer : function()
3957 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3958 return this.el.select('.navbar-header',true).first();
3961 return this.getChildContainer();
3965 initEvents : function()
3967 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3969 if (this.autohide) {
3974 Roo.get(document).on('scroll',function(e) {
3975 var ns = Roo.get(document).getScroll().top;
3976 var os = prevScroll;
3980 ft.removeClass('slideDown');
3981 ft.addClass('slideUp');
3984 ft.removeClass('slideUp');
3985 ft.addClass('slideDown');
4006 * @class Roo.bootstrap.NavSidebar
4007 * @extends Roo.bootstrap.Navbar
4008 * Bootstrap Sidebar class
4011 * Create a new Sidebar
4012 * @param {Object} config The config object
4016 Roo.bootstrap.NavSidebar = function(config){
4017 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4020 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4022 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4024 getAutoCreate : function(){
4029 cls: 'sidebar sidebar-nav'
4051 * @class Roo.bootstrap.NavGroup
4052 * @extends Roo.bootstrap.Component
4053 * Bootstrap NavGroup class
4054 * @cfg {String} align (left|right)
4055 * @cfg {Boolean} inverse
4056 * @cfg {String} type (nav|pills|tab) default nav
4057 * @cfg {String} navId - reference Id for navbar.
4061 * Create a new nav group
4062 * @param {Object} config The config object
4065 Roo.bootstrap.NavGroup = function(config){
4066 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4069 Roo.bootstrap.NavGroup.register(this);
4073 * Fires when the active item changes
4074 * @param {Roo.bootstrap.NavGroup} this
4075 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4076 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4083 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4094 getAutoCreate : function()
4096 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4103 if (['tabs','pills'].indexOf(this.type)!==-1) {
4104 cfg.cls += ' nav-' + this.type
4106 if (this.type!=='nav') {
4107 Roo.log('nav type must be nav/tabs/pills')
4109 cfg.cls += ' navbar-nav'
4112 if (this.parent() && this.parent().sidebar) {
4115 cls: 'dashboard-menu sidebar-menu'
4121 if (this.form === true) {
4127 if (this.align === 'right') {
4128 cfg.cls += ' navbar-right';
4130 cfg.cls += ' navbar-left';
4134 if (this.align === 'right') {
4135 cfg.cls += ' navbar-right';
4139 cfg.cls += ' navbar-inverse';
4147 * sets the active Navigation item
4148 * @param {Roo.bootstrap.NavItem} the new current navitem
4150 setActiveItem : function(item)
4153 Roo.each(this.navItems, function(v){
4158 v.setActive(false, true);
4165 item.setActive(true, true);
4166 this.fireEvent('changed', this, item, prev);
4171 * gets the active Navigation item
4172 * @return {Roo.bootstrap.NavItem} the current navitem
4174 getActive : function()
4178 Roo.each(this.navItems, function(v){
4189 indexOfNav : function()
4193 Roo.each(this.navItems, function(v,i){
4204 * adds a Navigation item
4205 * @param {Roo.bootstrap.NavItem} the navitem to add
4207 addItem : function(cfg)
4209 var cn = new Roo.bootstrap.NavItem(cfg);
4211 cn.parentId = this.id;
4212 cn.onRender(this.el, null);
4216 * register a Navigation item
4217 * @param {Roo.bootstrap.NavItem} the navitem to add
4219 register : function(item)
4221 this.navItems.push( item);
4222 item.navId = this.navId;
4227 * clear all the Navigation item
4230 clearAll : function()
4233 this.el.dom.innerHTML = '';
4236 getNavItem: function(tabId)
4239 Roo.each(this.navItems, function(e) {
4240 if (e.tabId == tabId) {
4250 setActiveNext : function()
4252 var i = this.indexOfNav(this.getActive());
4253 if (i > this.navItems.length) {
4256 this.setActiveItem(this.navItems[i+1]);
4258 setActivePrev : function()
4260 var i = this.indexOfNav(this.getActive());
4264 this.setActiveItem(this.navItems[i-1]);
4266 clearWasActive : function(except) {
4267 Roo.each(this.navItems, function(e) {
4268 if (e.tabId != except.tabId && e.was_active) {
4269 e.was_active = false;
4276 getWasActive : function ()
4279 Roo.each(this.navItems, function(e) {
4294 Roo.apply(Roo.bootstrap.NavGroup, {
4298 * register a Navigation Group
4299 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4301 register : function(navgrp)
4303 this.groups[navgrp.navId] = navgrp;
4307 * fetch a Navigation Group based on the navigation ID
4308 * @param {string} the navgroup to add
4309 * @returns {Roo.bootstrap.NavGroup} the navgroup
4311 get: function(navId) {
4312 if (typeof(this.groups[navId]) == 'undefined') {
4314 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4316 return this.groups[navId] ;
4331 * @class Roo.bootstrap.NavItem
4332 * @extends Roo.bootstrap.Component
4333 * Bootstrap Navbar.NavItem class
4334 * @cfg {String} href link to
4335 * @cfg {String} html content of button
4336 * @cfg {String} badge text inside badge
4337 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4338 * @cfg {String} glyphicon name of glyphicon
4339 * @cfg {String} icon name of font awesome icon
4340 * @cfg {Boolean} active Is item active
4341 * @cfg {Boolean} disabled Is item disabled
4343 * @cfg {Boolean} preventDefault (true | false) default false
4344 * @cfg {String} tabId the tab that this item activates.
4345 * @cfg {String} tagtype (a|span) render as a href or span?
4346 * @cfg {Boolean} animateRef (true|false) link to element default false
4349 * Create a new Navbar Item
4350 * @param {Object} config The config object
4352 Roo.bootstrap.NavItem = function(config){
4353 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4358 * The raw click event for the entire grid.
4359 * @param {Roo.EventObject} e
4364 * Fires when the active item active state changes
4365 * @param {Roo.bootstrap.NavItem} this
4366 * @param {boolean} state the new state
4372 * Fires when scroll to element
4373 * @param {Roo.bootstrap.NavItem} this
4374 * @param {Object} options
4375 * @param {Roo.EventObject} e
4383 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4391 preventDefault : false,
4398 getAutoCreate : function(){
4407 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4409 if (this.disabled) {
4410 cfg.cls += ' disabled';
4413 if (this.href || this.html || this.glyphicon || this.icon) {
4417 href : this.href || "#",
4418 html: this.html || ''
4423 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4426 if(this.glyphicon) {
4427 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4432 cfg.cn[0].html += " <span class='caret'></span>";
4436 if (this.badge !== '') {
4438 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4446 initEvents: function()
4448 if (typeof (this.menu) != 'undefined') {
4449 this.menu.parentType = this.xtype;
4450 this.menu.triggerEl = this.el;
4451 this.menu = this.addxtype(Roo.apply({}, this.menu));
4454 this.el.select('a',true).on('click', this.onClick, this);
4456 if(this.tagtype == 'span'){
4457 this.el.select('span',true).on('click', this.onClick, this);
4460 // at this point parent should be available..
4461 this.parent().register(this);
4464 onClick : function(e)
4466 if (e.getTarget('.dropdown-menu-item')) {
4467 // did you click on a menu itemm.... - then don't trigger onclick..
4472 this.preventDefault ||
4475 Roo.log("NavItem - prevent Default?");
4479 if (this.disabled) {
4483 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4484 if (tg && tg.transition) {
4485 Roo.log("waiting for the transitionend");
4491 //Roo.log("fire event clicked");
4492 if(this.fireEvent('click', this, e) === false){
4496 if(this.tagtype == 'span'){
4500 //Roo.log(this.href);
4501 var ael = this.el.select('a',true).first();
4504 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4505 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4506 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4507 return; // ignore... - it's a 'hash' to another page.
4509 Roo.log("NavItem - prevent Default?");
4511 this.scrollToElement(e);
4515 var p = this.parent();
4517 if (['tabs','pills'].indexOf(p.type)!==-1) {
4518 if (typeof(p.setActiveItem) !== 'undefined') {
4519 p.setActiveItem(this);
4523 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4524 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4525 // remove the collapsed menu expand...
4526 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4530 isActive: function () {
4533 setActive : function(state, fire, is_was_active)
4535 if (this.active && !state && this.navId) {
4536 this.was_active = true;
4537 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4539 nv.clearWasActive(this);
4543 this.active = state;
4546 this.el.removeClass('active');
4547 } else if (!this.el.hasClass('active')) {
4548 this.el.addClass('active');
4551 this.fireEvent('changed', this, state);
4554 // show a panel if it's registered and related..
4556 if (!this.navId || !this.tabId || !state || is_was_active) {
4560 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4564 var pan = tg.getPanelByName(this.tabId);
4568 // if we can not flip to new panel - go back to old nav highlight..
4569 if (false == tg.showPanel(pan)) {
4570 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4572 var onav = nv.getWasActive();
4574 onav.setActive(true, false, true);
4583 // this should not be here...
4584 setDisabled : function(state)
4586 this.disabled = state;
4588 this.el.removeClass('disabled');
4589 } else if (!this.el.hasClass('disabled')) {
4590 this.el.addClass('disabled');
4596 * Fetch the element to display the tooltip on.
4597 * @return {Roo.Element} defaults to this.el
4599 tooltipEl : function()
4601 return this.el.select('' + this.tagtype + '', true).first();
4604 scrollToElement : function(e)
4606 var c = document.body;
4609 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4611 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4612 c = document.documentElement;
4615 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4621 var o = target.calcOffsetsTo(c);
4628 this.fireEvent('scrollto', this, options, e);
4630 Roo.get(c).scrollTo('top', options.value, true);
4643 * <span> icon </span>
4644 * <span> text </span>
4645 * <span>badge </span>
4649 * @class Roo.bootstrap.NavSidebarItem
4650 * @extends Roo.bootstrap.NavItem
4651 * Bootstrap Navbar.NavSidebarItem class
4652 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4653 * {Boolean} open is the menu open
4654 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4655 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4656 * {String} buttonSize (sm|md|lg)the extra classes for the button
4657 * {Boolean} showArrow show arrow next to the text (default true)
4659 * Create a new Navbar Button
4660 * @param {Object} config The config object
4662 Roo.bootstrap.NavSidebarItem = function(config){
4663 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4668 * The raw click event for the entire grid.
4669 * @param {Roo.EventObject} e
4674 * Fires when the active item active state changes
4675 * @param {Roo.bootstrap.NavSidebarItem} this
4676 * @param {boolean} state the new state
4684 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4686 badgeWeight : 'default',
4692 buttonWeight : 'default',
4698 getAutoCreate : function(){
4703 href : this.href || '#',
4709 if(this.buttonView){
4712 href : this.href || '#',
4713 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4726 cfg.cls += ' active';
4729 if (this.disabled) {
4730 cfg.cls += ' disabled';
4733 cfg.cls += ' open x-open';
4736 if (this.glyphicon || this.icon) {
4737 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4738 a.cn.push({ tag : 'i', cls : c }) ;
4741 if(!this.buttonView){
4744 html : this.html || ''
4751 if (this.badge !== '') {
4752 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4758 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4761 a.cls += ' dropdown-toggle treeview' ;
4767 initEvents : function()
4769 if (typeof (this.menu) != 'undefined') {
4770 this.menu.parentType = this.xtype;
4771 this.menu.triggerEl = this.el;
4772 this.menu = this.addxtype(Roo.apply({}, this.menu));
4775 this.el.on('click', this.onClick, this);
4777 if(this.badge !== ''){
4778 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4783 onClick : function(e)
4790 if(this.preventDefault){
4794 this.fireEvent('click', this);
4797 disable : function()
4799 this.setDisabled(true);
4804 this.setDisabled(false);
4807 setDisabled : function(state)
4809 if(this.disabled == state){
4813 this.disabled = state;
4816 this.el.addClass('disabled');
4820 this.el.removeClass('disabled');
4825 setActive : function(state)
4827 if(this.active == state){
4831 this.active = state;
4834 this.el.addClass('active');
4838 this.el.removeClass('active');
4843 isActive: function ()
4848 setBadge : function(str)
4854 this.badgeEl.dom.innerHTML = str;
4871 * @class Roo.bootstrap.Row
4872 * @extends Roo.bootstrap.Component
4873 * Bootstrap Row class (contains columns...)
4877 * @param {Object} config The config object
4880 Roo.bootstrap.Row = function(config){
4881 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4884 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4886 getAutoCreate : function(){
4905 * @class Roo.bootstrap.Element
4906 * @extends Roo.bootstrap.Component
4907 * Bootstrap Element class
4908 * @cfg {String} html contents of the element
4909 * @cfg {String} tag tag of the element
4910 * @cfg {String} cls class of the element
4911 * @cfg {Boolean} preventDefault (true|false) default false
4912 * @cfg {Boolean} clickable (true|false) default false
4915 * Create a new Element
4916 * @param {Object} config The config object
4919 Roo.bootstrap.Element = function(config){
4920 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4926 * When a element is chick
4927 * @param {Roo.bootstrap.Element} this
4928 * @param {Roo.EventObject} e
4934 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4939 preventDefault: false,
4942 getAutoCreate : function(){
4946 // cls: this.cls, double assign in parent class Component.js :: onRender
4953 initEvents: function()
4955 Roo.bootstrap.Element.superclass.initEvents.call(this);
4958 this.el.on('click', this.onClick, this);
4963 onClick : function(e)
4965 if(this.preventDefault){
4969 this.fireEvent('click', this, e);
4972 getValue : function()
4974 return this.el.dom.innerHTML;
4977 setValue : function(value)
4979 this.el.dom.innerHTML = value;
4994 * @class Roo.bootstrap.Pagination
4995 * @extends Roo.bootstrap.Component
4996 * Bootstrap Pagination class
4997 * @cfg {String} size xs | sm | md | lg
4998 * @cfg {Boolean} inverse false | true
5001 * Create a new Pagination
5002 * @param {Object} config The config object
5005 Roo.bootstrap.Pagination = function(config){
5006 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5009 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5015 getAutoCreate : function(){
5021 cfg.cls += ' inverse';
5027 cfg.cls += " " + this.cls;
5045 * @class Roo.bootstrap.PaginationItem
5046 * @extends Roo.bootstrap.Component
5047 * Bootstrap PaginationItem class
5048 * @cfg {String} html text
5049 * @cfg {String} href the link
5050 * @cfg {Boolean} preventDefault (true | false) default true
5051 * @cfg {Boolean} active (true | false) default false
5052 * @cfg {Boolean} disabled default false
5056 * Create a new PaginationItem
5057 * @param {Object} config The config object
5061 Roo.bootstrap.PaginationItem = function(config){
5062 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5067 * The raw click event for the entire grid.
5068 * @param {Roo.EventObject} e
5074 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5078 preventDefault: true,
5083 getAutoCreate : function(){
5089 href : this.href ? this.href : '#',
5090 html : this.html ? this.html : ''
5100 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5104 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5110 initEvents: function() {
5112 this.el.on('click', this.onClick, this);
5115 onClick : function(e)
5117 Roo.log('PaginationItem on click ');
5118 if(this.preventDefault){
5126 this.fireEvent('click', this, e);
5142 * @class Roo.bootstrap.Slider
5143 * @extends Roo.bootstrap.Component
5144 * Bootstrap Slider class
5147 * Create a new Slider
5148 * @param {Object} config The config object
5151 Roo.bootstrap.Slider = function(config){
5152 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5155 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5157 getAutoCreate : function(){
5161 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5165 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5177 * Ext JS Library 1.1.1
5178 * Copyright(c) 2006-2007, Ext JS, LLC.
5180 * Originally Released Under LGPL - original licence link has changed is not relivant.
5183 * <script type="text/javascript">
5188 * @class Roo.grid.ColumnModel
5189 * @extends Roo.util.Observable
5190 * This is the default implementation of a ColumnModel used by the Grid. It defines
5191 * the columns in the grid.
5194 var colModel = new Roo.grid.ColumnModel([
5195 {header: "Ticker", width: 60, sortable: true, locked: true},
5196 {header: "Company Name", width: 150, sortable: true},
5197 {header: "Market Cap.", width: 100, sortable: true},
5198 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5199 {header: "Employees", width: 100, sortable: true, resizable: false}
5204 * The config options listed for this class are options which may appear in each
5205 * individual column definition.
5206 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5208 * @param {Object} config An Array of column config objects. See this class's
5209 * config objects for details.
5211 Roo.grid.ColumnModel = function(config){
5213 * The config passed into the constructor
5215 this.config = config;
5218 // if no id, create one
5219 // if the column does not have a dataIndex mapping,
5220 // map it to the order it is in the config
5221 for(var i = 0, len = config.length; i < len; i++){
5223 if(typeof c.dataIndex == "undefined"){
5226 if(typeof c.renderer == "string"){
5227 c.renderer = Roo.util.Format[c.renderer];
5229 if(typeof c.id == "undefined"){
5232 if(c.editor && c.editor.xtype){
5233 c.editor = Roo.factory(c.editor, Roo.grid);
5235 if(c.editor && c.editor.isFormField){
5236 c.editor = new Roo.grid.GridEditor(c.editor);
5238 this.lookup[c.id] = c;
5242 * The width of columns which have no width specified (defaults to 100)
5245 this.defaultWidth = 100;
5248 * Default sortable of columns which have no sortable specified (defaults to false)
5251 this.defaultSortable = false;
5255 * @event widthchange
5256 * Fires when the width of a column changes.
5257 * @param {ColumnModel} this
5258 * @param {Number} columnIndex The column index
5259 * @param {Number} newWidth The new width
5261 "widthchange": true,
5263 * @event headerchange
5264 * Fires when the text of a header changes.
5265 * @param {ColumnModel} this
5266 * @param {Number} columnIndex The column index
5267 * @param {Number} newText The new header text
5269 "headerchange": true,
5271 * @event hiddenchange
5272 * Fires when a column is hidden or "unhidden".
5273 * @param {ColumnModel} this
5274 * @param {Number} columnIndex The column index
5275 * @param {Boolean} hidden true if hidden, false otherwise
5277 "hiddenchange": true,
5279 * @event columnmoved
5280 * Fires when a column is moved.
5281 * @param {ColumnModel} this
5282 * @param {Number} oldIndex
5283 * @param {Number} newIndex
5285 "columnmoved" : true,
5287 * @event columlockchange
5288 * Fires when a column's locked state is changed
5289 * @param {ColumnModel} this
5290 * @param {Number} colIndex
5291 * @param {Boolean} locked true if locked
5293 "columnlockchange" : true
5295 Roo.grid.ColumnModel.superclass.constructor.call(this);
5297 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5299 * @cfg {String} header The header text to display in the Grid view.
5302 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5303 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5304 * specified, the column's index is used as an index into the Record's data Array.
5307 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5308 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5311 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5312 * Defaults to the value of the {@link #defaultSortable} property.
5313 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5316 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5319 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5322 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5325 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5328 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5329 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5330 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5331 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5334 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5337 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5340 * @cfg {String} cursor (Optional)
5343 * @cfg {String} tooltip (Optional)
5346 * @cfg {Number} xs (Optional)
5349 * @cfg {Number} sm (Optional)
5352 * @cfg {Number} md (Optional)
5355 * @cfg {Number} lg (Optional)
5358 * Returns the id of the column at the specified index.
5359 * @param {Number} index The column index
5360 * @return {String} the id
5362 getColumnId : function(index){
5363 return this.config[index].id;
5367 * Returns the column for a specified id.
5368 * @param {String} id The column id
5369 * @return {Object} the column
5371 getColumnById : function(id){
5372 return this.lookup[id];
5377 * Returns the column for a specified dataIndex.
5378 * @param {String} dataIndex The column dataIndex
5379 * @return {Object|Boolean} the column or false if not found
5381 getColumnByDataIndex: function(dataIndex){
5382 var index = this.findColumnIndex(dataIndex);
5383 return index > -1 ? this.config[index] : false;
5387 * Returns the index for a specified column id.
5388 * @param {String} id The column id
5389 * @return {Number} the index, or -1 if not found
5391 getIndexById : function(id){
5392 for(var i = 0, len = this.config.length; i < len; i++){
5393 if(this.config[i].id == id){
5401 * Returns the index for a specified column dataIndex.
5402 * @param {String} dataIndex The column dataIndex
5403 * @return {Number} the index, or -1 if not found
5406 findColumnIndex : function(dataIndex){
5407 for(var i = 0, len = this.config.length; i < len; i++){
5408 if(this.config[i].dataIndex == dataIndex){
5416 moveColumn : function(oldIndex, newIndex){
5417 var c = this.config[oldIndex];
5418 this.config.splice(oldIndex, 1);
5419 this.config.splice(newIndex, 0, c);
5420 this.dataMap = null;
5421 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5424 isLocked : function(colIndex){
5425 return this.config[colIndex].locked === true;
5428 setLocked : function(colIndex, value, suppressEvent){
5429 if(this.isLocked(colIndex) == value){
5432 this.config[colIndex].locked = value;
5434 this.fireEvent("columnlockchange", this, colIndex, value);
5438 getTotalLockedWidth : function(){
5440 for(var i = 0; i < this.config.length; i++){
5441 if(this.isLocked(i) && !this.isHidden(i)){
5442 this.totalWidth += this.getColumnWidth(i);
5448 getLockedCount : function(){
5449 for(var i = 0, len = this.config.length; i < len; i++){
5450 if(!this.isLocked(i)){
5455 return this.config.length;
5459 * Returns the number of columns.
5462 getColumnCount : function(visibleOnly){
5463 if(visibleOnly === true){
5465 for(var i = 0, len = this.config.length; i < len; i++){
5466 if(!this.isHidden(i)){
5472 return this.config.length;
5476 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5477 * @param {Function} fn
5478 * @param {Object} scope (optional)
5479 * @return {Array} result
5481 getColumnsBy : function(fn, scope){
5483 for(var i = 0, len = this.config.length; i < len; i++){
5484 var c = this.config[i];
5485 if(fn.call(scope||this, c, i) === true){
5493 * Returns true if the specified column is sortable.
5494 * @param {Number} col The column index
5497 isSortable : function(col){
5498 if(typeof this.config[col].sortable == "undefined"){
5499 return this.defaultSortable;
5501 return this.config[col].sortable;
5505 * Returns the rendering (formatting) function defined for the column.
5506 * @param {Number} col The column index.
5507 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5509 getRenderer : function(col){
5510 if(!this.config[col].renderer){
5511 return Roo.grid.ColumnModel.defaultRenderer;
5513 return this.config[col].renderer;
5517 * Sets the rendering (formatting) function for a column.
5518 * @param {Number} col The column index
5519 * @param {Function} fn The function to use to process the cell's raw data
5520 * to return HTML markup for the grid view. The render function is called with
5521 * the following parameters:<ul>
5522 * <li>Data value.</li>
5523 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5524 * <li>css A CSS style string to apply to the table cell.</li>
5525 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5526 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5527 * <li>Row index</li>
5528 * <li>Column index</li>
5529 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5531 setRenderer : function(col, fn){
5532 this.config[col].renderer = fn;
5536 * Returns the width for the specified column.
5537 * @param {Number} col The column index
5540 getColumnWidth : function(col){
5541 return this.config[col].width * 1 || this.defaultWidth;
5545 * Sets the width for a column.
5546 * @param {Number} col The column index
5547 * @param {Number} width The new width
5549 setColumnWidth : function(col, width, suppressEvent){
5550 this.config[col].width = width;
5551 this.totalWidth = null;
5553 this.fireEvent("widthchange", this, col, width);
5558 * Returns the total width of all columns.
5559 * @param {Boolean} includeHidden True to include hidden column widths
5562 getTotalWidth : function(includeHidden){
5563 if(!this.totalWidth){
5564 this.totalWidth = 0;
5565 for(var i = 0, len = this.config.length; i < len; i++){
5566 if(includeHidden || !this.isHidden(i)){
5567 this.totalWidth += this.getColumnWidth(i);
5571 return this.totalWidth;
5575 * Returns the header for the specified column.
5576 * @param {Number} col The column index
5579 getColumnHeader : function(col){
5580 return this.config[col].header;
5584 * Sets the header for a column.
5585 * @param {Number} col The column index
5586 * @param {String} header The new header
5588 setColumnHeader : function(col, header){
5589 this.config[col].header = header;
5590 this.fireEvent("headerchange", this, col, header);
5594 * Returns the tooltip for the specified column.
5595 * @param {Number} col The column index
5598 getColumnTooltip : function(col){
5599 return this.config[col].tooltip;
5602 * Sets the tooltip for a column.
5603 * @param {Number} col The column index
5604 * @param {String} tooltip The new tooltip
5606 setColumnTooltip : function(col, tooltip){
5607 this.config[col].tooltip = tooltip;
5611 * Returns the dataIndex for the specified column.
5612 * @param {Number} col The column index
5615 getDataIndex : function(col){
5616 return this.config[col].dataIndex;
5620 * Sets the dataIndex for a column.
5621 * @param {Number} col The column index
5622 * @param {Number} dataIndex The new dataIndex
5624 setDataIndex : function(col, dataIndex){
5625 this.config[col].dataIndex = dataIndex;
5631 * Returns true if the cell is editable.
5632 * @param {Number} colIndex The column index
5633 * @param {Number} rowIndex The row index - this is nto actually used..?
5636 isCellEditable : function(colIndex, rowIndex){
5637 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5641 * Returns the editor defined for the cell/column.
5642 * return false or null to disable editing.
5643 * @param {Number} colIndex The column index
5644 * @param {Number} rowIndex The row index
5647 getCellEditor : function(colIndex, rowIndex){
5648 return this.config[colIndex].editor;
5652 * Sets if a column is editable.
5653 * @param {Number} col The column index
5654 * @param {Boolean} editable True if the column is editable
5656 setEditable : function(col, editable){
5657 this.config[col].editable = editable;
5662 * Returns true if the column is hidden.
5663 * @param {Number} colIndex The column index
5666 isHidden : function(colIndex){
5667 return this.config[colIndex].hidden;
5672 * Returns true if the column width cannot be changed
5674 isFixed : function(colIndex){
5675 return this.config[colIndex].fixed;
5679 * Returns true if the column can be resized
5682 isResizable : function(colIndex){
5683 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5686 * Sets if a column is hidden.
5687 * @param {Number} colIndex The column index
5688 * @param {Boolean} hidden True if the column is hidden
5690 setHidden : function(colIndex, hidden){
5691 this.config[colIndex].hidden = hidden;
5692 this.totalWidth = null;
5693 this.fireEvent("hiddenchange", this, colIndex, hidden);
5697 * Sets the editor for a column.
5698 * @param {Number} col The column index
5699 * @param {Object} editor The editor object
5701 setEditor : function(col, editor){
5702 this.config[col].editor = editor;
5706 Roo.grid.ColumnModel.defaultRenderer = function(value)
5708 if(typeof value == "object") {
5711 if(typeof value == "string" && value.length < 1){
5715 return String.format("{0}", value);
5718 // Alias for backwards compatibility
5719 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5722 * Ext JS Library 1.1.1
5723 * Copyright(c) 2006-2007, Ext JS, LLC.
5725 * Originally Released Under LGPL - original licence link has changed is not relivant.
5728 * <script type="text/javascript">
5732 * @class Roo.LoadMask
5733 * A simple utility class for generically masking elements while loading data. If the element being masked has
5734 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5735 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5736 * element's UpdateManager load indicator and will be destroyed after the initial load.
5738 * Create a new LoadMask
5739 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5740 * @param {Object} config The config object
5742 Roo.LoadMask = function(el, config){
5743 this.el = Roo.get(el);
5744 Roo.apply(this, config);
5746 this.store.on('beforeload', this.onBeforeLoad, this);
5747 this.store.on('load', this.onLoad, this);
5748 this.store.on('loadexception', this.onLoadException, this);
5749 this.removeMask = false;
5751 var um = this.el.getUpdateManager();
5752 um.showLoadIndicator = false; // disable the default indicator
5753 um.on('beforeupdate', this.onBeforeLoad, this);
5754 um.on('update', this.onLoad, this);
5755 um.on('failure', this.onLoad, this);
5756 this.removeMask = true;
5760 Roo.LoadMask.prototype = {
5762 * @cfg {Boolean} removeMask
5763 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5764 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5768 * The text to display in a centered loading message box (defaults to 'Loading...')
5772 * @cfg {String} msgCls
5773 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5775 msgCls : 'x-mask-loading',
5778 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5784 * Disables the mask to prevent it from being displayed
5786 disable : function(){
5787 this.disabled = true;
5791 * Enables the mask so that it can be displayed
5793 enable : function(){
5794 this.disabled = false;
5797 onLoadException : function()
5801 if (typeof(arguments[3]) != 'undefined') {
5802 Roo.MessageBox.alert("Error loading",arguments[3]);
5806 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5807 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5814 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5819 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5823 onBeforeLoad : function(){
5825 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5830 destroy : function(){
5832 this.store.un('beforeload', this.onBeforeLoad, this);
5833 this.store.un('load', this.onLoad, this);
5834 this.store.un('loadexception', this.onLoadException, this);
5836 var um = this.el.getUpdateManager();
5837 um.un('beforeupdate', this.onBeforeLoad, this);
5838 um.un('update', this.onLoad, this);
5839 um.un('failure', this.onLoad, this);
5850 * @class Roo.bootstrap.Table
5851 * @extends Roo.bootstrap.Component
5852 * Bootstrap Table class
5853 * @cfg {String} cls table class
5854 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5855 * @cfg {String} bgcolor Specifies the background color for a table
5856 * @cfg {Number} border Specifies whether the table cells should have borders or not
5857 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5858 * @cfg {Number} cellspacing Specifies the space between cells
5859 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5860 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5861 * @cfg {String} sortable Specifies that the table should be sortable
5862 * @cfg {String} summary Specifies a summary of the content of a table
5863 * @cfg {Number} width Specifies the width of a table
5864 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5866 * @cfg {boolean} striped Should the rows be alternative striped
5867 * @cfg {boolean} bordered Add borders to the table
5868 * @cfg {boolean} hover Add hover highlighting
5869 * @cfg {boolean} condensed Format condensed
5870 * @cfg {boolean} responsive Format condensed
5871 * @cfg {Boolean} loadMask (true|false) default false
5872 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5873 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5874 * @cfg {Boolean} rowSelection (true|false) default false
5875 * @cfg {Boolean} cellSelection (true|false) default false
5876 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5877 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5878 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5882 * Create a new Table
5883 * @param {Object} config The config object
5886 Roo.bootstrap.Table = function(config){
5887 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5892 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5893 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5894 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5895 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5897 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5899 this.sm.grid = this;
5900 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5901 this.sm = this.selModel;
5902 this.sm.xmodule = this.xmodule || false;
5905 if (this.cm && typeof(this.cm.config) == 'undefined') {
5906 this.colModel = new Roo.grid.ColumnModel(this.cm);
5907 this.cm = this.colModel;
5908 this.cm.xmodule = this.xmodule || false;
5911 this.store= Roo.factory(this.store, Roo.data);
5912 this.ds = this.store;
5913 this.ds.xmodule = this.xmodule || false;
5916 if (this.footer && this.store) {
5917 this.footer.dataSource = this.ds;
5918 this.footer = Roo.factory(this.footer);
5925 * Fires when a cell is clicked
5926 * @param {Roo.bootstrap.Table} this
5927 * @param {Roo.Element} el
5928 * @param {Number} rowIndex
5929 * @param {Number} columnIndex
5930 * @param {Roo.EventObject} e
5934 * @event celldblclick
5935 * Fires when a cell is double clicked
5936 * @param {Roo.bootstrap.Table} this
5937 * @param {Roo.Element} el
5938 * @param {Number} rowIndex
5939 * @param {Number} columnIndex
5940 * @param {Roo.EventObject} e
5942 "celldblclick" : true,
5945 * Fires when a row is clicked
5946 * @param {Roo.bootstrap.Table} this
5947 * @param {Roo.Element} el
5948 * @param {Number} rowIndex
5949 * @param {Roo.EventObject} e
5953 * @event rowdblclick
5954 * Fires when a row is double clicked
5955 * @param {Roo.bootstrap.Table} this
5956 * @param {Roo.Element} el
5957 * @param {Number} rowIndex
5958 * @param {Roo.EventObject} e
5960 "rowdblclick" : true,
5963 * Fires when a mouseover occur
5964 * @param {Roo.bootstrap.Table} this
5965 * @param {Roo.Element} el
5966 * @param {Number} rowIndex
5967 * @param {Number} columnIndex
5968 * @param {Roo.EventObject} e
5973 * Fires when a mouseout occur
5974 * @param {Roo.bootstrap.Table} this
5975 * @param {Roo.Element} el
5976 * @param {Number} rowIndex
5977 * @param {Number} columnIndex
5978 * @param {Roo.EventObject} e
5983 * Fires when a row is rendered, so you can change add a style to it.
5984 * @param {Roo.bootstrap.Table} this
5985 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5989 * @event rowsrendered
5990 * Fires when all the rows have been rendered
5991 * @param {Roo.bootstrap.Table} this
5993 'rowsrendered' : true,
5995 * @event contextmenu
5996 * The raw contextmenu event for the entire grid.
5997 * @param {Roo.EventObject} e
5999 "contextmenu" : true,
6001 * @event rowcontextmenu
6002 * Fires when a row is right clicked
6003 * @param {Roo.bootstrap.Table} this
6004 * @param {Number} rowIndex
6005 * @param {Roo.EventObject} e
6007 "rowcontextmenu" : true,
6009 * @event cellcontextmenu
6010 * Fires when a cell is right clicked
6011 * @param {Roo.bootstrap.Table} this
6012 * @param {Number} rowIndex
6013 * @param {Number} cellIndex
6014 * @param {Roo.EventObject} e
6016 "cellcontextmenu" : true,
6018 * @event headercontextmenu
6019 * Fires when a header is right clicked
6020 * @param {Roo.bootstrap.Table} this
6021 * @param {Number} columnIndex
6022 * @param {Roo.EventObject} e
6024 "headercontextmenu" : true
6028 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6054 rowSelection : false,
6055 cellSelection : false,
6058 // Roo.Element - the tbody
6060 // Roo.Element - thead element
6063 container: false, // used by gridpanel...
6069 getAutoCreate : function()
6071 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6078 if (this.scrollBody) {
6079 cfg.cls += ' table-body-fixed';
6082 cfg.cls += ' table-striped';
6086 cfg.cls += ' table-hover';
6088 if (this.bordered) {
6089 cfg.cls += ' table-bordered';
6091 if (this.condensed) {
6092 cfg.cls += ' table-condensed';
6094 if (this.responsive) {
6095 cfg.cls += ' table-responsive';
6099 cfg.cls+= ' ' +this.cls;
6102 // this lot should be simplifed...
6105 cfg.align=this.align;
6108 cfg.bgcolor=this.bgcolor;
6111 cfg.border=this.border;
6113 if (this.cellpadding) {
6114 cfg.cellpadding=this.cellpadding;
6116 if (this.cellspacing) {
6117 cfg.cellspacing=this.cellspacing;
6120 cfg.frame=this.frame;
6123 cfg.rules=this.rules;
6125 if (this.sortable) {
6126 cfg.sortable=this.sortable;
6129 cfg.summary=this.summary;
6132 cfg.width=this.width;
6135 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6138 if(this.store || this.cm){
6139 if(this.headerShow){
6140 cfg.cn.push(this.renderHeader());
6143 cfg.cn.push(this.renderBody());
6145 if(this.footerShow){
6146 cfg.cn.push(this.renderFooter());
6148 // where does this come from?
6149 //cfg.cls+= ' TableGrid';
6152 return { cn : [ cfg ] };
6155 initEvents : function()
6157 if(!this.store || !this.cm){
6160 if (this.selModel) {
6161 this.selModel.initEvents();
6165 //Roo.log('initEvents with ds!!!!');
6167 this.mainBody = this.el.select('tbody', true).first();
6168 this.mainHead = this.el.select('thead', true).first();
6175 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6176 e.on('click', _this.sort, _this);
6179 this.mainBody.on("click", this.onClick, this);
6180 this.mainBody.on("dblclick", this.onDblClick, this);
6182 // why is this done????? = it breaks dialogs??
6183 //this.parent().el.setStyle('position', 'relative');
6187 this.footer.parentId = this.id;
6188 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6191 this.el.select('tfoot tr td').first().addClass('hide');
6196 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6199 this.store.on('load', this.onLoad, this);
6200 this.store.on('beforeload', this.onBeforeLoad, this);
6201 this.store.on('update', this.onUpdate, this);
6202 this.store.on('add', this.onAdd, this);
6203 this.store.on("clear", this.clear, this);
6205 this.el.on("contextmenu", this.onContextMenu, this);
6207 this.mainBody.on('scroll', this.onBodyScroll, this);
6209 this.cm.on("headerchange", this.onHeaderChange, this);
6211 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6215 onContextMenu : function(e, t)
6217 this.processEvent("contextmenu", e);
6220 processEvent : function(name, e)
6222 if (name != 'touchstart' ) {
6223 this.fireEvent(name, e);
6226 var t = e.getTarget();
6228 var cell = Roo.get(t);
6234 if(cell.findParent('tfoot', false, true)){
6238 if(cell.findParent('thead', false, true)){
6240 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6241 cell = Roo.get(t).findParent('th', false, true);
6243 Roo.log("failed to find th in thead?");
6244 Roo.log(e.getTarget());
6249 var cellIndex = cell.dom.cellIndex;
6251 var ename = name == 'touchstart' ? 'click' : name;
6252 this.fireEvent("header" + ename, this, cellIndex, e);
6257 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6258 cell = Roo.get(t).findParent('td', false, true);
6260 Roo.log("failed to find th in tbody?");
6261 Roo.log(e.getTarget());
6266 var row = cell.findParent('tr', false, true);
6267 var cellIndex = cell.dom.cellIndex;
6268 var rowIndex = row.dom.rowIndex - 1;
6272 this.fireEvent("row" + name, this, rowIndex, e);
6276 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6282 onMouseover : function(e, el)
6284 var cell = Roo.get(el);
6290 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6291 cell = cell.findParent('td', false, true);
6294 var row = cell.findParent('tr', false, true);
6295 var cellIndex = cell.dom.cellIndex;
6296 var rowIndex = row.dom.rowIndex - 1; // start from 0
6298 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6302 onMouseout : function(e, el)
6304 var cell = Roo.get(el);
6310 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6311 cell = cell.findParent('td', false, true);
6314 var row = cell.findParent('tr', false, true);
6315 var cellIndex = cell.dom.cellIndex;
6316 var rowIndex = row.dom.rowIndex - 1; // start from 0
6318 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6322 onClick : function(e, el)
6324 var cell = Roo.get(el);
6326 if(!cell || (!this.cellSelection && !this.rowSelection)){
6330 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6331 cell = cell.findParent('td', false, true);
6334 if(!cell || typeof(cell) == 'undefined'){
6338 var row = cell.findParent('tr', false, true);
6340 if(!row || typeof(row) == 'undefined'){
6344 var cellIndex = cell.dom.cellIndex;
6345 var rowIndex = this.getRowIndex(row);
6347 // why??? - should these not be based on SelectionModel?
6348 if(this.cellSelection){
6349 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6352 if(this.rowSelection){
6353 this.fireEvent('rowclick', this, row, rowIndex, e);
6359 onDblClick : function(e,el)
6361 var cell = Roo.get(el);
6363 if(!cell || (!this.cellSelection && !this.rowSelection)){
6367 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6368 cell = cell.findParent('td', false, true);
6371 if(!cell || typeof(cell) == 'undefined'){
6375 var row = cell.findParent('tr', false, true);
6377 if(!row || typeof(row) == 'undefined'){
6381 var cellIndex = cell.dom.cellIndex;
6382 var rowIndex = this.getRowIndex(row);
6384 if(this.cellSelection){
6385 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6388 if(this.rowSelection){
6389 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6393 sort : function(e,el)
6395 var col = Roo.get(el);
6397 if(!col.hasClass('sortable')){
6401 var sort = col.attr('sort');
6404 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6408 this.store.sortInfo = {field : sort, direction : dir};
6411 Roo.log("calling footer first");
6412 this.footer.onClick('first');
6415 this.store.load({ params : { start : 0 } });
6419 renderHeader : function()
6427 this.totalWidth = 0;
6429 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6431 var config = cm.config[i];
6435 cls : 'x-hcol-' + i,
6437 html: cm.getColumnHeader(i)
6442 if(typeof(config.sortable) != 'undefined' && config.sortable){
6444 c.html = '<i class="glyphicon"></i>' + c.html;
6447 if(typeof(config.lgHeader) != 'undefined'){
6448 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6451 if(typeof(config.mdHeader) != 'undefined'){
6452 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6455 if(typeof(config.smHeader) != 'undefined'){
6456 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6459 if(typeof(config.xsHeader) != 'undefined'){
6460 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6467 if(typeof(config.tooltip) != 'undefined'){
6468 c.tooltip = config.tooltip;
6471 if(typeof(config.colspan) != 'undefined'){
6472 c.colspan = config.colspan;
6475 if(typeof(config.hidden) != 'undefined' && config.hidden){
6476 c.style += ' display:none;';
6479 if(typeof(config.dataIndex) != 'undefined'){
6480 c.sort = config.dataIndex;
6485 if(typeof(config.align) != 'undefined' && config.align.length){
6486 c.style += ' text-align:' + config.align + ';';
6489 if(typeof(config.width) != 'undefined'){
6490 c.style += ' width:' + config.width + 'px;';
6491 this.totalWidth += config.width;
6493 this.totalWidth += 100; // assume minimum of 100 per column?
6496 if(typeof(config.cls) != 'undefined'){
6497 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6500 ['xs','sm','md','lg'].map(function(size){
6502 if(typeof(config[size]) == 'undefined'){
6506 if (!config[size]) { // 0 = hidden
6507 c.cls += ' hidden-' + size;
6511 c.cls += ' col-' + size + '-' + config[size];
6521 renderBody : function()
6531 colspan : this.cm.getColumnCount()
6541 renderFooter : function()
6551 colspan : this.cm.getColumnCount()
6565 // Roo.log('ds onload');
6570 var ds = this.store;
6572 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6573 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6574 if (_this.store.sortInfo) {
6576 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6577 e.select('i', true).addClass(['glyphicon-arrow-up']);
6580 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6581 e.select('i', true).addClass(['glyphicon-arrow-down']);
6586 var tbody = this.mainBody;
6588 if(ds.getCount() > 0){
6589 ds.data.each(function(d,rowIndex){
6590 var row = this.renderRow(cm, ds, rowIndex);
6592 tbody.createChild(row);
6596 if(row.cellObjects.length){
6597 Roo.each(row.cellObjects, function(r){
6598 _this.renderCellObject(r);
6605 Roo.each(this.el.select('tbody td', true).elements, function(e){
6606 e.on('mouseover', _this.onMouseover, _this);
6609 Roo.each(this.el.select('tbody td', true).elements, function(e){
6610 e.on('mouseout', _this.onMouseout, _this);
6612 this.fireEvent('rowsrendered', this);
6618 onUpdate : function(ds,record)
6620 this.refreshRow(record);
6624 onRemove : function(ds, record, index, isUpdate){
6625 if(isUpdate !== true){
6626 this.fireEvent("beforerowremoved", this, index, record);
6628 var bt = this.mainBody.dom;
6630 var rows = this.el.select('tbody > tr', true).elements;
6632 if(typeof(rows[index]) != 'undefined'){
6633 bt.removeChild(rows[index].dom);
6636 // if(bt.rows[index]){
6637 // bt.removeChild(bt.rows[index]);
6640 if(isUpdate !== true){
6641 //this.stripeRows(index);
6642 //this.syncRowHeights(index, index);
6644 this.fireEvent("rowremoved", this, index, record);
6648 onAdd : function(ds, records, rowIndex)
6650 //Roo.log('on Add called');
6651 // - note this does not handle multiple adding very well..
6652 var bt = this.mainBody.dom;
6653 for (var i =0 ; i < records.length;i++) {
6654 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6655 //Roo.log(records[i]);
6656 //Roo.log(this.store.getAt(rowIndex+i));
6657 this.insertRow(this.store, rowIndex + i, false);
6664 refreshRow : function(record){
6665 var ds = this.store, index;
6666 if(typeof record == 'number'){
6668 record = ds.getAt(index);
6670 index = ds.indexOf(record);
6672 this.insertRow(ds, index, true);
6674 this.onRemove(ds, record, index+1, true);
6676 //this.syncRowHeights(index, index);
6678 this.fireEvent("rowupdated", this, index, record);
6681 insertRow : function(dm, rowIndex, isUpdate){
6684 this.fireEvent("beforerowsinserted", this, rowIndex);
6686 //var s = this.getScrollState();
6687 var row = this.renderRow(this.cm, this.store, rowIndex);
6688 // insert before rowIndex..
6689 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6693 if(row.cellObjects.length){
6694 Roo.each(row.cellObjects, function(r){
6695 _this.renderCellObject(r);
6700 this.fireEvent("rowsinserted", this, rowIndex);
6701 //this.syncRowHeights(firstRow, lastRow);
6702 //this.stripeRows(firstRow);
6709 getRowDom : function(rowIndex)
6711 var rows = this.el.select('tbody > tr', true).elements;
6713 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6716 // returns the object tree for a tr..
6719 renderRow : function(cm, ds, rowIndex)
6721 var d = ds.getAt(rowIndex);
6725 cls : 'x-row-' + rowIndex,
6729 var cellObjects = [];
6731 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6732 var config = cm.config[i];
6734 var renderer = cm.getRenderer(i);
6738 if(typeof(renderer) !== 'undefined'){
6739 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6741 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6742 // and are rendered into the cells after the row is rendered - using the id for the element.
6744 if(typeof(value) === 'object'){
6754 rowIndex : rowIndex,
6759 this.fireEvent('rowclass', this, rowcfg);
6763 cls : rowcfg.rowClass + ' x-col-' + i,
6765 html: (typeof(value) === 'object') ? '' : value
6772 if(typeof(config.colspan) != 'undefined'){
6773 td.colspan = config.colspan;
6776 if(typeof(config.hidden) != 'undefined' && config.hidden){
6777 td.style += ' display:none;';
6780 if(typeof(config.align) != 'undefined' && config.align.length){
6781 td.style += ' text-align:' + config.align + ';';
6784 if(typeof(config.width) != 'undefined'){
6785 td.style += ' width:' + config.width + 'px;';
6788 if(typeof(config.cursor) != 'undefined'){
6789 td.style += ' cursor:' + config.cursor + ';';
6792 if(typeof(config.cls) != 'undefined'){
6793 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6796 ['xs','sm','md','lg'].map(function(size){
6798 if(typeof(config[size]) == 'undefined'){
6802 if (!config[size]) { // 0 = hidden
6803 td.cls += ' hidden-' + size;
6807 td.cls += ' col-' + size + '-' + config[size];
6815 row.cellObjects = cellObjects;
6823 onBeforeLoad : function()
6832 this.el.select('tbody', true).first().dom.innerHTML = '';
6835 * Show or hide a row.
6836 * @param {Number} rowIndex to show or hide
6837 * @param {Boolean} state hide
6839 setRowVisibility : function(rowIndex, state)
6841 var bt = this.mainBody.dom;
6843 var rows = this.el.select('tbody > tr', true).elements;
6845 if(typeof(rows[rowIndex]) == 'undefined'){
6848 rows[rowIndex].dom.style.display = state ? '' : 'none';
6852 getSelectionModel : function(){
6854 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6856 return this.selModel;
6859 * Render the Roo.bootstrap object from renderder
6861 renderCellObject : function(r)
6865 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6867 var t = r.cfg.render(r.container);
6870 Roo.each(r.cfg.cn, function(c){
6872 container: t.getChildContainer(),
6875 _this.renderCellObject(child);
6880 getRowIndex : function(row)
6884 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6895 * Returns the grid's underlying element = used by panel.Grid
6896 * @return {Element} The element
6898 getGridEl : function(){
6902 * Forces a resize - used by panel.Grid
6903 * @return {Element} The element
6905 autoSize : function()
6907 //var ctr = Roo.get(this.container.dom.parentElement);
6908 var ctr = Roo.get(this.el.dom);
6910 var thd = this.getGridEl().select('thead',true).first();
6911 var tbd = this.getGridEl().select('tbody', true).first();
6912 var tfd = this.getGridEl().select('tfoot', true).first();
6914 var cw = ctr.getWidth();
6918 tbd.setSize(ctr.getWidth(),
6919 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6921 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6924 cw = Math.max(cw, this.totalWidth);
6925 this.getGridEl().select('tr',true).setWidth(cw);
6926 // resize 'expandable coloumn?
6928 return; // we doe not have a view in this design..
6931 onBodyScroll: function()
6933 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6935 this.mainHead.setStyle({
6936 'position' : 'relative',
6937 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6943 var scrollHeight = this.mainBody.dom.scrollHeight;
6945 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6947 var height = this.mainBody.getHeight();
6949 if(scrollHeight - height == scrollTop) {
6951 var total = this.ds.getTotalCount();
6953 if(this.footer.cursor + this.footer.pageSize < total){
6955 this.footer.ds.load({
6957 start : this.footer.cursor + this.footer.pageSize,
6958 limit : this.footer.pageSize
6968 onHeaderChange : function()
6970 var header = this.renderHeader();
6971 var table = this.el.select('table', true).first();
6973 this.mainHead.remove();
6974 this.mainHead = table.createChild(header, this.mainBody, false);
6977 onHiddenChange : function(colModel, colIndex, hidden)
6979 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6980 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6982 this.CSS.updateRule(thSelector, "display", "");
6983 this.CSS.updateRule(tdSelector, "display", "");
6986 this.CSS.updateRule(thSelector, "display", "none");
6987 this.CSS.updateRule(tdSelector, "display", "none");
6990 this.onHeaderChange();
7007 * @class Roo.bootstrap.TableCell
7008 * @extends Roo.bootstrap.Component
7009 * Bootstrap TableCell class
7010 * @cfg {String} html cell contain text
7011 * @cfg {String} cls cell class
7012 * @cfg {String} tag cell tag (td|th) default td
7013 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7014 * @cfg {String} align Aligns the content in a cell
7015 * @cfg {String} axis Categorizes cells
7016 * @cfg {String} bgcolor Specifies the background color of a cell
7017 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7018 * @cfg {Number} colspan Specifies the number of columns a cell should span
7019 * @cfg {String} headers Specifies one or more header cells a cell is related to
7020 * @cfg {Number} height Sets the height of a cell
7021 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7022 * @cfg {Number} rowspan Sets the number of rows a cell should span
7023 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7024 * @cfg {String} valign Vertical aligns the content in a cell
7025 * @cfg {Number} width Specifies the width of a cell
7028 * Create a new TableCell
7029 * @param {Object} config The config object
7032 Roo.bootstrap.TableCell = function(config){
7033 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7036 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7056 getAutoCreate : function(){
7057 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7077 cfg.align=this.align
7083 cfg.bgcolor=this.bgcolor
7086 cfg.charoff=this.charoff
7089 cfg.colspan=this.colspan
7092 cfg.headers=this.headers
7095 cfg.height=this.height
7098 cfg.nowrap=this.nowrap
7101 cfg.rowspan=this.rowspan
7104 cfg.scope=this.scope
7107 cfg.valign=this.valign
7110 cfg.width=this.width
7129 * @class Roo.bootstrap.TableRow
7130 * @extends Roo.bootstrap.Component
7131 * Bootstrap TableRow class
7132 * @cfg {String} cls row class
7133 * @cfg {String} align Aligns the content in a table row
7134 * @cfg {String} bgcolor Specifies a background color for a table row
7135 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7136 * @cfg {String} valign Vertical aligns the content in a table row
7139 * Create a new TableRow
7140 * @param {Object} config The config object
7143 Roo.bootstrap.TableRow = function(config){
7144 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7147 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7155 getAutoCreate : function(){
7156 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7166 cfg.align = this.align;
7169 cfg.bgcolor = this.bgcolor;
7172 cfg.charoff = this.charoff;
7175 cfg.valign = this.valign;
7193 * @class Roo.bootstrap.TableBody
7194 * @extends Roo.bootstrap.Component
7195 * Bootstrap TableBody class
7196 * @cfg {String} cls element class
7197 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7198 * @cfg {String} align Aligns the content inside the element
7199 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7200 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7203 * Create a new TableBody
7204 * @param {Object} config The config object
7207 Roo.bootstrap.TableBody = function(config){
7208 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7211 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7219 getAutoCreate : function(){
7220 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7234 cfg.align = this.align;
7237 cfg.charoff = this.charoff;
7240 cfg.valign = this.valign;
7247 // initEvents : function()
7254 // this.store = Roo.factory(this.store, Roo.data);
7255 // this.store.on('load', this.onLoad, this);
7257 // this.store.load();
7261 // onLoad: function ()
7263 // this.fireEvent('load', this);
7273 * Ext JS Library 1.1.1
7274 * Copyright(c) 2006-2007, Ext JS, LLC.
7276 * Originally Released Under LGPL - original licence link has changed is not relivant.
7279 * <script type="text/javascript">
7282 // as we use this in bootstrap.
7283 Roo.namespace('Roo.form');
7285 * @class Roo.form.Action
7286 * Internal Class used to handle form actions
7288 * @param {Roo.form.BasicForm} el The form element or its id
7289 * @param {Object} config Configuration options
7294 // define the action interface
7295 Roo.form.Action = function(form, options){
7297 this.options = options || {};
7300 * Client Validation Failed
7303 Roo.form.Action.CLIENT_INVALID = 'client';
7305 * Server Validation Failed
7308 Roo.form.Action.SERVER_INVALID = 'server';
7310 * Connect to Server Failed
7313 Roo.form.Action.CONNECT_FAILURE = 'connect';
7315 * Reading Data from Server Failed
7318 Roo.form.Action.LOAD_FAILURE = 'load';
7320 Roo.form.Action.prototype = {
7322 failureType : undefined,
7323 response : undefined,
7327 run : function(options){
7332 success : function(response){
7337 handleResponse : function(response){
7341 // default connection failure
7342 failure : function(response){
7344 this.response = response;
7345 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7346 this.form.afterAction(this, false);
7349 processResponse : function(response){
7350 this.response = response;
7351 if(!response.responseText){
7354 this.result = this.handleResponse(response);
7358 // utility functions used internally
7359 getUrl : function(appendParams){
7360 var url = this.options.url || this.form.url || this.form.el.dom.action;
7362 var p = this.getParams();
7364 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7370 getMethod : function(){
7371 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7374 getParams : function(){
7375 var bp = this.form.baseParams;
7376 var p = this.options.params;
7378 if(typeof p == "object"){
7379 p = Roo.urlEncode(Roo.applyIf(p, bp));
7380 }else if(typeof p == 'string' && bp){
7381 p += '&' + Roo.urlEncode(bp);
7384 p = Roo.urlEncode(bp);
7389 createCallback : function(){
7391 success: this.success,
7392 failure: this.failure,
7394 timeout: (this.form.timeout*1000),
7395 upload: this.form.fileUpload ? this.success : undefined
7400 Roo.form.Action.Submit = function(form, options){
7401 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7404 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7407 haveProgress : false,
7408 uploadComplete : false,
7410 // uploadProgress indicator.
7411 uploadProgress : function()
7413 if (!this.form.progressUrl) {
7417 if (!this.haveProgress) {
7418 Roo.MessageBox.progress("Uploading", "Uploading");
7420 if (this.uploadComplete) {
7421 Roo.MessageBox.hide();
7425 this.haveProgress = true;
7427 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7429 var c = new Roo.data.Connection();
7431 url : this.form.progressUrl,
7436 success : function(req){
7437 //console.log(data);
7441 rdata = Roo.decode(req.responseText)
7443 Roo.log("Invalid data from server..");
7447 if (!rdata || !rdata.success) {
7449 Roo.MessageBox.alert(Roo.encode(rdata));
7452 var data = rdata.data;
7454 if (this.uploadComplete) {
7455 Roo.MessageBox.hide();
7460 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7461 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7464 this.uploadProgress.defer(2000,this);
7467 failure: function(data) {
7468 Roo.log('progress url failed ');
7479 // run get Values on the form, so it syncs any secondary forms.
7480 this.form.getValues();
7482 var o = this.options;
7483 var method = this.getMethod();
7484 var isPost = method == 'POST';
7485 if(o.clientValidation === false || this.form.isValid()){
7487 if (this.form.progressUrl) {
7488 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7489 (new Date() * 1) + '' + Math.random());
7494 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7495 form:this.form.el.dom,
7496 url:this.getUrl(!isPost),
7498 params:isPost ? this.getParams() : null,
7499 isUpload: this.form.fileUpload
7502 this.uploadProgress();
7504 }else if (o.clientValidation !== false){ // client validation failed
7505 this.failureType = Roo.form.Action.CLIENT_INVALID;
7506 this.form.afterAction(this, false);
7510 success : function(response)
7512 this.uploadComplete= true;
7513 if (this.haveProgress) {
7514 Roo.MessageBox.hide();
7518 var result = this.processResponse(response);
7519 if(result === true || result.success){
7520 this.form.afterAction(this, true);
7524 this.form.markInvalid(result.errors);
7525 this.failureType = Roo.form.Action.SERVER_INVALID;
7527 this.form.afterAction(this, false);
7529 failure : function(response)
7531 this.uploadComplete= true;
7532 if (this.haveProgress) {
7533 Roo.MessageBox.hide();
7536 this.response = response;
7537 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7538 this.form.afterAction(this, false);
7541 handleResponse : function(response){
7542 if(this.form.errorReader){
7543 var rs = this.form.errorReader.read(response);
7546 for(var i = 0, len = rs.records.length; i < len; i++) {
7547 var r = rs.records[i];
7551 if(errors.length < 1){
7555 success : rs.success,
7561 ret = Roo.decode(response.responseText);
7565 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7575 Roo.form.Action.Load = function(form, options){
7576 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7577 this.reader = this.form.reader;
7580 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7585 Roo.Ajax.request(Roo.apply(
7586 this.createCallback(), {
7587 method:this.getMethod(),
7588 url:this.getUrl(false),
7589 params:this.getParams()
7593 success : function(response){
7595 var result = this.processResponse(response);
7596 if(result === true || !result.success || !result.data){
7597 this.failureType = Roo.form.Action.LOAD_FAILURE;
7598 this.form.afterAction(this, false);
7601 this.form.clearInvalid();
7602 this.form.setValues(result.data);
7603 this.form.afterAction(this, true);
7606 handleResponse : function(response){
7607 if(this.form.reader){
7608 var rs = this.form.reader.read(response);
7609 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7611 success : rs.success,
7615 return Roo.decode(response.responseText);
7619 Roo.form.Action.ACTION_TYPES = {
7620 'load' : Roo.form.Action.Load,
7621 'submit' : Roo.form.Action.Submit
7630 * @class Roo.bootstrap.Form
7631 * @extends Roo.bootstrap.Component
7632 * Bootstrap Form class
7633 * @cfg {String} method GET | POST (default POST)
7634 * @cfg {String} labelAlign top | left (default top)
7635 * @cfg {String} align left | right - for navbars
7636 * @cfg {Boolean} loadMask load mask when submit (default true)
7641 * @param {Object} config The config object
7645 Roo.bootstrap.Form = function(config){
7647 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7649 Roo.bootstrap.Form.popover.apply();
7653 * @event clientvalidation
7654 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7655 * @param {Form} this
7656 * @param {Boolean} valid true if the form has passed client-side validation
7658 clientvalidation: true,
7660 * @event beforeaction
7661 * Fires before any action is performed. Return false to cancel the action.
7662 * @param {Form} this
7663 * @param {Action} action The action to be performed
7667 * @event actionfailed
7668 * Fires when an action fails.
7669 * @param {Form} this
7670 * @param {Action} action The action that failed
7672 actionfailed : true,
7674 * @event actioncomplete
7675 * Fires when an action is completed.
7676 * @param {Form} this
7677 * @param {Action} action The action that completed
7679 actioncomplete : true
7683 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7686 * @cfg {String} method
7687 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7692 * The URL to use for form actions if one isn't supplied in the action options.
7695 * @cfg {Boolean} fileUpload
7696 * Set to true if this form is a file upload.
7700 * @cfg {Object} baseParams
7701 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7705 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7709 * @cfg {Sting} align (left|right) for navbar forms
7714 activeAction : null,
7717 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7718 * element by passing it or its id or mask the form itself by passing in true.
7721 waitMsgTarget : false,
7726 * @cfg {Boolean} errorMask (true|false) default false
7731 * @cfg {Number} maskOffset Default 100
7736 * @cfg {Boolean} maskBody
7740 getAutoCreate : function(){
7744 method : this.method || 'POST',
7745 id : this.id || Roo.id(),
7748 if (this.parent().xtype.match(/^Nav/)) {
7749 cfg.cls = 'navbar-form navbar-' + this.align;
7753 if (this.labelAlign == 'left' ) {
7754 cfg.cls += ' form-horizontal';
7760 initEvents : function()
7762 this.el.on('submit', this.onSubmit, this);
7763 // this was added as random key presses on the form where triggering form submit.
7764 this.el.on('keypress', function(e) {
7765 if (e.getCharCode() != 13) {
7768 // we might need to allow it for textareas.. and some other items.
7769 // check e.getTarget().
7771 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7775 Roo.log("keypress blocked");
7783 onSubmit : function(e){
7788 * Returns true if client-side validation on the form is successful.
7791 isValid : function(){
7792 var items = this.getItems();
7796 items.each(function(f){
7804 if(!target && f.el.isVisible(true)){
7810 if(this.errorMask && !valid){
7811 Roo.bootstrap.Form.popover.mask(this, target);
7818 * Returns true if any fields in this form have changed since their original load.
7821 isDirty : function(){
7823 var items = this.getItems();
7824 items.each(function(f){
7834 * Performs a predefined action (submit or load) or custom actions you define on this form.
7835 * @param {String} actionName The name of the action type
7836 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7837 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7838 * accept other config options):
7840 Property Type Description
7841 ---------------- --------------- ----------------------------------------------------------------------------------
7842 url String The url for the action (defaults to the form's url)
7843 method String The form method to use (defaults to the form's method, or POST if not defined)
7844 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7845 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7846 validate the form on the client (defaults to false)
7848 * @return {BasicForm} this
7850 doAction : function(action, options){
7851 if(typeof action == 'string'){
7852 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7854 if(this.fireEvent('beforeaction', this, action) !== false){
7855 this.beforeAction(action);
7856 action.run.defer(100, action);
7862 beforeAction : function(action){
7863 var o = action.options;
7868 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7870 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7873 // not really supported yet.. ??
7875 //if(this.waitMsgTarget === true){
7876 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7877 //}else if(this.waitMsgTarget){
7878 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7879 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7881 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7887 afterAction : function(action, success){
7888 this.activeAction = null;
7889 var o = action.options;
7894 Roo.get(document.body).unmask();
7900 //if(this.waitMsgTarget === true){
7901 // this.el.unmask();
7902 //}else if(this.waitMsgTarget){
7903 // this.waitMsgTarget.unmask();
7905 // Roo.MessageBox.updateProgress(1);
7906 // Roo.MessageBox.hide();
7913 Roo.callback(o.success, o.scope, [this, action]);
7914 this.fireEvent('actioncomplete', this, action);
7918 // failure condition..
7919 // we have a scenario where updates need confirming.
7920 // eg. if a locking scenario exists..
7921 // we look for { errors : { needs_confirm : true }} in the response.
7923 (typeof(action.result) != 'undefined') &&
7924 (typeof(action.result.errors) != 'undefined') &&
7925 (typeof(action.result.errors.needs_confirm) != 'undefined')
7928 Roo.log("not supported yet");
7931 Roo.MessageBox.confirm(
7932 "Change requires confirmation",
7933 action.result.errorMsg,
7938 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7948 Roo.callback(o.failure, o.scope, [this, action]);
7949 // show an error message if no failed handler is set..
7950 if (!this.hasListener('actionfailed')) {
7951 Roo.log("need to add dialog support");
7953 Roo.MessageBox.alert("Error",
7954 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7955 action.result.errorMsg :
7956 "Saving Failed, please check your entries or try again"
7961 this.fireEvent('actionfailed', this, action);
7966 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7967 * @param {String} id The value to search for
7970 findField : function(id){
7971 var items = this.getItems();
7972 var field = items.get(id);
7974 items.each(function(f){
7975 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7982 return field || null;
7985 * Mark fields in this form invalid in bulk.
7986 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7987 * @return {BasicForm} this
7989 markInvalid : function(errors){
7990 if(errors instanceof Array){
7991 for(var i = 0, len = errors.length; i < len; i++){
7992 var fieldError = errors[i];
7993 var f = this.findField(fieldError.id);
7995 f.markInvalid(fieldError.msg);
8001 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8002 field.markInvalid(errors[id]);
8006 //Roo.each(this.childForms || [], function (f) {
8007 // f.markInvalid(errors);
8014 * Set values for fields in this form in bulk.
8015 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8016 * @return {BasicForm} this
8018 setValues : function(values){
8019 if(values instanceof Array){ // array of objects
8020 for(var i = 0, len = values.length; i < len; i++){
8022 var f = this.findField(v.id);
8024 f.setValue(v.value);
8025 if(this.trackResetOnLoad){
8026 f.originalValue = f.getValue();
8030 }else{ // object hash
8033 if(typeof values[id] != 'function' && (field = this.findField(id))){
8035 if (field.setFromData &&
8037 field.displayField &&
8038 // combos' with local stores can
8039 // be queried via setValue()
8040 // to set their value..
8041 (field.store && !field.store.isLocal)
8045 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8046 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8047 field.setFromData(sd);
8049 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8051 field.setFromData(values);
8054 field.setValue(values[id]);
8058 if(this.trackResetOnLoad){
8059 field.originalValue = field.getValue();
8065 //Roo.each(this.childForms || [], function (f) {
8066 // f.setValues(values);
8073 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8074 * they are returned as an array.
8075 * @param {Boolean} asString
8078 getValues : function(asString){
8079 //if (this.childForms) {
8080 // copy values from the child forms
8081 // Roo.each(this.childForms, function (f) {
8082 // this.setValues(f.getValues());
8088 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8089 if(asString === true){
8092 return Roo.urlDecode(fs);
8096 * Returns the fields in this form as an object with key/value pairs.
8097 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8100 getFieldValues : function(with_hidden)
8102 var items = this.getItems();
8104 items.each(function(f){
8110 var v = f.getValue();
8112 if (f.inputType =='radio') {
8113 if (typeof(ret[f.getName()]) == 'undefined') {
8114 ret[f.getName()] = ''; // empty..
8117 if (!f.el.dom.checked) {
8125 if(f.xtype == 'MoneyField'){
8126 ret[f.currencyName] = f.getCurrency();
8129 // not sure if this supported any more..
8130 if ((typeof(v) == 'object') && f.getRawValue) {
8131 v = f.getRawValue() ; // dates..
8133 // combo boxes where name != hiddenName...
8134 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8135 ret[f.name] = f.getRawValue();
8137 ret[f.getName()] = v;
8144 * Clears all invalid messages in this form.
8145 * @return {BasicForm} this
8147 clearInvalid : function(){
8148 var items = this.getItems();
8150 items.each(function(f){
8159 * @return {BasicForm} this
8162 var items = this.getItems();
8163 items.each(function(f){
8167 Roo.each(this.childForms || [], function (f) {
8175 getItems : function()
8177 var r=new Roo.util.MixedCollection(false, function(o){
8178 return o.id || (o.id = Roo.id());
8180 var iter = function(el) {
8187 Roo.each(el.items,function(e) {
8196 hideFields : function(items)
8198 Roo.each(items, function(i){
8200 var f = this.findField(i);
8206 if(f.xtype == 'DateField'){
8207 f.setVisible(false);
8216 showFields : function(items)
8218 Roo.each(items, function(i){
8220 var f = this.findField(i);
8226 if(f.xtype == 'DateField'){
8238 Roo.apply(Roo.bootstrap.Form, {
8265 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8266 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8267 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8268 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8271 this.maskEl.top.enableDisplayMode("block");
8272 this.maskEl.left.enableDisplayMode("block");
8273 this.maskEl.bottom.enableDisplayMode("block");
8274 this.maskEl.right.enableDisplayMode("block");
8276 this.toolTip = new Roo.bootstrap.Tooltip({
8277 cls : 'roo-form-error-popover',
8279 'left' : ['r-l', [-2,0], 'right'],
8280 'right' : ['l-r', [2,0], 'left'],
8281 'bottom' : ['tl-bl', [0,2], 'top'],
8282 'top' : [ 'bl-tl', [0,-2], 'bottom']
8286 this.toolTip.render(Roo.get(document.body));
8288 this.toolTip.el.enableDisplayMode("block");
8290 Roo.get(document.body).on('click', function(){
8294 Roo.get(document.body).on('touchstart', function(){
8298 this.isApplied = true
8301 mask : function(form, target)
8305 this.target = target;
8307 if(!this.form.errorMask || !target.el){
8311 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8313 Roo.log(scrollable);
8315 var ot = this.target.el.calcOffsetsTo(scrollable);
8317 var scrollTo = ot[1] - this.form.maskOffset;
8319 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8321 scrollable.scrollTo('top', scrollTo);
8323 var box = this.target.el.getBox();
8325 var zIndex = Roo.bootstrap.Modal.zIndex++;
8328 this.maskEl.top.setStyle('position', 'absolute');
8329 this.maskEl.top.setStyle('z-index', zIndex);
8330 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8331 this.maskEl.top.setLeft(0);
8332 this.maskEl.top.setTop(0);
8333 this.maskEl.top.show();
8335 this.maskEl.left.setStyle('position', 'absolute');
8336 this.maskEl.left.setStyle('z-index', zIndex);
8337 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8338 this.maskEl.left.setLeft(0);
8339 this.maskEl.left.setTop(box.y - this.padding);
8340 this.maskEl.left.show();
8342 this.maskEl.bottom.setStyle('position', 'absolute');
8343 this.maskEl.bottom.setStyle('z-index', zIndex);
8344 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8345 this.maskEl.bottom.setLeft(0);
8346 this.maskEl.bottom.setTop(box.bottom + this.padding);
8347 this.maskEl.bottom.show();
8349 this.maskEl.right.setStyle('position', 'absolute');
8350 this.maskEl.right.setStyle('z-index', zIndex);
8351 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8352 this.maskEl.right.setLeft(box.right + this.padding);
8353 this.maskEl.right.setTop(box.y - this.padding);
8354 this.maskEl.right.show();
8356 this.toolTip.bindEl = this.target.el;
8358 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8360 var tip = this.target.blankText;
8362 if(this.target.getValue() !== '' ) {
8364 if (this.target.invalidText.length) {
8365 tip = this.target.invalidText;
8366 } else if (this.target.regexText.length){
8367 tip = this.target.regexText;
8371 this.toolTip.show(tip);
8373 this.intervalID = window.setInterval(function() {
8374 Roo.bootstrap.Form.popover.unmask();
8377 window.onwheel = function(){ return false;};
8379 (function(){ this.isMasked = true; }).defer(500, this);
8385 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8389 this.maskEl.top.setStyle('position', 'absolute');
8390 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8391 this.maskEl.top.hide();
8393 this.maskEl.left.setStyle('position', 'absolute');
8394 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8395 this.maskEl.left.hide();
8397 this.maskEl.bottom.setStyle('position', 'absolute');
8398 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8399 this.maskEl.bottom.hide();
8401 this.maskEl.right.setStyle('position', 'absolute');
8402 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8403 this.maskEl.right.hide();
8405 this.toolTip.hide();
8407 this.toolTip.el.hide();
8409 window.onwheel = function(){ return true;};
8411 if(this.intervalID){
8412 window.clearInterval(this.intervalID);
8413 this.intervalID = false;
8416 this.isMasked = false;
8426 * Ext JS Library 1.1.1
8427 * Copyright(c) 2006-2007, Ext JS, LLC.
8429 * Originally Released Under LGPL - original licence link has changed is not relivant.
8432 * <script type="text/javascript">
8435 * @class Roo.form.VTypes
8436 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8439 Roo.form.VTypes = function(){
8440 // closure these in so they are only created once.
8441 var alpha = /^[a-zA-Z_]+$/;
8442 var alphanum = /^[a-zA-Z0-9_]+$/;
8443 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8444 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8446 // All these messages and functions are configurable
8449 * The function used to validate email addresses
8450 * @param {String} value The email address
8452 'email' : function(v){
8453 return email.test(v);
8456 * The error text to display when the email validation function returns false
8459 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8461 * The keystroke filter mask to be applied on email input
8464 'emailMask' : /[a-z0-9_\.\-@]/i,
8467 * The function used to validate URLs
8468 * @param {String} value The URL
8470 'url' : function(v){
8474 * The error text to display when the url validation function returns false
8477 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8480 * The function used to validate alpha values
8481 * @param {String} value The value
8483 'alpha' : function(v){
8484 return alpha.test(v);
8487 * The error text to display when the alpha validation function returns false
8490 'alphaText' : 'This field should only contain letters and _',
8492 * The keystroke filter mask to be applied on alpha input
8495 'alphaMask' : /[a-z_]/i,
8498 * The function used to validate alphanumeric values
8499 * @param {String} value The value
8501 'alphanum' : function(v){
8502 return alphanum.test(v);
8505 * The error text to display when the alphanumeric validation function returns false
8508 'alphanumText' : 'This field should only contain letters, numbers and _',
8510 * The keystroke filter mask to be applied on alphanumeric input
8513 'alphanumMask' : /[a-z0-9_]/i
8523 * @class Roo.bootstrap.Input
8524 * @extends Roo.bootstrap.Component
8525 * Bootstrap Input class
8526 * @cfg {Boolean} disabled is it disabled
8527 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8528 * @cfg {String} name name of the input
8529 * @cfg {string} fieldLabel - the label associated
8530 * @cfg {string} placeholder - placeholder to put in text.
8531 * @cfg {string} before - input group add on before
8532 * @cfg {string} after - input group add on after
8533 * @cfg {string} size - (lg|sm) or leave empty..
8534 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8535 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8536 * @cfg {Number} md colspan out of 12 for computer-sized screens
8537 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8538 * @cfg {string} value default value of the input
8539 * @cfg {Number} labelWidth set the width of label
8540 * @cfg {Number} labellg set the width of label (1-12)
8541 * @cfg {Number} labelmd set the width of label (1-12)
8542 * @cfg {Number} labelsm set the width of label (1-12)
8543 * @cfg {Number} labelxs set the width of label (1-12)
8544 * @cfg {String} labelAlign (top|left)
8545 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8546 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8547 * @cfg {String} indicatorpos (left|right) default left
8548 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8549 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8551 * @cfg {String} align (left|center|right) Default left
8552 * @cfg {Boolean} forceFeedback (true|false) Default false
8555 * Create a new Input
8556 * @param {Object} config The config object
8559 Roo.bootstrap.Input = function(config){
8561 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8566 * Fires when this field receives input focus.
8567 * @param {Roo.form.Field} this
8572 * Fires when this field loses input focus.
8573 * @param {Roo.form.Field} this
8578 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8579 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8580 * @param {Roo.form.Field} this
8581 * @param {Roo.EventObject} e The event object
8586 * Fires just before the field blurs if the field value has changed.
8587 * @param {Roo.form.Field} this
8588 * @param {Mixed} newValue The new value
8589 * @param {Mixed} oldValue The original value
8594 * Fires after the field has been marked as invalid.
8595 * @param {Roo.form.Field} this
8596 * @param {String} msg The validation message
8601 * Fires after the field has been validated with no errors.
8602 * @param {Roo.form.Field} this
8607 * Fires after the key up
8608 * @param {Roo.form.Field} this
8609 * @param {Roo.EventObject} e The event Object
8615 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8617 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8618 automatic validation (defaults to "keyup").
8620 validationEvent : "keyup",
8622 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8624 validateOnBlur : true,
8626 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8628 validationDelay : 250,
8630 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8632 focusClass : "x-form-focus", // not needed???
8636 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8638 invalidClass : "has-warning",
8641 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8643 validClass : "has-success",
8646 * @cfg {Boolean} hasFeedback (true|false) default true
8651 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8653 invalidFeedbackClass : "glyphicon-warning-sign",
8656 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8658 validFeedbackClass : "glyphicon-ok",
8661 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8663 selectOnFocus : false,
8666 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8670 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8675 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8677 disableKeyFilter : false,
8680 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8684 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8688 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8690 blankText : "Please complete this mandatory field",
8693 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8697 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8699 maxLength : Number.MAX_VALUE,
8701 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8703 minLengthText : "The minimum length for this field is {0}",
8705 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8707 maxLengthText : "The maximum length for this field is {0}",
8711 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8712 * If available, this function will be called only after the basic validators all return true, and will be passed the
8713 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8717 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8718 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8719 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8723 * @cfg {String} regexText -- Depricated - use Invalid Text
8728 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8734 autocomplete: false,
8753 formatedValue : false,
8754 forceFeedback : false,
8756 indicatorpos : 'left',
8766 parentLabelAlign : function()
8769 while (parent.parent()) {
8770 parent = parent.parent();
8771 if (typeof(parent.labelAlign) !='undefined') {
8772 return parent.labelAlign;
8779 getAutoCreate : function()
8781 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8787 if(this.inputType != 'hidden'){
8788 cfg.cls = 'form-group' //input-group
8794 type : this.inputType,
8796 cls : 'form-control',
8797 placeholder : this.placeholder || '',
8798 autocomplete : this.autocomplete || 'new-password'
8801 if(this.capture.length){
8802 input.capture = this.capture;
8805 if(this.accept.length){
8806 input.accept = this.accept + "/*";
8810 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8813 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8814 input.maxLength = this.maxLength;
8817 if (this.disabled) {
8818 input.disabled=true;
8821 if (this.readOnly) {
8822 input.readonly=true;
8826 input.name = this.name;
8830 input.cls += ' input-' + this.size;
8834 ['xs','sm','md','lg'].map(function(size){
8835 if (settings[size]) {
8836 cfg.cls += ' col-' + size + '-' + settings[size];
8840 var inputblock = input;
8844 cls: 'glyphicon form-control-feedback'
8847 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8850 cls : 'has-feedback',
8858 if (this.before || this.after) {
8861 cls : 'input-group',
8865 if (this.before && typeof(this.before) == 'string') {
8867 inputblock.cn.push({
8869 cls : 'roo-input-before input-group-addon',
8873 if (this.before && typeof(this.before) == 'object') {
8874 this.before = Roo.factory(this.before);
8876 inputblock.cn.push({
8878 cls : 'roo-input-before input-group-' +
8879 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8883 inputblock.cn.push(input);
8885 if (this.after && typeof(this.after) == 'string') {
8886 inputblock.cn.push({
8888 cls : 'roo-input-after input-group-addon',
8892 if (this.after && typeof(this.after) == 'object') {
8893 this.after = Roo.factory(this.after);
8895 inputblock.cn.push({
8897 cls : 'roo-input-after input-group-' +
8898 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8902 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8903 inputblock.cls += ' has-feedback';
8904 inputblock.cn.push(feedback);
8908 if (align ==='left' && this.fieldLabel.length) {
8910 cfg.cls += ' roo-form-group-label-left';
8915 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8916 tooltip : 'This field is required'
8921 cls : 'control-label',
8922 html : this.fieldLabel
8933 var labelCfg = cfg.cn[1];
8934 var contentCfg = cfg.cn[2];
8936 if(this.indicatorpos == 'right'){
8941 cls : 'control-label',
8945 html : this.fieldLabel
8949 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8950 tooltip : 'This field is required'
8963 labelCfg = cfg.cn[0];
8964 contentCfg = cfg.cn[1];
8968 if(this.labelWidth > 12){
8969 labelCfg.style = "width: " + this.labelWidth + 'px';
8972 if(this.labelWidth < 13 && this.labelmd == 0){
8973 this.labelmd = this.labelWidth;
8976 if(this.labellg > 0){
8977 labelCfg.cls += ' col-lg-' + this.labellg;
8978 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8981 if(this.labelmd > 0){
8982 labelCfg.cls += ' col-md-' + this.labelmd;
8983 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8986 if(this.labelsm > 0){
8987 labelCfg.cls += ' col-sm-' + this.labelsm;
8988 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8991 if(this.labelxs > 0){
8992 labelCfg.cls += ' col-xs-' + this.labelxs;
8993 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8997 } else if ( this.fieldLabel.length) {
9002 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9003 tooltip : 'This field is required'
9007 //cls : 'input-group-addon',
9008 html : this.fieldLabel
9016 if(this.indicatorpos == 'right'){
9021 //cls : 'input-group-addon',
9022 html : this.fieldLabel
9027 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9028 tooltip : 'This field is required'
9048 if (this.parentType === 'Navbar' && this.parent().bar) {
9049 cfg.cls += ' navbar-form';
9052 if (this.parentType === 'NavGroup') {
9053 cfg.cls += ' navbar-form';
9061 * return the real input element.
9063 inputEl: function ()
9065 return this.el.select('input.form-control',true).first();
9068 tooltipEl : function()
9070 return this.inputEl();
9073 indicatorEl : function()
9075 var indicator = this.el.select('i.roo-required-indicator',true).first();
9085 setDisabled : function(v)
9087 var i = this.inputEl().dom;
9089 i.removeAttribute('disabled');
9093 i.setAttribute('disabled','true');
9095 initEvents : function()
9098 this.inputEl().on("keydown" , this.fireKey, this);
9099 this.inputEl().on("focus", this.onFocus, this);
9100 this.inputEl().on("blur", this.onBlur, this);
9102 this.inputEl().relayEvent('keyup', this);
9104 this.indicator = this.indicatorEl();
9107 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9110 // reference to original value for reset
9111 this.originalValue = this.getValue();
9112 //Roo.form.TextField.superclass.initEvents.call(this);
9113 if(this.validationEvent == 'keyup'){
9114 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9115 this.inputEl().on('keyup', this.filterValidation, this);
9117 else if(this.validationEvent !== false){
9118 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9121 if(this.selectOnFocus){
9122 this.on("focus", this.preFocus, this);
9125 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9126 this.inputEl().on("keypress", this.filterKeys, this);
9128 this.inputEl().relayEvent('keypress', this);
9131 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9132 this.el.on("click", this.autoSize, this);
9135 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9136 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9139 if (typeof(this.before) == 'object') {
9140 this.before.render(this.el.select('.roo-input-before',true).first());
9142 if (typeof(this.after) == 'object') {
9143 this.after.render(this.el.select('.roo-input-after',true).first());
9146 this.inputEl().on('change', this.onChange, this);
9149 filterValidation : function(e){
9150 if(!e.isNavKeyPress()){
9151 this.validationTask.delay(this.validationDelay);
9155 * Validates the field value
9156 * @return {Boolean} True if the value is valid, else false
9158 validate : function(){
9159 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9160 if(this.disabled || this.validateValue(this.getRawValue())){
9171 * Validates a value according to the field's validation rules and marks the field as invalid
9172 * if the validation fails
9173 * @param {Mixed} value The value to validate
9174 * @return {Boolean} True if the value is valid, else false
9176 validateValue : function(value)
9178 if(this.getVisibilityEl().hasClass('hidden')){
9182 if(value.length < 1) { // if it's blank
9183 if(this.allowBlank){
9189 if(value.length < this.minLength){
9192 if(value.length > this.maxLength){
9196 var vt = Roo.form.VTypes;
9197 if(!vt[this.vtype](value, this)){
9201 if(typeof this.validator == "function"){
9202 var msg = this.validator(value);
9206 if (typeof(msg) == 'string') {
9207 this.invalidText = msg;
9211 if(this.regex && !this.regex.test(value)){
9219 fireKey : function(e){
9220 //Roo.log('field ' + e.getKey());
9221 if(e.isNavKeyPress()){
9222 this.fireEvent("specialkey", this, e);
9225 focus : function (selectText){
9227 this.inputEl().focus();
9228 if(selectText === true){
9229 this.inputEl().dom.select();
9235 onFocus : function(){
9236 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9237 // this.el.addClass(this.focusClass);
9240 this.hasFocus = true;
9241 this.startValue = this.getValue();
9242 this.fireEvent("focus", this);
9246 beforeBlur : Roo.emptyFn,
9250 onBlur : function(){
9252 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9253 //this.el.removeClass(this.focusClass);
9255 this.hasFocus = false;
9256 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9259 var v = this.getValue();
9260 if(String(v) !== String(this.startValue)){
9261 this.fireEvent('change', this, v, this.startValue);
9263 this.fireEvent("blur", this);
9266 onChange : function(e)
9268 var v = this.getValue();
9269 if(String(v) !== String(this.startValue)){
9270 this.fireEvent('change', this, v, this.startValue);
9276 * Resets the current field value to the originally loaded value and clears any validation messages
9279 this.setValue(this.originalValue);
9283 * Returns the name of the field
9284 * @return {Mixed} name The name field
9286 getName: function(){
9290 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9291 * @return {Mixed} value The field value
9293 getValue : function(){
9295 var v = this.inputEl().getValue();
9300 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9301 * @return {Mixed} value The field value
9303 getRawValue : function(){
9304 var v = this.inputEl().getValue();
9310 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9311 * @param {Mixed} value The value to set
9313 setRawValue : function(v){
9314 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9317 selectText : function(start, end){
9318 var v = this.getRawValue();
9320 start = start === undefined ? 0 : start;
9321 end = end === undefined ? v.length : end;
9322 var d = this.inputEl().dom;
9323 if(d.setSelectionRange){
9324 d.setSelectionRange(start, end);
9325 }else if(d.createTextRange){
9326 var range = d.createTextRange();
9327 range.moveStart("character", start);
9328 range.moveEnd("character", v.length-end);
9335 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9336 * @param {Mixed} value The value to set
9338 setValue : function(v){
9341 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9347 processValue : function(value){
9348 if(this.stripCharsRe){
9349 var newValue = value.replace(this.stripCharsRe, '');
9350 if(newValue !== value){
9351 this.setRawValue(newValue);
9358 preFocus : function(){
9360 if(this.selectOnFocus){
9361 this.inputEl().dom.select();
9364 filterKeys : function(e){
9366 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9369 var c = e.getCharCode(), cc = String.fromCharCode(c);
9370 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9373 if(!this.maskRe.test(cc)){
9378 * Clear any invalid styles/messages for this field
9380 clearInvalid : function(){
9382 if(!this.el || this.preventMark){ // not rendered
9387 this.el.removeClass(this.invalidClass);
9389 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9391 var feedback = this.el.select('.form-control-feedback', true).first();
9394 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9399 this.fireEvent('valid', this);
9403 * Mark this field as valid
9405 markValid : function()
9407 if(!this.el || this.preventMark){ // not rendered...
9411 this.el.removeClass([this.invalidClass, this.validClass]);
9413 var feedback = this.el.select('.form-control-feedback', true).first();
9416 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9420 this.indicator.removeClass('visible');
9421 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9428 if(this.allowBlank && !this.getRawValue().length){
9432 this.el.addClass(this.validClass);
9434 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9436 var feedback = this.el.select('.form-control-feedback', true).first();
9439 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9440 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9445 this.fireEvent('valid', this);
9449 * Mark this field as invalid
9450 * @param {String} msg The validation message
9452 markInvalid : function(msg)
9454 if(!this.el || this.preventMark){ // not rendered
9458 this.el.removeClass([this.invalidClass, this.validClass]);
9460 var feedback = this.el.select('.form-control-feedback', true).first();
9463 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9470 if(this.allowBlank && !this.getRawValue().length){
9475 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9476 this.indicator.addClass('visible');
9479 this.el.addClass(this.invalidClass);
9481 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9483 var feedback = this.el.select('.form-control-feedback', true).first();
9486 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9488 if(this.getValue().length || this.forceFeedback){
9489 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9496 this.fireEvent('invalid', this, msg);
9499 SafariOnKeyDown : function(event)
9501 // this is a workaround for a password hang bug on chrome/ webkit.
9502 if (this.inputEl().dom.type != 'password') {
9506 var isSelectAll = false;
9508 if(this.inputEl().dom.selectionEnd > 0){
9509 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9511 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9512 event.preventDefault();
9517 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9519 event.preventDefault();
9520 // this is very hacky as keydown always get's upper case.
9522 var cc = String.fromCharCode(event.getCharCode());
9523 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9527 adjustWidth : function(tag, w){
9528 tag = tag.toLowerCase();
9529 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9530 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9534 if(tag == 'textarea'){
9537 }else if(Roo.isOpera){
9541 if(tag == 'textarea'){
9549 setFieldLabel : function(v)
9556 var ar = this.el.select('label > span',true);
9558 if (ar.elements.length) {
9559 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9560 this.fieldLabel = v;
9564 var br = this.el.select('label',true);
9566 if(br.elements.length) {
9567 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9568 this.fieldLabel = v;
9572 Roo.log('Cannot Found any of label > span || label in input');
9576 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9577 this.fieldLabel = v;
9592 * @class Roo.bootstrap.TextArea
9593 * @extends Roo.bootstrap.Input
9594 * Bootstrap TextArea class
9595 * @cfg {Number} cols Specifies the visible width of a text area
9596 * @cfg {Number} rows Specifies the visible number of lines in a text area
9597 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9598 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9599 * @cfg {string} html text
9602 * Create a new TextArea
9603 * @param {Object} config The config object
9606 Roo.bootstrap.TextArea = function(config){
9607 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9611 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9621 getAutoCreate : function(){
9623 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9629 if(this.inputType != 'hidden'){
9630 cfg.cls = 'form-group' //input-group
9638 value : this.value || '',
9639 html: this.html || '',
9640 cls : 'form-control',
9641 placeholder : this.placeholder || ''
9645 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9646 input.maxLength = this.maxLength;
9650 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9654 input.cols = this.cols;
9657 if (this.readOnly) {
9658 input.readonly = true;
9662 input.name = this.name;
9666 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9670 ['xs','sm','md','lg'].map(function(size){
9671 if (settings[size]) {
9672 cfg.cls += ' col-' + size + '-' + settings[size];
9676 var inputblock = input;
9678 if(this.hasFeedback && !this.allowBlank){
9682 cls: 'glyphicon form-control-feedback'
9686 cls : 'has-feedback',
9695 if (this.before || this.after) {
9698 cls : 'input-group',
9702 inputblock.cn.push({
9704 cls : 'input-group-addon',
9709 inputblock.cn.push(input);
9711 if(this.hasFeedback && !this.allowBlank){
9712 inputblock.cls += ' has-feedback';
9713 inputblock.cn.push(feedback);
9717 inputblock.cn.push({
9719 cls : 'input-group-addon',
9726 if (align ==='left' && this.fieldLabel.length) {
9731 cls : 'control-label',
9732 html : this.fieldLabel
9743 if(this.labelWidth > 12){
9744 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9747 if(this.labelWidth < 13 && this.labelmd == 0){
9748 this.labelmd = this.labelWidth;
9751 if(this.labellg > 0){
9752 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9753 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9756 if(this.labelmd > 0){
9757 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9758 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9761 if(this.labelsm > 0){
9762 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9763 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9766 if(this.labelxs > 0){
9767 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9768 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9771 } else if ( this.fieldLabel.length) {
9776 //cls : 'input-group-addon',
9777 html : this.fieldLabel
9795 if (this.disabled) {
9796 input.disabled=true;
9803 * return the real textarea element.
9805 inputEl: function ()
9807 return this.el.select('textarea.form-control',true).first();
9811 * Clear any invalid styles/messages for this field
9813 clearInvalid : function()
9816 if(!this.el || this.preventMark){ // not rendered
9820 var label = this.el.select('label', true).first();
9821 var icon = this.el.select('i.fa-star', true).first();
9827 this.el.removeClass(this.invalidClass);
9829 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9831 var feedback = this.el.select('.form-control-feedback', true).first();
9834 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9839 this.fireEvent('valid', this);
9843 * Mark this field as valid
9845 markValid : function()
9847 if(!this.el || this.preventMark){ // not rendered
9851 this.el.removeClass([this.invalidClass, this.validClass]);
9853 var feedback = this.el.select('.form-control-feedback', true).first();
9856 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9859 if(this.disabled || this.allowBlank){
9863 var label = this.el.select('label', true).first();
9864 var icon = this.el.select('i.fa-star', true).first();
9870 this.el.addClass(this.validClass);
9872 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9874 var feedback = this.el.select('.form-control-feedback', true).first();
9877 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9878 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9883 this.fireEvent('valid', this);
9887 * Mark this field as invalid
9888 * @param {String} msg The validation message
9890 markInvalid : function(msg)
9892 if(!this.el || this.preventMark){ // not rendered
9896 this.el.removeClass([this.invalidClass, this.validClass]);
9898 var feedback = this.el.select('.form-control-feedback', true).first();
9901 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9904 if(this.disabled || this.allowBlank){
9908 var label = this.el.select('label', true).first();
9909 var icon = this.el.select('i.fa-star', true).first();
9911 if(!this.getValue().length && label && !icon){
9912 this.el.createChild({
9914 cls : 'text-danger fa fa-lg fa-star',
9915 tooltip : 'This field is required',
9916 style : 'margin-right:5px;'
9920 this.el.addClass(this.invalidClass);
9922 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9924 var feedback = this.el.select('.form-control-feedback', true).first();
9927 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9929 if(this.getValue().length || this.forceFeedback){
9930 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9937 this.fireEvent('invalid', this, msg);
9945 * trigger field - base class for combo..
9950 * @class Roo.bootstrap.TriggerField
9951 * @extends Roo.bootstrap.Input
9952 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9953 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9954 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9955 * for which you can provide a custom implementation. For example:
9957 var trigger = new Roo.bootstrap.TriggerField();
9958 trigger.onTriggerClick = myTriggerFn;
9959 trigger.applyTo('my-field');
9962 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9963 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9964 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9965 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9966 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9969 * Create a new TriggerField.
9970 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9971 * to the base TextField)
9973 Roo.bootstrap.TriggerField = function(config){
9974 this.mimicing = false;
9975 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9978 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9980 * @cfg {String} triggerClass A CSS class to apply to the trigger
9983 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9988 * @cfg {Boolean} removable (true|false) special filter default false
9992 /** @cfg {Boolean} grow @hide */
9993 /** @cfg {Number} growMin @hide */
9994 /** @cfg {Number} growMax @hide */
10000 autoSize: Roo.emptyFn,
10004 deferHeight : true,
10007 actionMode : 'wrap',
10012 getAutoCreate : function(){
10014 var align = this.labelAlign || this.parentLabelAlign();
10019 cls: 'form-group' //input-group
10026 type : this.inputType,
10027 cls : 'form-control',
10028 autocomplete: 'new-password',
10029 placeholder : this.placeholder || ''
10033 input.name = this.name;
10036 input.cls += ' input-' + this.size;
10039 if (this.disabled) {
10040 input.disabled=true;
10043 var inputblock = input;
10045 if(this.hasFeedback && !this.allowBlank){
10049 cls: 'glyphicon form-control-feedback'
10052 if(this.removable && !this.editable && !this.tickable){
10054 cls : 'has-feedback',
10060 cls : 'roo-combo-removable-btn close'
10067 cls : 'has-feedback',
10076 if(this.removable && !this.editable && !this.tickable){
10078 cls : 'roo-removable',
10084 cls : 'roo-combo-removable-btn close'
10091 if (this.before || this.after) {
10094 cls : 'input-group',
10098 inputblock.cn.push({
10100 cls : 'input-group-addon',
10105 inputblock.cn.push(input);
10107 if(this.hasFeedback && !this.allowBlank){
10108 inputblock.cls += ' has-feedback';
10109 inputblock.cn.push(feedback);
10113 inputblock.cn.push({
10115 cls : 'input-group-addon',
10128 cls: 'form-hidden-field'
10142 cls: 'form-hidden-field'
10146 cls: 'roo-select2-choices',
10150 cls: 'roo-select2-search-field',
10163 cls: 'roo-select2-container input-group',
10168 // cls: 'typeahead typeahead-long dropdown-menu',
10169 // style: 'display:none'
10174 if(!this.multiple && this.showToggleBtn){
10180 if (this.caret != false) {
10183 cls: 'fa fa-' + this.caret
10190 cls : 'input-group-addon btn dropdown-toggle',
10195 cls: 'combobox-clear',
10209 combobox.cls += ' roo-select2-container-multi';
10212 if (align ==='left' && this.fieldLabel.length) {
10214 cfg.cls += ' roo-form-group-label-left';
10219 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10220 tooltip : 'This field is required'
10225 cls : 'control-label',
10226 html : this.fieldLabel
10238 var labelCfg = cfg.cn[1];
10239 var contentCfg = cfg.cn[2];
10241 if(this.indicatorpos == 'right'){
10246 cls : 'control-label',
10250 html : this.fieldLabel
10254 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10255 tooltip : 'This field is required'
10268 labelCfg = cfg.cn[0];
10269 contentCfg = cfg.cn[1];
10272 if(this.labelWidth > 12){
10273 labelCfg.style = "width: " + this.labelWidth + 'px';
10276 if(this.labelWidth < 13 && this.labelmd == 0){
10277 this.labelmd = this.labelWidth;
10280 if(this.labellg > 0){
10281 labelCfg.cls += ' col-lg-' + this.labellg;
10282 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10285 if(this.labelmd > 0){
10286 labelCfg.cls += ' col-md-' + this.labelmd;
10287 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10290 if(this.labelsm > 0){
10291 labelCfg.cls += ' col-sm-' + this.labelsm;
10292 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10295 if(this.labelxs > 0){
10296 labelCfg.cls += ' col-xs-' + this.labelxs;
10297 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10300 } else if ( this.fieldLabel.length) {
10301 // Roo.log(" label");
10305 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10306 tooltip : 'This field is required'
10310 //cls : 'input-group-addon',
10311 html : this.fieldLabel
10319 if(this.indicatorpos == 'right'){
10327 html : this.fieldLabel
10331 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10332 tooltip : 'This field is required'
10345 // Roo.log(" no label && no align");
10352 ['xs','sm','md','lg'].map(function(size){
10353 if (settings[size]) {
10354 cfg.cls += ' col-' + size + '-' + settings[size];
10365 onResize : function(w, h){
10366 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10367 // if(typeof w == 'number'){
10368 // var x = w - this.trigger.getWidth();
10369 // this.inputEl().setWidth(this.adjustWidth('input', x));
10370 // this.trigger.setStyle('left', x+'px');
10375 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10378 getResizeEl : function(){
10379 return this.inputEl();
10383 getPositionEl : function(){
10384 return this.inputEl();
10388 alignErrorIcon : function(){
10389 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10393 initEvents : function(){
10397 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10398 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10399 if(!this.multiple && this.showToggleBtn){
10400 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10401 if(this.hideTrigger){
10402 this.trigger.setDisplayed(false);
10404 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10408 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10411 if(this.removable && !this.editable && !this.tickable){
10412 var close = this.closeTriggerEl();
10415 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10416 close.on('click', this.removeBtnClick, this, close);
10420 //this.trigger.addClassOnOver('x-form-trigger-over');
10421 //this.trigger.addClassOnClick('x-form-trigger-click');
10424 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10428 closeTriggerEl : function()
10430 var close = this.el.select('.roo-combo-removable-btn', true).first();
10431 return close ? close : false;
10434 removeBtnClick : function(e, h, el)
10436 e.preventDefault();
10438 if(this.fireEvent("remove", this) !== false){
10440 this.fireEvent("afterremove", this)
10444 createList : function()
10446 this.list = Roo.get(document.body).createChild({
10448 cls: 'typeahead typeahead-long dropdown-menu',
10449 style: 'display:none'
10452 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10457 initTrigger : function(){
10462 onDestroy : function(){
10464 this.trigger.removeAllListeners();
10465 // this.trigger.remove();
10468 // this.wrap.remove();
10470 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10474 onFocus : function(){
10475 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10477 if(!this.mimicing){
10478 this.wrap.addClass('x-trigger-wrap-focus');
10479 this.mimicing = true;
10480 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10481 if(this.monitorTab){
10482 this.el.on("keydown", this.checkTab, this);
10489 checkTab : function(e){
10490 if(e.getKey() == e.TAB){
10491 this.triggerBlur();
10496 onBlur : function(){
10501 mimicBlur : function(e, t){
10503 if(!this.wrap.contains(t) && this.validateBlur()){
10504 this.triggerBlur();
10510 triggerBlur : function(){
10511 this.mimicing = false;
10512 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10513 if(this.monitorTab){
10514 this.el.un("keydown", this.checkTab, this);
10516 //this.wrap.removeClass('x-trigger-wrap-focus');
10517 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10521 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10522 validateBlur : function(e, t){
10527 onDisable : function(){
10528 this.inputEl().dom.disabled = true;
10529 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10531 // this.wrap.addClass('x-item-disabled');
10536 onEnable : function(){
10537 this.inputEl().dom.disabled = false;
10538 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10540 // this.el.removeClass('x-item-disabled');
10545 onShow : function(){
10546 var ae = this.getActionEl();
10549 ae.dom.style.display = '';
10550 ae.dom.style.visibility = 'visible';
10556 onHide : function(){
10557 var ae = this.getActionEl();
10558 ae.dom.style.display = 'none';
10562 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10563 * by an implementing function.
10565 * @param {EventObject} e
10567 onTriggerClick : Roo.emptyFn
10571 * Ext JS Library 1.1.1
10572 * Copyright(c) 2006-2007, Ext JS, LLC.
10574 * Originally Released Under LGPL - original licence link has changed is not relivant.
10577 * <script type="text/javascript">
10582 * @class Roo.data.SortTypes
10584 * Defines the default sorting (casting?) comparison functions used when sorting data.
10586 Roo.data.SortTypes = {
10588 * Default sort that does nothing
10589 * @param {Mixed} s The value being converted
10590 * @return {Mixed} The comparison value
10592 none : function(s){
10597 * The regular expression used to strip tags
10601 stripTagsRE : /<\/?[^>]+>/gi,
10604 * Strips all HTML tags to sort on text only
10605 * @param {Mixed} s The value being converted
10606 * @return {String} The comparison value
10608 asText : function(s){
10609 return String(s).replace(this.stripTagsRE, "");
10613 * Strips all HTML tags to sort on text only - Case insensitive
10614 * @param {Mixed} s The value being converted
10615 * @return {String} The comparison value
10617 asUCText : function(s){
10618 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10622 * Case insensitive string
10623 * @param {Mixed} s The value being converted
10624 * @return {String} The comparison value
10626 asUCString : function(s) {
10627 return String(s).toUpperCase();
10632 * @param {Mixed} s The value being converted
10633 * @return {Number} The comparison value
10635 asDate : function(s) {
10639 if(s instanceof Date){
10640 return s.getTime();
10642 return Date.parse(String(s));
10647 * @param {Mixed} s The value being converted
10648 * @return {Float} The comparison value
10650 asFloat : function(s) {
10651 var val = parseFloat(String(s).replace(/,/g, ""));
10660 * @param {Mixed} s The value being converted
10661 * @return {Number} The comparison value
10663 asInt : function(s) {
10664 var val = parseInt(String(s).replace(/,/g, ""));
10672 * Ext JS Library 1.1.1
10673 * Copyright(c) 2006-2007, Ext JS, LLC.
10675 * Originally Released Under LGPL - original licence link has changed is not relivant.
10678 * <script type="text/javascript">
10682 * @class Roo.data.Record
10683 * Instances of this class encapsulate both record <em>definition</em> information, and record
10684 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10685 * to access Records cached in an {@link Roo.data.Store} object.<br>
10687 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10688 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10691 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10693 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10694 * {@link #create}. The parameters are the same.
10695 * @param {Array} data An associative Array of data values keyed by the field name.
10696 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10697 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10698 * not specified an integer id is generated.
10700 Roo.data.Record = function(data, id){
10701 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10706 * Generate a constructor for a specific record layout.
10707 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10708 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10709 * Each field definition object may contain the following properties: <ul>
10710 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
10711 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10712 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10713 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10714 * is being used, then this is a string containing the javascript expression to reference the data relative to
10715 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10716 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10717 * this may be omitted.</p></li>
10718 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10719 * <ul><li>auto (Default, implies no conversion)</li>
10724 * <li>date</li></ul></p></li>
10725 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10726 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10727 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10728 * by the Reader into an object that will be stored in the Record. It is passed the
10729 * following parameters:<ul>
10730 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10732 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10734 * <br>usage:<br><pre><code>
10735 var TopicRecord = Roo.data.Record.create(
10736 {name: 'title', mapping: 'topic_title'},
10737 {name: 'author', mapping: 'username'},
10738 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10739 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10740 {name: 'lastPoster', mapping: 'user2'},
10741 {name: 'excerpt', mapping: 'post_text'}
10744 var myNewRecord = new TopicRecord({
10745 title: 'Do my job please',
10748 lastPost: new Date(),
10749 lastPoster: 'Animal',
10750 excerpt: 'No way dude!'
10752 myStore.add(myNewRecord);
10757 Roo.data.Record.create = function(o){
10758 var f = function(){
10759 f.superclass.constructor.apply(this, arguments);
10761 Roo.extend(f, Roo.data.Record);
10762 var p = f.prototype;
10763 p.fields = new Roo.util.MixedCollection(false, function(field){
10766 for(var i = 0, len = o.length; i < len; i++){
10767 p.fields.add(new Roo.data.Field(o[i]));
10769 f.getField = function(name){
10770 return p.fields.get(name);
10775 Roo.data.Record.AUTO_ID = 1000;
10776 Roo.data.Record.EDIT = 'edit';
10777 Roo.data.Record.REJECT = 'reject';
10778 Roo.data.Record.COMMIT = 'commit';
10780 Roo.data.Record.prototype = {
10782 * Readonly flag - true if this record has been modified.
10791 join : function(store){
10792 this.store = store;
10796 * Set the named field to the specified value.
10797 * @param {String} name The name of the field to set.
10798 * @param {Object} value The value to set the field to.
10800 set : function(name, value){
10801 if(this.data[name] == value){
10805 if(!this.modified){
10806 this.modified = {};
10808 if(typeof this.modified[name] == 'undefined'){
10809 this.modified[name] = this.data[name];
10811 this.data[name] = value;
10812 if(!this.editing && this.store){
10813 this.store.afterEdit(this);
10818 * Get the value of the named field.
10819 * @param {String} name The name of the field to get the value of.
10820 * @return {Object} The value of the field.
10822 get : function(name){
10823 return this.data[name];
10827 beginEdit : function(){
10828 this.editing = true;
10829 this.modified = {};
10833 cancelEdit : function(){
10834 this.editing = false;
10835 delete this.modified;
10839 endEdit : function(){
10840 this.editing = false;
10841 if(this.dirty && this.store){
10842 this.store.afterEdit(this);
10847 * Usually called by the {@link Roo.data.Store} which owns the Record.
10848 * Rejects all changes made to the Record since either creation, or the last commit operation.
10849 * Modified fields are reverted to their original values.
10851 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10852 * of reject operations.
10854 reject : function(){
10855 var m = this.modified;
10857 if(typeof m[n] != "function"){
10858 this.data[n] = m[n];
10861 this.dirty = false;
10862 delete this.modified;
10863 this.editing = false;
10865 this.store.afterReject(this);
10870 * Usually called by the {@link Roo.data.Store} which owns the Record.
10871 * Commits all changes made to the Record since either creation, or the last commit operation.
10873 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10874 * of commit operations.
10876 commit : function(){
10877 this.dirty = false;
10878 delete this.modified;
10879 this.editing = false;
10881 this.store.afterCommit(this);
10886 hasError : function(){
10887 return this.error != null;
10891 clearError : function(){
10896 * Creates a copy of this record.
10897 * @param {String} id (optional) A new record id if you don't want to use this record's id
10900 copy : function(newId) {
10901 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10905 * Ext JS Library 1.1.1
10906 * Copyright(c) 2006-2007, Ext JS, LLC.
10908 * Originally Released Under LGPL - original licence link has changed is not relivant.
10911 * <script type="text/javascript">
10917 * @class Roo.data.Store
10918 * @extends Roo.util.Observable
10919 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10920 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10922 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10923 * has no knowledge of the format of the data returned by the Proxy.<br>
10925 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10926 * instances from the data object. These records are cached and made available through accessor functions.
10928 * Creates a new Store.
10929 * @param {Object} config A config object containing the objects needed for the Store to access data,
10930 * and read the data into Records.
10932 Roo.data.Store = function(config){
10933 this.data = new Roo.util.MixedCollection(false);
10934 this.data.getKey = function(o){
10937 this.baseParams = {};
10939 this.paramNames = {
10944 "multisort" : "_multisort"
10947 if(config && config.data){
10948 this.inlineData = config.data;
10949 delete config.data;
10952 Roo.apply(this, config);
10954 if(this.reader){ // reader passed
10955 this.reader = Roo.factory(this.reader, Roo.data);
10956 this.reader.xmodule = this.xmodule || false;
10957 if(!this.recordType){
10958 this.recordType = this.reader.recordType;
10960 if(this.reader.onMetaChange){
10961 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10965 if(this.recordType){
10966 this.fields = this.recordType.prototype.fields;
10968 this.modified = [];
10972 * @event datachanged
10973 * Fires when the data cache has changed, and a widget which is using this Store
10974 * as a Record cache should refresh its view.
10975 * @param {Store} this
10977 datachanged : true,
10979 * @event metachange
10980 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10981 * @param {Store} this
10982 * @param {Object} meta The JSON metadata
10987 * Fires when Records have been added to the Store
10988 * @param {Store} this
10989 * @param {Roo.data.Record[]} records The array of Records added
10990 * @param {Number} index The index at which the record(s) were added
10995 * Fires when a Record has been removed from the Store
10996 * @param {Store} this
10997 * @param {Roo.data.Record} record The Record that was removed
10998 * @param {Number} index The index at which the record was removed
11003 * Fires when a Record has been updated
11004 * @param {Store} this
11005 * @param {Roo.data.Record} record The Record that was updated
11006 * @param {String} operation The update operation being performed. Value may be one of:
11008 Roo.data.Record.EDIT
11009 Roo.data.Record.REJECT
11010 Roo.data.Record.COMMIT
11016 * Fires when the data cache has been cleared.
11017 * @param {Store} this
11021 * @event beforeload
11022 * Fires before a request is made for a new data object. If the beforeload handler returns false
11023 * the load action will be canceled.
11024 * @param {Store} this
11025 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11029 * @event beforeloadadd
11030 * Fires after a new set of Records has been loaded.
11031 * @param {Store} this
11032 * @param {Roo.data.Record[]} records The Records that were loaded
11033 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11035 beforeloadadd : true,
11038 * Fires after a new set of Records has been loaded, before they are added to the store.
11039 * @param {Store} this
11040 * @param {Roo.data.Record[]} records The Records that were loaded
11041 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11042 * @params {Object} return from reader
11046 * @event loadexception
11047 * Fires if an exception occurs in the Proxy during loading.
11048 * Called with the signature of the Proxy's "loadexception" event.
11049 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11052 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11053 * @param {Object} load options
11054 * @param {Object} jsonData from your request (normally this contains the Exception)
11056 loadexception : true
11060 this.proxy = Roo.factory(this.proxy, Roo.data);
11061 this.proxy.xmodule = this.xmodule || false;
11062 this.relayEvents(this.proxy, ["loadexception"]);
11064 this.sortToggle = {};
11065 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11067 Roo.data.Store.superclass.constructor.call(this);
11069 if(this.inlineData){
11070 this.loadData(this.inlineData);
11071 delete this.inlineData;
11075 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11077 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11078 * without a remote query - used by combo/forms at present.
11082 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11085 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11088 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11089 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11092 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11093 * on any HTTP request
11096 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11099 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11103 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11104 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11106 remoteSort : false,
11109 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11110 * loaded or when a record is removed. (defaults to false).
11112 pruneModifiedRecords : false,
11115 lastOptions : null,
11118 * Add Records to the Store and fires the add event.
11119 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11121 add : function(records){
11122 records = [].concat(records);
11123 for(var i = 0, len = records.length; i < len; i++){
11124 records[i].join(this);
11126 var index = this.data.length;
11127 this.data.addAll(records);
11128 this.fireEvent("add", this, records, index);
11132 * Remove a Record from the Store and fires the remove event.
11133 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11135 remove : function(record){
11136 var index = this.data.indexOf(record);
11137 this.data.removeAt(index);
11139 if(this.pruneModifiedRecords){
11140 this.modified.remove(record);
11142 this.fireEvent("remove", this, record, index);
11146 * Remove all Records from the Store and fires the clear event.
11148 removeAll : function(){
11150 if(this.pruneModifiedRecords){
11151 this.modified = [];
11153 this.fireEvent("clear", this);
11157 * Inserts Records to the Store at the given index and fires the add event.
11158 * @param {Number} index The start index at which to insert the passed Records.
11159 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11161 insert : function(index, records){
11162 records = [].concat(records);
11163 for(var i = 0, len = records.length; i < len; i++){
11164 this.data.insert(index, records[i]);
11165 records[i].join(this);
11167 this.fireEvent("add", this, records, index);
11171 * Get the index within the cache of the passed Record.
11172 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11173 * @return {Number} The index of the passed Record. Returns -1 if not found.
11175 indexOf : function(record){
11176 return this.data.indexOf(record);
11180 * Get the index within the cache of the Record with the passed id.
11181 * @param {String} id The id of the Record to find.
11182 * @return {Number} The index of the Record. Returns -1 if not found.
11184 indexOfId : function(id){
11185 return this.data.indexOfKey(id);
11189 * Get the Record with the specified id.
11190 * @param {String} id The id of the Record to find.
11191 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11193 getById : function(id){
11194 return this.data.key(id);
11198 * Get the Record at the specified index.
11199 * @param {Number} index The index of the Record to find.
11200 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11202 getAt : function(index){
11203 return this.data.itemAt(index);
11207 * Returns a range of Records between specified indices.
11208 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11209 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11210 * @return {Roo.data.Record[]} An array of Records
11212 getRange : function(start, end){
11213 return this.data.getRange(start, end);
11217 storeOptions : function(o){
11218 o = Roo.apply({}, o);
11221 this.lastOptions = o;
11225 * Loads the Record cache from the configured Proxy using the configured Reader.
11227 * If using remote paging, then the first load call must specify the <em>start</em>
11228 * and <em>limit</em> properties in the options.params property to establish the initial
11229 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11231 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11232 * and this call will return before the new data has been loaded. Perform any post-processing
11233 * in a callback function, or in a "load" event handler.</strong>
11235 * @param {Object} options An object containing properties which control loading options:<ul>
11236 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11237 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11238 * passed the following arguments:<ul>
11239 * <li>r : Roo.data.Record[]</li>
11240 * <li>options: Options object from the load call</li>
11241 * <li>success: Boolean success indicator</li></ul></li>
11242 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11243 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11246 load : function(options){
11247 options = options || {};
11248 if(this.fireEvent("beforeload", this, options) !== false){
11249 this.storeOptions(options);
11250 var p = Roo.apply(options.params || {}, this.baseParams);
11251 // if meta was not loaded from remote source.. try requesting it.
11252 if (!this.reader.metaFromRemote) {
11253 p._requestMeta = 1;
11255 if(this.sortInfo && this.remoteSort){
11256 var pn = this.paramNames;
11257 p[pn["sort"]] = this.sortInfo.field;
11258 p[pn["dir"]] = this.sortInfo.direction;
11260 if (this.multiSort) {
11261 var pn = this.paramNames;
11262 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11265 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11270 * Reloads the Record cache from the configured Proxy using the configured Reader and
11271 * the options from the last load operation performed.
11272 * @param {Object} options (optional) An object containing properties which may override the options
11273 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11274 * the most recently used options are reused).
11276 reload : function(options){
11277 this.load(Roo.applyIf(options||{}, this.lastOptions));
11281 // Called as a callback by the Reader during a load operation.
11282 loadRecords : function(o, options, success){
11283 if(!o || success === false){
11284 if(success !== false){
11285 this.fireEvent("load", this, [], options, o);
11287 if(options.callback){
11288 options.callback.call(options.scope || this, [], options, false);
11292 // if data returned failure - throw an exception.
11293 if (o.success === false) {
11294 // show a message if no listener is registered.
11295 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11296 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11298 // loadmask wil be hooked into this..
11299 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11302 var r = o.records, t = o.totalRecords || r.length;
11304 this.fireEvent("beforeloadadd", this, r, options, o);
11306 if(!options || options.add !== true){
11307 if(this.pruneModifiedRecords){
11308 this.modified = [];
11310 for(var i = 0, len = r.length; i < len; i++){
11314 this.data = this.snapshot;
11315 delete this.snapshot;
11318 this.data.addAll(r);
11319 this.totalLength = t;
11321 this.fireEvent("datachanged", this);
11323 this.totalLength = Math.max(t, this.data.length+r.length);
11327 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11329 var e = new Roo.data.Record({});
11331 e.set(this.parent.displayField, this.parent.emptyTitle);
11332 e.set(this.parent.valueField, '');
11337 this.fireEvent("load", this, r, options, o);
11338 if(options.callback){
11339 options.callback.call(options.scope || this, r, options, true);
11345 * Loads data from a passed data block. A Reader which understands the format of the data
11346 * must have been configured in the constructor.
11347 * @param {Object} data The data block from which to read the Records. The format of the data expected
11348 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11349 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11351 loadData : function(o, append){
11352 var r = this.reader.readRecords(o);
11353 this.loadRecords(r, {add: append}, true);
11357 * Gets the number of cached records.
11359 * <em>If using paging, this may not be the total size of the dataset. If the data object
11360 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11361 * the data set size</em>
11363 getCount : function(){
11364 return this.data.length || 0;
11368 * Gets the total number of records in the dataset as returned by the server.
11370 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11371 * the dataset size</em>
11373 getTotalCount : function(){
11374 return this.totalLength || 0;
11378 * Returns the sort state of the Store as an object with two properties:
11380 field {String} The name of the field by which the Records are sorted
11381 direction {String} The sort order, "ASC" or "DESC"
11384 getSortState : function(){
11385 return this.sortInfo;
11389 applySort : function(){
11390 if(this.sortInfo && !this.remoteSort){
11391 var s = this.sortInfo, f = s.field;
11392 var st = this.fields.get(f).sortType;
11393 var fn = function(r1, r2){
11394 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11395 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11397 this.data.sort(s.direction, fn);
11398 if(this.snapshot && this.snapshot != this.data){
11399 this.snapshot.sort(s.direction, fn);
11405 * Sets the default sort column and order to be used by the next load operation.
11406 * @param {String} fieldName The name of the field to sort by.
11407 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11409 setDefaultSort : function(field, dir){
11410 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11414 * Sort the Records.
11415 * If remote sorting is used, the sort is performed on the server, and the cache is
11416 * reloaded. If local sorting is used, the cache is sorted internally.
11417 * @param {String} fieldName The name of the field to sort by.
11418 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11420 sort : function(fieldName, dir){
11421 var f = this.fields.get(fieldName);
11423 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11425 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11426 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11431 this.sortToggle[f.name] = dir;
11432 this.sortInfo = {field: f.name, direction: dir};
11433 if(!this.remoteSort){
11435 this.fireEvent("datachanged", this);
11437 this.load(this.lastOptions);
11442 * Calls the specified function for each of the Records in the cache.
11443 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11444 * Returning <em>false</em> aborts and exits the iteration.
11445 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11447 each : function(fn, scope){
11448 this.data.each(fn, scope);
11452 * Gets all records modified since the last commit. Modified records are persisted across load operations
11453 * (e.g., during paging).
11454 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11456 getModifiedRecords : function(){
11457 return this.modified;
11461 createFilterFn : function(property, value, anyMatch){
11462 if(!value.exec){ // not a regex
11463 value = String(value);
11464 if(value.length == 0){
11467 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11469 return function(r){
11470 return value.test(r.data[property]);
11475 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11476 * @param {String} property A field on your records
11477 * @param {Number} start The record index to start at (defaults to 0)
11478 * @param {Number} end The last record index to include (defaults to length - 1)
11479 * @return {Number} The sum
11481 sum : function(property, start, end){
11482 var rs = this.data.items, v = 0;
11483 start = start || 0;
11484 end = (end || end === 0) ? end : rs.length-1;
11486 for(var i = start; i <= end; i++){
11487 v += (rs[i].data[property] || 0);
11493 * Filter the records by a specified property.
11494 * @param {String} field A field on your records
11495 * @param {String/RegExp} value Either a string that the field
11496 * should start with or a RegExp to test against the field
11497 * @param {Boolean} anyMatch True to match any part not just the beginning
11499 filter : function(property, value, anyMatch){
11500 var fn = this.createFilterFn(property, value, anyMatch);
11501 return fn ? this.filterBy(fn) : this.clearFilter();
11505 * Filter by a function. The specified function will be called with each
11506 * record in this data source. If the function returns true the record is included,
11507 * otherwise it is filtered.
11508 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11509 * @param {Object} scope (optional) The scope of the function (defaults to this)
11511 filterBy : function(fn, scope){
11512 this.snapshot = this.snapshot || this.data;
11513 this.data = this.queryBy(fn, scope||this);
11514 this.fireEvent("datachanged", this);
11518 * Query the records by a specified property.
11519 * @param {String} field A field on your records
11520 * @param {String/RegExp} value Either a string that the field
11521 * should start with or a RegExp to test against the field
11522 * @param {Boolean} anyMatch True to match any part not just the beginning
11523 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11525 query : function(property, value, anyMatch){
11526 var fn = this.createFilterFn(property, value, anyMatch);
11527 return fn ? this.queryBy(fn) : this.data.clone();
11531 * Query by a function. The specified function will be called with each
11532 * record in this data source. If the function returns true the record is included
11534 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11535 * @param {Object} scope (optional) The scope of the function (defaults to this)
11536 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11538 queryBy : function(fn, scope){
11539 var data = this.snapshot || this.data;
11540 return data.filterBy(fn, scope||this);
11544 * Collects unique values for a particular dataIndex from this store.
11545 * @param {String} dataIndex The property to collect
11546 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11547 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11548 * @return {Array} An array of the unique values
11550 collect : function(dataIndex, allowNull, bypassFilter){
11551 var d = (bypassFilter === true && this.snapshot) ?
11552 this.snapshot.items : this.data.items;
11553 var v, sv, r = [], l = {};
11554 for(var i = 0, len = d.length; i < len; i++){
11555 v = d[i].data[dataIndex];
11557 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11566 * Revert to a view of the Record cache with no filtering applied.
11567 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11569 clearFilter : function(suppressEvent){
11570 if(this.snapshot && this.snapshot != this.data){
11571 this.data = this.snapshot;
11572 delete this.snapshot;
11573 if(suppressEvent !== true){
11574 this.fireEvent("datachanged", this);
11580 afterEdit : function(record){
11581 if(this.modified.indexOf(record) == -1){
11582 this.modified.push(record);
11584 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11588 afterReject : function(record){
11589 this.modified.remove(record);
11590 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11594 afterCommit : function(record){
11595 this.modified.remove(record);
11596 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11600 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11601 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11603 commitChanges : function(){
11604 var m = this.modified.slice(0);
11605 this.modified = [];
11606 for(var i = 0, len = m.length; i < len; i++){
11612 * Cancel outstanding changes on all changed records.
11614 rejectChanges : function(){
11615 var m = this.modified.slice(0);
11616 this.modified = [];
11617 for(var i = 0, len = m.length; i < len; i++){
11622 onMetaChange : function(meta, rtype, o){
11623 this.recordType = rtype;
11624 this.fields = rtype.prototype.fields;
11625 delete this.snapshot;
11626 this.sortInfo = meta.sortInfo || this.sortInfo;
11627 this.modified = [];
11628 this.fireEvent('metachange', this, this.reader.meta);
11631 moveIndex : function(data, type)
11633 var index = this.indexOf(data);
11635 var newIndex = index + type;
11639 this.insert(newIndex, data);
11644 * Ext JS Library 1.1.1
11645 * Copyright(c) 2006-2007, Ext JS, LLC.
11647 * Originally Released Under LGPL - original licence link has changed is not relivant.
11650 * <script type="text/javascript">
11654 * @class Roo.data.SimpleStore
11655 * @extends Roo.data.Store
11656 * Small helper class to make creating Stores from Array data easier.
11657 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11658 * @cfg {Array} fields An array of field definition objects, or field name strings.
11659 * @cfg {Array} data The multi-dimensional array of data
11661 * @param {Object} config
11663 Roo.data.SimpleStore = function(config){
11664 Roo.data.SimpleStore.superclass.constructor.call(this, {
11666 reader: new Roo.data.ArrayReader({
11669 Roo.data.Record.create(config.fields)
11671 proxy : new Roo.data.MemoryProxy(config.data)
11675 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11677 * Ext JS Library 1.1.1
11678 * Copyright(c) 2006-2007, Ext JS, LLC.
11680 * Originally Released Under LGPL - original licence link has changed is not relivant.
11683 * <script type="text/javascript">
11688 * @extends Roo.data.Store
11689 * @class Roo.data.JsonStore
11690 * Small helper class to make creating Stores for JSON data easier. <br/>
11692 var store = new Roo.data.JsonStore({
11693 url: 'get-images.php',
11695 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11698 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11699 * JsonReader and HttpProxy (unless inline data is provided).</b>
11700 * @cfg {Array} fields An array of field definition objects, or field name strings.
11702 * @param {Object} config
11704 Roo.data.JsonStore = function(c){
11705 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11706 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11707 reader: new Roo.data.JsonReader(c, c.fields)
11710 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11712 * Ext JS Library 1.1.1
11713 * Copyright(c) 2006-2007, Ext JS, LLC.
11715 * Originally Released Under LGPL - original licence link has changed is not relivant.
11718 * <script type="text/javascript">
11722 Roo.data.Field = function(config){
11723 if(typeof config == "string"){
11724 config = {name: config};
11726 Roo.apply(this, config);
11729 this.type = "auto";
11732 var st = Roo.data.SortTypes;
11733 // named sortTypes are supported, here we look them up
11734 if(typeof this.sortType == "string"){
11735 this.sortType = st[this.sortType];
11738 // set default sortType for strings and dates
11739 if(!this.sortType){
11742 this.sortType = st.asUCString;
11745 this.sortType = st.asDate;
11748 this.sortType = st.none;
11753 var stripRe = /[\$,%]/g;
11755 // prebuilt conversion function for this field, instead of
11756 // switching every time we're reading a value
11758 var cv, dateFormat = this.dateFormat;
11763 cv = function(v){ return v; };
11766 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11770 return v !== undefined && v !== null && v !== '' ?
11771 parseInt(String(v).replace(stripRe, ""), 10) : '';
11776 return v !== undefined && v !== null && v !== '' ?
11777 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11782 cv = function(v){ return v === true || v === "true" || v == 1; };
11789 if(v instanceof Date){
11793 if(dateFormat == "timestamp"){
11794 return new Date(v*1000);
11796 return Date.parseDate(v, dateFormat);
11798 var parsed = Date.parse(v);
11799 return parsed ? new Date(parsed) : null;
11808 Roo.data.Field.prototype = {
11816 * Ext JS Library 1.1.1
11817 * Copyright(c) 2006-2007, Ext JS, LLC.
11819 * Originally Released Under LGPL - original licence link has changed is not relivant.
11822 * <script type="text/javascript">
11825 // Base class for reading structured data from a data source. This class is intended to be
11826 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11829 * @class Roo.data.DataReader
11830 * Base class for reading structured data from a data source. This class is intended to be
11831 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11834 Roo.data.DataReader = function(meta, recordType){
11838 this.recordType = recordType instanceof Array ?
11839 Roo.data.Record.create(recordType) : recordType;
11842 Roo.data.DataReader.prototype = {
11844 * Create an empty record
11845 * @param {Object} data (optional) - overlay some values
11846 * @return {Roo.data.Record} record created.
11848 newRow : function(d) {
11850 this.recordType.prototype.fields.each(function(c) {
11852 case 'int' : da[c.name] = 0; break;
11853 case 'date' : da[c.name] = new Date(); break;
11854 case 'float' : da[c.name] = 0.0; break;
11855 case 'boolean' : da[c.name] = false; break;
11856 default : da[c.name] = ""; break;
11860 return new this.recordType(Roo.apply(da, d));
11865 * Ext JS Library 1.1.1
11866 * Copyright(c) 2006-2007, Ext JS, LLC.
11868 * Originally Released Under LGPL - original licence link has changed is not relivant.
11871 * <script type="text/javascript">
11875 * @class Roo.data.DataProxy
11876 * @extends Roo.data.Observable
11877 * This class is an abstract base class for implementations which provide retrieval of
11878 * unformatted data objects.<br>
11880 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11881 * (of the appropriate type which knows how to parse the data object) to provide a block of
11882 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11884 * Custom implementations must implement the load method as described in
11885 * {@link Roo.data.HttpProxy#load}.
11887 Roo.data.DataProxy = function(){
11890 * @event beforeload
11891 * Fires before a network request is made to retrieve a data object.
11892 * @param {Object} This DataProxy object.
11893 * @param {Object} params The params parameter to the load function.
11898 * Fires before the load method's callback is called.
11899 * @param {Object} This DataProxy object.
11900 * @param {Object} o The data object.
11901 * @param {Object} arg The callback argument object passed to the load function.
11905 * @event loadexception
11906 * Fires if an Exception occurs during data retrieval.
11907 * @param {Object} This DataProxy object.
11908 * @param {Object} o The data object.
11909 * @param {Object} arg The callback argument object passed to the load function.
11910 * @param {Object} e The Exception.
11912 loadexception : true
11914 Roo.data.DataProxy.superclass.constructor.call(this);
11917 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11920 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11924 * Ext JS Library 1.1.1
11925 * Copyright(c) 2006-2007, Ext JS, LLC.
11927 * Originally Released Under LGPL - original licence link has changed is not relivant.
11930 * <script type="text/javascript">
11933 * @class Roo.data.MemoryProxy
11934 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11935 * to the Reader when its load method is called.
11937 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11939 Roo.data.MemoryProxy = function(data){
11943 Roo.data.MemoryProxy.superclass.constructor.call(this);
11947 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11950 * Load data from the requested source (in this case an in-memory
11951 * data object passed to the constructor), read the data object into
11952 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11953 * process that block using the passed callback.
11954 * @param {Object} params This parameter is not used by the MemoryProxy class.
11955 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11956 * object into a block of Roo.data.Records.
11957 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11958 * The function must be passed <ul>
11959 * <li>The Record block object</li>
11960 * <li>The "arg" argument from the load function</li>
11961 * <li>A boolean success indicator</li>
11963 * @param {Object} scope The scope in which to call the callback
11964 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11966 load : function(params, reader, callback, scope, arg){
11967 params = params || {};
11970 result = reader.readRecords(this.data);
11972 this.fireEvent("loadexception", this, arg, null, e);
11973 callback.call(scope, null, arg, false);
11976 callback.call(scope, result, arg, true);
11980 update : function(params, records){
11985 * Ext JS Library 1.1.1
11986 * Copyright(c) 2006-2007, Ext JS, LLC.
11988 * Originally Released Under LGPL - original licence link has changed is not relivant.
11991 * <script type="text/javascript">
11994 * @class Roo.data.HttpProxy
11995 * @extends Roo.data.DataProxy
11996 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11997 * configured to reference a certain URL.<br><br>
11999 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12000 * from which the running page was served.<br><br>
12002 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12004 * Be aware that to enable the browser to parse an XML document, the server must set
12005 * the Content-Type header in the HTTP response to "text/xml".
12007 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12008 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12009 * will be used to make the request.
12011 Roo.data.HttpProxy = function(conn){
12012 Roo.data.HttpProxy.superclass.constructor.call(this);
12013 // is conn a conn config or a real conn?
12015 this.useAjax = !conn || !conn.events;
12019 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12020 // thse are take from connection...
12023 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12026 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12027 * extra parameters to each request made by this object. (defaults to undefined)
12030 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12031 * to each request made by this object. (defaults to undefined)
12034 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12037 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12040 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12046 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12050 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12051 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12052 * a finer-grained basis than the DataProxy events.
12054 getConnection : function(){
12055 return this.useAjax ? Roo.Ajax : this.conn;
12059 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12060 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12061 * process that block using the passed callback.
12062 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12063 * for the request to the remote server.
12064 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12065 * object into a block of Roo.data.Records.
12066 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12067 * The function must be passed <ul>
12068 * <li>The Record block object</li>
12069 * <li>The "arg" argument from the load function</li>
12070 * <li>A boolean success indicator</li>
12072 * @param {Object} scope The scope in which to call the callback
12073 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12075 load : function(params, reader, callback, scope, arg){
12076 if(this.fireEvent("beforeload", this, params) !== false){
12078 params : params || {},
12080 callback : callback,
12085 callback : this.loadResponse,
12089 Roo.applyIf(o, this.conn);
12090 if(this.activeRequest){
12091 Roo.Ajax.abort(this.activeRequest);
12093 this.activeRequest = Roo.Ajax.request(o);
12095 this.conn.request(o);
12098 callback.call(scope||this, null, arg, false);
12103 loadResponse : function(o, success, response){
12104 delete this.activeRequest;
12106 this.fireEvent("loadexception", this, o, response);
12107 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12112 result = o.reader.read(response);
12114 this.fireEvent("loadexception", this, o, response, e);
12115 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12119 this.fireEvent("load", this, o, o.request.arg);
12120 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12124 update : function(dataSet){
12129 updateResponse : function(dataSet){
12134 * Ext JS Library 1.1.1
12135 * Copyright(c) 2006-2007, Ext JS, LLC.
12137 * Originally Released Under LGPL - original licence link has changed is not relivant.
12140 * <script type="text/javascript">
12144 * @class Roo.data.ScriptTagProxy
12145 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12146 * other than the originating domain of the running page.<br><br>
12148 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
12149 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12151 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12152 * source code that is used as the source inside a <script> tag.<br><br>
12154 * In order for the browser to process the returned data, the server must wrap the data object
12155 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12156 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12157 * depending on whether the callback name was passed:
12160 boolean scriptTag = false;
12161 String cb = request.getParameter("callback");
12164 response.setContentType("text/javascript");
12166 response.setContentType("application/x-json");
12168 Writer out = response.getWriter();
12170 out.write(cb + "(");
12172 out.print(dataBlock.toJsonString());
12179 * @param {Object} config A configuration object.
12181 Roo.data.ScriptTagProxy = function(config){
12182 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12183 Roo.apply(this, config);
12184 this.head = document.getElementsByTagName("head")[0];
12187 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12189 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12191 * @cfg {String} url The URL from which to request the data object.
12194 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12198 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12199 * the server the name of the callback function set up by the load call to process the returned data object.
12200 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12201 * javascript output which calls this named function passing the data object as its only parameter.
12203 callbackParam : "callback",
12205 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12206 * name to the request.
12211 * Load data from the configured URL, read the data object into
12212 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12213 * process that block using the passed callback.
12214 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12215 * for the request to the remote server.
12216 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12217 * object into a block of Roo.data.Records.
12218 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12219 * The function must be passed <ul>
12220 * <li>The Record block object</li>
12221 * <li>The "arg" argument from the load function</li>
12222 * <li>A boolean success indicator</li>
12224 * @param {Object} scope The scope in which to call the callback
12225 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12227 load : function(params, reader, callback, scope, arg){
12228 if(this.fireEvent("beforeload", this, params) !== false){
12230 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12232 var url = this.url;
12233 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12235 url += "&_dc=" + (new Date().getTime());
12237 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12240 cb : "stcCallback"+transId,
12241 scriptId : "stcScript"+transId,
12245 callback : callback,
12251 window[trans.cb] = function(o){
12252 conn.handleResponse(o, trans);
12255 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12257 if(this.autoAbort !== false){
12261 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12263 var script = document.createElement("script");
12264 script.setAttribute("src", url);
12265 script.setAttribute("type", "text/javascript");
12266 script.setAttribute("id", trans.scriptId);
12267 this.head.appendChild(script);
12269 this.trans = trans;
12271 callback.call(scope||this, null, arg, false);
12276 isLoading : function(){
12277 return this.trans ? true : false;
12281 * Abort the current server request.
12283 abort : function(){
12284 if(this.isLoading()){
12285 this.destroyTrans(this.trans);
12290 destroyTrans : function(trans, isLoaded){
12291 this.head.removeChild(document.getElementById(trans.scriptId));
12292 clearTimeout(trans.timeoutId);
12294 window[trans.cb] = undefined;
12296 delete window[trans.cb];
12299 // if hasn't been loaded, wait for load to remove it to prevent script error
12300 window[trans.cb] = function(){
12301 window[trans.cb] = undefined;
12303 delete window[trans.cb];
12310 handleResponse : function(o, trans){
12311 this.trans = false;
12312 this.destroyTrans(trans, true);
12315 result = trans.reader.readRecords(o);
12317 this.fireEvent("loadexception", this, o, trans.arg, e);
12318 trans.callback.call(trans.scope||window, null, trans.arg, false);
12321 this.fireEvent("load", this, o, trans.arg);
12322 trans.callback.call(trans.scope||window, result, trans.arg, true);
12326 handleFailure : function(trans){
12327 this.trans = false;
12328 this.destroyTrans(trans, false);
12329 this.fireEvent("loadexception", this, null, trans.arg);
12330 trans.callback.call(trans.scope||window, null, trans.arg, false);
12334 * Ext JS Library 1.1.1
12335 * Copyright(c) 2006-2007, Ext JS, LLC.
12337 * Originally Released Under LGPL - original licence link has changed is not relivant.
12340 * <script type="text/javascript">
12344 * @class Roo.data.JsonReader
12345 * @extends Roo.data.DataReader
12346 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12347 * based on mappings in a provided Roo.data.Record constructor.
12349 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12350 * in the reply previously.
12355 var RecordDef = Roo.data.Record.create([
12356 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12357 {name: 'occupation'} // This field will use "occupation" as the mapping.
12359 var myReader = new Roo.data.JsonReader({
12360 totalProperty: "results", // The property which contains the total dataset size (optional)
12361 root: "rows", // The property which contains an Array of row objects
12362 id: "id" // The property within each row object that provides an ID for the record (optional)
12366 * This would consume a JSON file like this:
12368 { 'results': 2, 'rows': [
12369 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12370 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12373 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12374 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12375 * paged from the remote server.
12376 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12377 * @cfg {String} root name of the property which contains the Array of row objects.
12378 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12379 * @cfg {Array} fields Array of field definition objects
12381 * Create a new JsonReader
12382 * @param {Object} meta Metadata configuration options
12383 * @param {Object} recordType Either an Array of field definition objects,
12384 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12386 Roo.data.JsonReader = function(meta, recordType){
12389 // set some defaults:
12390 Roo.applyIf(meta, {
12391 totalProperty: 'total',
12392 successProperty : 'success',
12397 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12399 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12402 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12403 * Used by Store query builder to append _requestMeta to params.
12406 metaFromRemote : false,
12408 * This method is only used by a DataProxy which has retrieved data from a remote server.
12409 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12410 * @return {Object} data A data block which is used by an Roo.data.Store object as
12411 * a cache of Roo.data.Records.
12413 read : function(response){
12414 var json = response.responseText;
12416 var o = /* eval:var:o */ eval("("+json+")");
12418 throw {message: "JsonReader.read: Json object not found"};
12424 this.metaFromRemote = true;
12425 this.meta = o.metaData;
12426 this.recordType = Roo.data.Record.create(o.metaData.fields);
12427 this.onMetaChange(this.meta, this.recordType, o);
12429 return this.readRecords(o);
12432 // private function a store will implement
12433 onMetaChange : function(meta, recordType, o){
12440 simpleAccess: function(obj, subsc) {
12447 getJsonAccessor: function(){
12449 return function(expr) {
12451 return(re.test(expr))
12452 ? new Function("obj", "return obj." + expr)
12457 return Roo.emptyFn;
12462 * Create a data block containing Roo.data.Records from an XML document.
12463 * @param {Object} o An object which contains an Array of row objects in the property specified
12464 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12465 * which contains the total size of the dataset.
12466 * @return {Object} data A data block which is used by an Roo.data.Store object as
12467 * a cache of Roo.data.Records.
12469 readRecords : function(o){
12471 * After any data loads, the raw JSON data is available for further custom processing.
12475 var s = this.meta, Record = this.recordType,
12476 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12478 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12480 if(s.totalProperty) {
12481 this.getTotal = this.getJsonAccessor(s.totalProperty);
12483 if(s.successProperty) {
12484 this.getSuccess = this.getJsonAccessor(s.successProperty);
12486 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12488 var g = this.getJsonAccessor(s.id);
12489 this.getId = function(rec) {
12491 return (r === undefined || r === "") ? null : r;
12494 this.getId = function(){return null;};
12497 for(var jj = 0; jj < fl; jj++){
12499 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12500 this.ef[jj] = this.getJsonAccessor(map);
12504 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12505 if(s.totalProperty){
12506 var vt = parseInt(this.getTotal(o), 10);
12511 if(s.successProperty){
12512 var vs = this.getSuccess(o);
12513 if(vs === false || vs === 'false'){
12518 for(var i = 0; i < c; i++){
12521 var id = this.getId(n);
12522 for(var j = 0; j < fl; j++){
12524 var v = this.ef[j](n);
12526 Roo.log('missing convert for ' + f.name);
12530 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12532 var record = new Record(values, id);
12534 records[i] = record;
12540 totalRecords : totalRecords
12545 * Ext JS Library 1.1.1
12546 * Copyright(c) 2006-2007, Ext JS, LLC.
12548 * Originally Released Under LGPL - original licence link has changed is not relivant.
12551 * <script type="text/javascript">
12555 * @class Roo.data.ArrayReader
12556 * @extends Roo.data.DataReader
12557 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12558 * Each element of that Array represents a row of data fields. The
12559 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12560 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12564 var RecordDef = Roo.data.Record.create([
12565 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12566 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12568 var myReader = new Roo.data.ArrayReader({
12569 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12573 * This would consume an Array like this:
12575 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12577 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12579 * Create a new JsonReader
12580 * @param {Object} meta Metadata configuration options.
12581 * @param {Object} recordType Either an Array of field definition objects
12582 * as specified to {@link Roo.data.Record#create},
12583 * or an {@link Roo.data.Record} object
12584 * created using {@link Roo.data.Record#create}.
12586 Roo.data.ArrayReader = function(meta, recordType){
12587 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12590 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12592 * Create a data block containing Roo.data.Records from an XML document.
12593 * @param {Object} o An Array of row objects which represents the dataset.
12594 * @return {Object} data A data block which is used by an Roo.data.Store object as
12595 * a cache of Roo.data.Records.
12597 readRecords : function(o){
12598 var sid = this.meta ? this.meta.id : null;
12599 var recordType = this.recordType, fields = recordType.prototype.fields;
12602 for(var i = 0; i < root.length; i++){
12605 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12606 for(var j = 0, jlen = fields.length; j < jlen; j++){
12607 var f = fields.items[j];
12608 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12609 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12611 values[f.name] = v;
12613 var record = new recordType(values, id);
12615 records[records.length] = record;
12619 totalRecords : records.length
12628 * @class Roo.bootstrap.ComboBox
12629 * @extends Roo.bootstrap.TriggerField
12630 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12631 * @cfg {Boolean} append (true|false) default false
12632 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12633 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12634 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12635 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12636 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12637 * @cfg {Boolean} animate default true
12638 * @cfg {Boolean} emptyResultText only for touch device
12639 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12640 * @cfg {String} emptyTitle default ''
12642 * Create a new ComboBox.
12643 * @param {Object} config Configuration options
12645 Roo.bootstrap.ComboBox = function(config){
12646 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12650 * Fires when the dropdown list is expanded
12651 * @param {Roo.bootstrap.ComboBox} combo This combo box
12656 * Fires when the dropdown list is collapsed
12657 * @param {Roo.bootstrap.ComboBox} combo This combo box
12661 * @event beforeselect
12662 * Fires before a list item is selected. Return false to cancel the selection.
12663 * @param {Roo.bootstrap.ComboBox} combo This combo box
12664 * @param {Roo.data.Record} record The data record returned from the underlying store
12665 * @param {Number} index The index of the selected item in the dropdown list
12667 'beforeselect' : true,
12670 * Fires when a list item is selected
12671 * @param {Roo.bootstrap.ComboBox} combo This combo box
12672 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12673 * @param {Number} index The index of the selected item in the dropdown list
12677 * @event beforequery
12678 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12679 * The event object passed has these properties:
12680 * @param {Roo.bootstrap.ComboBox} combo This combo box
12681 * @param {String} query The query
12682 * @param {Boolean} forceAll true to force "all" query
12683 * @param {Boolean} cancel true to cancel the query
12684 * @param {Object} e The query event object
12686 'beforequery': true,
12689 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12690 * @param {Roo.bootstrap.ComboBox} combo This combo box
12695 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12696 * @param {Roo.bootstrap.ComboBox} combo This combo box
12697 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12702 * Fires when the remove value from the combobox array
12703 * @param {Roo.bootstrap.ComboBox} combo This combo box
12707 * @event afterremove
12708 * Fires when the remove value from the combobox array
12709 * @param {Roo.bootstrap.ComboBox} combo This combo box
12711 'afterremove' : true,
12713 * @event specialfilter
12714 * Fires when specialfilter
12715 * @param {Roo.bootstrap.ComboBox} combo This combo box
12717 'specialfilter' : true,
12720 * Fires when tick the element
12721 * @param {Roo.bootstrap.ComboBox} combo This combo box
12725 * @event touchviewdisplay
12726 * Fires when touch view require special display (default is using displayField)
12727 * @param {Roo.bootstrap.ComboBox} combo This combo box
12728 * @param {Object} cfg set html .
12730 'touchviewdisplay' : true
12735 this.tickItems = [];
12737 this.selectedIndex = -1;
12738 if(this.mode == 'local'){
12739 if(config.queryDelay === undefined){
12740 this.queryDelay = 10;
12742 if(config.minChars === undefined){
12748 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12751 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12752 * rendering into an Roo.Editor, defaults to false)
12755 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12756 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12759 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12762 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12763 * the dropdown list (defaults to undefined, with no header element)
12767 * @cfg {String/Roo.Template} tpl The template to use to render the output
12771 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12773 listWidth: undefined,
12775 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12776 * mode = 'remote' or 'text' if mode = 'local')
12778 displayField: undefined,
12781 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12782 * mode = 'remote' or 'value' if mode = 'local').
12783 * Note: use of a valueField requires the user make a selection
12784 * in order for a value to be mapped.
12786 valueField: undefined,
12788 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12793 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12794 * field's data value (defaults to the underlying DOM element's name)
12796 hiddenName: undefined,
12798 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12802 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12804 selectedClass: 'active',
12807 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12811 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12812 * anchor positions (defaults to 'tl-bl')
12814 listAlign: 'tl-bl?',
12816 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12820 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12821 * query specified by the allQuery config option (defaults to 'query')
12823 triggerAction: 'query',
12825 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12826 * (defaults to 4, does not apply if editable = false)
12830 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12831 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12835 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12836 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12840 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12841 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12845 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12846 * when editable = true (defaults to false)
12848 selectOnFocus:false,
12850 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12852 queryParam: 'query',
12854 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12855 * when mode = 'remote' (defaults to 'Loading...')
12857 loadingText: 'Loading...',
12859 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12863 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12867 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12868 * traditional select (defaults to true)
12872 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12876 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12880 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12881 * listWidth has a higher value)
12885 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12886 * allow the user to set arbitrary text into the field (defaults to false)
12888 forceSelection:false,
12890 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12891 * if typeAhead = true (defaults to 250)
12893 typeAheadDelay : 250,
12895 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12896 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12898 valueNotFoundText : undefined,
12900 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12902 blockFocus : false,
12905 * @cfg {Boolean} disableClear Disable showing of clear button.
12907 disableClear : false,
12909 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12911 alwaysQuery : false,
12914 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12919 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12921 invalidClass : "has-warning",
12924 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12926 validClass : "has-success",
12929 * @cfg {Boolean} specialFilter (true|false) special filter default false
12931 specialFilter : false,
12934 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12936 mobileTouchView : true,
12939 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12941 useNativeIOS : false,
12943 ios_options : false,
12955 btnPosition : 'right',
12956 triggerList : true,
12957 showToggleBtn : true,
12959 emptyResultText: 'Empty',
12960 triggerText : 'Select',
12963 // element that contains real text value.. (when hidden is used..)
12965 getAutoCreate : function()
12970 * Render classic select for iso
12973 if(Roo.isIOS && this.useNativeIOS){
12974 cfg = this.getAutoCreateNativeIOS();
12982 if(Roo.isTouch && this.mobileTouchView){
12983 cfg = this.getAutoCreateTouchView();
12990 if(!this.tickable){
12991 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12996 * ComboBox with tickable selections
12999 var align = this.labelAlign || this.parentLabelAlign();
13002 cls : 'form-group roo-combobox-tickable' //input-group
13005 var btn_text_select = '';
13006 var btn_text_done = '';
13007 var btn_text_cancel = '';
13009 if (this.btn_text_show) {
13010 btn_text_select = 'Select';
13011 btn_text_done = 'Done';
13012 btn_text_cancel = 'Cancel';
13017 cls : 'tickable-buttons',
13022 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13023 //html : this.triggerText
13024 html: btn_text_select
13030 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13032 html: btn_text_done
13038 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13040 html: btn_text_cancel
13046 buttons.cn.unshift({
13048 cls: 'roo-select2-search-field-input'
13054 Roo.each(buttons.cn, function(c){
13056 c.cls += ' btn-' + _this.size;
13059 if (_this.disabled) {
13070 cls: 'form-hidden-field'
13074 cls: 'roo-select2-choices',
13078 cls: 'roo-select2-search-field',
13089 cls: 'roo-select2-container input-group roo-select2-container-multi',
13094 // cls: 'typeahead typeahead-long dropdown-menu',
13095 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13100 if(this.hasFeedback && !this.allowBlank){
13104 cls: 'glyphicon form-control-feedback'
13107 combobox.cn.push(feedback);
13111 if (align ==='left' && this.fieldLabel.length) {
13113 cfg.cls += ' roo-form-group-label-left';
13118 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13119 tooltip : 'This field is required'
13124 cls : 'control-label',
13125 html : this.fieldLabel
13137 var labelCfg = cfg.cn[1];
13138 var contentCfg = cfg.cn[2];
13141 if(this.indicatorpos == 'right'){
13147 cls : 'control-label',
13151 html : this.fieldLabel
13155 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13156 tooltip : 'This field is required'
13171 labelCfg = cfg.cn[0];
13172 contentCfg = cfg.cn[1];
13176 if(this.labelWidth > 12){
13177 labelCfg.style = "width: " + this.labelWidth + 'px';
13180 if(this.labelWidth < 13 && this.labelmd == 0){
13181 this.labelmd = this.labelWidth;
13184 if(this.labellg > 0){
13185 labelCfg.cls += ' col-lg-' + this.labellg;
13186 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13189 if(this.labelmd > 0){
13190 labelCfg.cls += ' col-md-' + this.labelmd;
13191 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13194 if(this.labelsm > 0){
13195 labelCfg.cls += ' col-sm-' + this.labelsm;
13196 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13199 if(this.labelxs > 0){
13200 labelCfg.cls += ' col-xs-' + this.labelxs;
13201 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13205 } else if ( this.fieldLabel.length) {
13206 // Roo.log(" label");
13210 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13211 tooltip : 'This field is required'
13215 //cls : 'input-group-addon',
13216 html : this.fieldLabel
13221 if(this.indicatorpos == 'right'){
13225 //cls : 'input-group-addon',
13226 html : this.fieldLabel
13230 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13231 tooltip : 'This field is required'
13240 // Roo.log(" no label && no align");
13247 ['xs','sm','md','lg'].map(function(size){
13248 if (settings[size]) {
13249 cfg.cls += ' col-' + size + '-' + settings[size];
13257 _initEventsCalled : false,
13260 initEvents: function()
13262 if (this._initEventsCalled) { // as we call render... prevent looping...
13265 this._initEventsCalled = true;
13268 throw "can not find store for combo";
13271 this.indicator = this.indicatorEl();
13273 this.store = Roo.factory(this.store, Roo.data);
13274 this.store.parent = this;
13276 // if we are building from html. then this element is so complex, that we can not really
13277 // use the rendered HTML.
13278 // so we have to trash and replace the previous code.
13279 if (Roo.XComponent.build_from_html) {
13280 // remove this element....
13281 var e = this.el.dom, k=0;
13282 while (e ) { e = e.previousSibling; ++k;}
13287 this.rendered = false;
13289 this.render(this.parent().getChildContainer(true), k);
13292 if(Roo.isIOS && this.useNativeIOS){
13293 this.initIOSView();
13301 if(Roo.isTouch && this.mobileTouchView){
13302 this.initTouchView();
13307 this.initTickableEvents();
13311 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13313 if(this.hiddenName){
13315 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13317 this.hiddenField.dom.value =
13318 this.hiddenValue !== undefined ? this.hiddenValue :
13319 this.value !== undefined ? this.value : '';
13321 // prevent input submission
13322 this.el.dom.removeAttribute('name');
13323 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13328 // this.el.dom.setAttribute('autocomplete', 'off');
13331 var cls = 'x-combo-list';
13333 //this.list = new Roo.Layer({
13334 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13340 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13341 _this.list.setWidth(lw);
13344 this.list.on('mouseover', this.onViewOver, this);
13345 this.list.on('mousemove', this.onViewMove, this);
13346 this.list.on('scroll', this.onViewScroll, this);
13349 this.list.swallowEvent('mousewheel');
13350 this.assetHeight = 0;
13353 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13354 this.assetHeight += this.header.getHeight();
13357 this.innerList = this.list.createChild({cls:cls+'-inner'});
13358 this.innerList.on('mouseover', this.onViewOver, this);
13359 this.innerList.on('mousemove', this.onViewMove, this);
13360 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13362 if(this.allowBlank && !this.pageSize && !this.disableClear){
13363 this.footer = this.list.createChild({cls:cls+'-ft'});
13364 this.pageTb = new Roo.Toolbar(this.footer);
13368 this.footer = this.list.createChild({cls:cls+'-ft'});
13369 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13370 {pageSize: this.pageSize});
13374 if (this.pageTb && this.allowBlank && !this.disableClear) {
13376 this.pageTb.add(new Roo.Toolbar.Fill(), {
13377 cls: 'x-btn-icon x-btn-clear',
13379 handler: function()
13382 _this.clearValue();
13383 _this.onSelect(false, -1);
13388 this.assetHeight += this.footer.getHeight();
13393 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13396 this.view = new Roo.View(this.list, this.tpl, {
13397 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13399 //this.view.wrapEl.setDisplayed(false);
13400 this.view.on('click', this.onViewClick, this);
13403 this.store.on('beforeload', this.onBeforeLoad, this);
13404 this.store.on('load', this.onLoad, this);
13405 this.store.on('loadexception', this.onLoadException, this);
13407 if(this.resizable){
13408 this.resizer = new Roo.Resizable(this.list, {
13409 pinned:true, handles:'se'
13411 this.resizer.on('resize', function(r, w, h){
13412 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13413 this.listWidth = w;
13414 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13415 this.restrictHeight();
13417 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13420 if(!this.editable){
13421 this.editable = true;
13422 this.setEditable(false);
13427 if (typeof(this.events.add.listeners) != 'undefined') {
13429 this.addicon = this.wrap.createChild(
13430 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13432 this.addicon.on('click', function(e) {
13433 this.fireEvent('add', this);
13436 if (typeof(this.events.edit.listeners) != 'undefined') {
13438 this.editicon = this.wrap.createChild(
13439 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13440 if (this.addicon) {
13441 this.editicon.setStyle('margin-left', '40px');
13443 this.editicon.on('click', function(e) {
13445 // we fire even if inothing is selected..
13446 this.fireEvent('edit', this, this.lastData );
13452 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13453 "up" : function(e){
13454 this.inKeyMode = true;
13458 "down" : function(e){
13459 if(!this.isExpanded()){
13460 this.onTriggerClick();
13462 this.inKeyMode = true;
13467 "enter" : function(e){
13468 // this.onViewClick();
13472 if(this.fireEvent("specialkey", this, e)){
13473 this.onViewClick(false);
13479 "esc" : function(e){
13483 "tab" : function(e){
13486 if(this.fireEvent("specialkey", this, e)){
13487 this.onViewClick(false);
13495 doRelay : function(foo, bar, hname){
13496 if(hname == 'down' || this.scope.isExpanded()){
13497 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13506 this.queryDelay = Math.max(this.queryDelay || 10,
13507 this.mode == 'local' ? 10 : 250);
13510 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13512 if(this.typeAhead){
13513 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13515 if(this.editable !== false){
13516 this.inputEl().on("keyup", this.onKeyUp, this);
13518 if(this.forceSelection){
13519 this.inputEl().on('blur', this.doForce, this);
13523 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13524 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13528 initTickableEvents: function()
13532 if(this.hiddenName){
13534 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13536 this.hiddenField.dom.value =
13537 this.hiddenValue !== undefined ? this.hiddenValue :
13538 this.value !== undefined ? this.value : '';
13540 // prevent input submission
13541 this.el.dom.removeAttribute('name');
13542 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13547 // this.list = this.el.select('ul.dropdown-menu',true).first();
13549 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13550 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13551 if(this.triggerList){
13552 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13555 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13556 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13558 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13559 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13561 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13562 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13564 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13565 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13566 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13569 this.cancelBtn.hide();
13574 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13575 _this.list.setWidth(lw);
13578 this.list.on('mouseover', this.onViewOver, this);
13579 this.list.on('mousemove', this.onViewMove, this);
13581 this.list.on('scroll', this.onViewScroll, this);
13584 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13587 this.view = new Roo.View(this.list, this.tpl, {
13588 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13591 //this.view.wrapEl.setDisplayed(false);
13592 this.view.on('click', this.onViewClick, this);
13596 this.store.on('beforeload', this.onBeforeLoad, this);
13597 this.store.on('load', this.onLoad, this);
13598 this.store.on('loadexception', this.onLoadException, this);
13601 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13602 "up" : function(e){
13603 this.inKeyMode = true;
13607 "down" : function(e){
13608 this.inKeyMode = true;
13612 "enter" : function(e){
13613 if(this.fireEvent("specialkey", this, e)){
13614 this.onViewClick(false);
13620 "esc" : function(e){
13621 this.onTickableFooterButtonClick(e, false, false);
13624 "tab" : function(e){
13625 this.fireEvent("specialkey", this, e);
13627 this.onTickableFooterButtonClick(e, false, false);
13634 doRelay : function(e, fn, key){
13635 if(this.scope.isExpanded()){
13636 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13645 this.queryDelay = Math.max(this.queryDelay || 10,
13646 this.mode == 'local' ? 10 : 250);
13649 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13651 if(this.typeAhead){
13652 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13655 if(this.editable !== false){
13656 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13659 this.indicator = this.indicatorEl();
13661 if(this.indicator){
13662 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13663 this.indicator.hide();
13668 onDestroy : function(){
13670 this.view.setStore(null);
13671 this.view.el.removeAllListeners();
13672 this.view.el.remove();
13673 this.view.purgeListeners();
13676 this.list.dom.innerHTML = '';
13680 this.store.un('beforeload', this.onBeforeLoad, this);
13681 this.store.un('load', this.onLoad, this);
13682 this.store.un('loadexception', this.onLoadException, this);
13684 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13688 fireKey : function(e){
13689 if(e.isNavKeyPress() && !this.list.isVisible()){
13690 this.fireEvent("specialkey", this, e);
13695 onResize: function(w, h){
13696 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13698 // if(typeof w != 'number'){
13699 // // we do not handle it!?!?
13702 // var tw = this.trigger.getWidth();
13703 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13704 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13706 // this.inputEl().setWidth( this.adjustWidth('input', x));
13708 // //this.trigger.setStyle('left', x+'px');
13710 // if(this.list && this.listWidth === undefined){
13711 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13712 // this.list.setWidth(lw);
13713 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13721 * Allow or prevent the user from directly editing the field text. If false is passed,
13722 * the user will only be able to select from the items defined in the dropdown list. This method
13723 * is the runtime equivalent of setting the 'editable' config option at config time.
13724 * @param {Boolean} value True to allow the user to directly edit the field text
13726 setEditable : function(value){
13727 if(value == this.editable){
13730 this.editable = value;
13732 this.inputEl().dom.setAttribute('readOnly', true);
13733 this.inputEl().on('mousedown', this.onTriggerClick, this);
13734 this.inputEl().addClass('x-combo-noedit');
13736 this.inputEl().dom.setAttribute('readOnly', false);
13737 this.inputEl().un('mousedown', this.onTriggerClick, this);
13738 this.inputEl().removeClass('x-combo-noedit');
13744 onBeforeLoad : function(combo,opts){
13745 if(!this.hasFocus){
13749 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13751 this.restrictHeight();
13752 this.selectedIndex = -1;
13756 onLoad : function(){
13758 this.hasQuery = false;
13760 if(!this.hasFocus){
13764 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13765 this.loading.hide();
13768 if(this.store.getCount() > 0){
13771 this.restrictHeight();
13772 if(this.lastQuery == this.allQuery){
13773 if(this.editable && !this.tickable){
13774 this.inputEl().dom.select();
13778 !this.selectByValue(this.value, true) &&
13781 !this.store.lastOptions ||
13782 typeof(this.store.lastOptions.add) == 'undefined' ||
13783 this.store.lastOptions.add != true
13786 this.select(0, true);
13789 if(this.autoFocus){
13792 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13793 this.taTask.delay(this.typeAheadDelay);
13797 this.onEmptyResults();
13803 onLoadException : function()
13805 this.hasQuery = false;
13807 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13808 this.loading.hide();
13811 if(this.tickable && this.editable){
13816 // only causes errors at present
13817 //Roo.log(this.store.reader.jsonData);
13818 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13820 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13826 onTypeAhead : function(){
13827 if(this.store.getCount() > 0){
13828 var r = this.store.getAt(0);
13829 var newValue = r.data[this.displayField];
13830 var len = newValue.length;
13831 var selStart = this.getRawValue().length;
13833 if(selStart != len){
13834 this.setRawValue(newValue);
13835 this.selectText(selStart, newValue.length);
13841 onSelect : function(record, index){
13843 if(this.fireEvent('beforeselect', this, record, index) !== false){
13845 this.setFromData(index > -1 ? record.data : false);
13848 this.fireEvent('select', this, record, index);
13853 * Returns the currently selected field value or empty string if no value is set.
13854 * @return {String} value The selected value
13856 getValue : function()
13858 if(Roo.isIOS && this.useNativeIOS){
13859 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13863 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13866 if(this.valueField){
13867 return typeof this.value != 'undefined' ? this.value : '';
13869 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13873 getRawValue : function()
13875 if(Roo.isIOS && this.useNativeIOS){
13876 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13879 var v = this.inputEl().getValue();
13885 * Clears any text/value currently set in the field
13887 clearValue : function(){
13889 if(this.hiddenField){
13890 this.hiddenField.dom.value = '';
13893 this.setRawValue('');
13894 this.lastSelectionText = '';
13895 this.lastData = false;
13897 var close = this.closeTriggerEl();
13908 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13909 * will be displayed in the field. If the value does not match the data value of an existing item,
13910 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13911 * Otherwise the field will be blank (although the value will still be set).
13912 * @param {String} value The value to match
13914 setValue : function(v)
13916 if(Roo.isIOS && this.useNativeIOS){
13917 this.setIOSValue(v);
13927 if(this.valueField){
13928 var r = this.findRecord(this.valueField, v);
13930 text = r.data[this.displayField];
13931 }else if(this.valueNotFoundText !== undefined){
13932 text = this.valueNotFoundText;
13935 this.lastSelectionText = text;
13936 if(this.hiddenField){
13937 this.hiddenField.dom.value = v;
13939 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13942 var close = this.closeTriggerEl();
13945 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13951 * @property {Object} the last set data for the element
13956 * Sets the value of the field based on a object which is related to the record format for the store.
13957 * @param {Object} value the value to set as. or false on reset?
13959 setFromData : function(o){
13966 var dv = ''; // display value
13967 var vv = ''; // value value..
13969 if (this.displayField) {
13970 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13972 // this is an error condition!!!
13973 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13976 if(this.valueField){
13977 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13980 var close = this.closeTriggerEl();
13983 if(dv.length || vv * 1 > 0){
13985 this.blockFocus=true;
13991 if(this.hiddenField){
13992 this.hiddenField.dom.value = vv;
13994 this.lastSelectionText = dv;
13995 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13999 // no hidden field.. - we store the value in 'value', but still display
14000 // display field!!!!
14001 this.lastSelectionText = dv;
14002 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14009 reset : function(){
14010 // overridden so that last data is reset..
14017 this.setValue(this.originalValue);
14018 //this.clearInvalid();
14019 this.lastData = false;
14021 this.view.clearSelections();
14027 findRecord : function(prop, value){
14029 if(this.store.getCount() > 0){
14030 this.store.each(function(r){
14031 if(r.data[prop] == value){
14041 getName: function()
14043 // returns hidden if it's set..
14044 if (!this.rendered) {return ''};
14045 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14049 onViewMove : function(e, t){
14050 this.inKeyMode = false;
14054 onViewOver : function(e, t){
14055 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14058 var item = this.view.findItemFromChild(t);
14061 var index = this.view.indexOf(item);
14062 this.select(index, false);
14067 onViewClick : function(view, doFocus, el, e)
14069 var index = this.view.getSelectedIndexes()[0];
14071 var r = this.store.getAt(index);
14075 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14082 Roo.each(this.tickItems, function(v,k){
14084 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14086 _this.tickItems.splice(k, 1);
14088 if(typeof(e) == 'undefined' && view == false){
14089 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14101 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14102 this.tickItems.push(r.data);
14105 if(typeof(e) == 'undefined' && view == false){
14106 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14113 this.onSelect(r, index);
14115 if(doFocus !== false && !this.blockFocus){
14116 this.inputEl().focus();
14121 restrictHeight : function(){
14122 //this.innerList.dom.style.height = '';
14123 //var inner = this.innerList.dom;
14124 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14125 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14126 //this.list.beginUpdate();
14127 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14128 this.list.alignTo(this.inputEl(), this.listAlign);
14129 this.list.alignTo(this.inputEl(), this.listAlign);
14130 //this.list.endUpdate();
14134 onEmptyResults : function(){
14136 if(this.tickable && this.editable){
14137 this.hasFocus = false;
14138 this.restrictHeight();
14146 * Returns true if the dropdown list is expanded, else false.
14148 isExpanded : function(){
14149 return this.list.isVisible();
14153 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14154 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14155 * @param {String} value The data value of the item to select
14156 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14157 * selected item if it is not currently in view (defaults to true)
14158 * @return {Boolean} True if the value matched an item in the list, else false
14160 selectByValue : function(v, scrollIntoView){
14161 if(v !== undefined && v !== null){
14162 var r = this.findRecord(this.valueField || this.displayField, v);
14164 this.select(this.store.indexOf(r), scrollIntoView);
14172 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14173 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14174 * @param {Number} index The zero-based index of the list item to select
14175 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14176 * selected item if it is not currently in view (defaults to true)
14178 select : function(index, scrollIntoView){
14179 this.selectedIndex = index;
14180 this.view.select(index);
14181 if(scrollIntoView !== false){
14182 var el = this.view.getNode(index);
14184 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14187 this.list.scrollChildIntoView(el, false);
14193 selectNext : function(){
14194 var ct = this.store.getCount();
14196 if(this.selectedIndex == -1){
14198 }else if(this.selectedIndex < ct-1){
14199 this.select(this.selectedIndex+1);
14205 selectPrev : function(){
14206 var ct = this.store.getCount();
14208 if(this.selectedIndex == -1){
14210 }else if(this.selectedIndex != 0){
14211 this.select(this.selectedIndex-1);
14217 onKeyUp : function(e){
14218 if(this.editable !== false && !e.isSpecialKey()){
14219 this.lastKey = e.getKey();
14220 this.dqTask.delay(this.queryDelay);
14225 validateBlur : function(){
14226 return !this.list || !this.list.isVisible();
14230 initQuery : function(){
14232 var v = this.getRawValue();
14234 if(this.tickable && this.editable){
14235 v = this.tickableInputEl().getValue();
14242 doForce : function(){
14243 if(this.inputEl().dom.value.length > 0){
14244 this.inputEl().dom.value =
14245 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14251 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14252 * query allowing the query action to be canceled if needed.
14253 * @param {String} query The SQL query to execute
14254 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14255 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14256 * saved in the current store (defaults to false)
14258 doQuery : function(q, forceAll){
14260 if(q === undefined || q === null){
14265 forceAll: forceAll,
14269 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14274 forceAll = qe.forceAll;
14275 if(forceAll === true || (q.length >= this.minChars)){
14277 this.hasQuery = true;
14279 if(this.lastQuery != q || this.alwaysQuery){
14280 this.lastQuery = q;
14281 if(this.mode == 'local'){
14282 this.selectedIndex = -1;
14284 this.store.clearFilter();
14287 if(this.specialFilter){
14288 this.fireEvent('specialfilter', this);
14293 this.store.filter(this.displayField, q);
14296 this.store.fireEvent("datachanged", this.store);
14303 this.store.baseParams[this.queryParam] = q;
14305 var options = {params : this.getParams(q)};
14308 options.add = true;
14309 options.params.start = this.page * this.pageSize;
14312 this.store.load(options);
14315 * this code will make the page width larger, at the beginning, the list not align correctly,
14316 * we should expand the list on onLoad
14317 * so command out it
14322 this.selectedIndex = -1;
14327 this.loadNext = false;
14331 getParams : function(q){
14333 //p[this.queryParam] = q;
14337 p.limit = this.pageSize;
14343 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14345 collapse : function(){
14346 if(!this.isExpanded()){
14352 this.hasFocus = false;
14356 this.cancelBtn.hide();
14357 this.trigger.show();
14360 this.tickableInputEl().dom.value = '';
14361 this.tickableInputEl().blur();
14366 Roo.get(document).un('mousedown', this.collapseIf, this);
14367 Roo.get(document).un('mousewheel', this.collapseIf, this);
14368 if (!this.editable) {
14369 Roo.get(document).un('keydown', this.listKeyPress, this);
14371 this.fireEvent('collapse', this);
14377 collapseIf : function(e){
14378 var in_combo = e.within(this.el);
14379 var in_list = e.within(this.list);
14380 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14382 if (in_combo || in_list || is_list) {
14383 //e.stopPropagation();
14388 this.onTickableFooterButtonClick(e, false, false);
14396 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14398 expand : function(){
14400 if(this.isExpanded() || !this.hasFocus){
14404 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14405 this.list.setWidth(lw);
14411 this.restrictHeight();
14415 this.tickItems = Roo.apply([], this.item);
14418 this.cancelBtn.show();
14419 this.trigger.hide();
14422 this.tickableInputEl().focus();
14427 Roo.get(document).on('mousedown', this.collapseIf, this);
14428 Roo.get(document).on('mousewheel', this.collapseIf, this);
14429 if (!this.editable) {
14430 Roo.get(document).on('keydown', this.listKeyPress, this);
14433 this.fireEvent('expand', this);
14437 // Implements the default empty TriggerField.onTriggerClick function
14438 onTriggerClick : function(e)
14440 Roo.log('trigger click');
14442 if(this.disabled || !this.triggerList){
14447 this.loadNext = false;
14449 if(this.isExpanded()){
14451 if (!this.blockFocus) {
14452 this.inputEl().focus();
14456 this.hasFocus = true;
14457 if(this.triggerAction == 'all') {
14458 this.doQuery(this.allQuery, true);
14460 this.doQuery(this.getRawValue());
14462 if (!this.blockFocus) {
14463 this.inputEl().focus();
14468 onTickableTriggerClick : function(e)
14475 this.loadNext = false;
14476 this.hasFocus = true;
14478 if(this.triggerAction == 'all') {
14479 this.doQuery(this.allQuery, true);
14481 this.doQuery(this.getRawValue());
14485 onSearchFieldClick : function(e)
14487 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14488 this.onTickableFooterButtonClick(e, false, false);
14492 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14497 this.loadNext = false;
14498 this.hasFocus = true;
14500 if(this.triggerAction == 'all') {
14501 this.doQuery(this.allQuery, true);
14503 this.doQuery(this.getRawValue());
14507 listKeyPress : function(e)
14509 //Roo.log('listkeypress');
14510 // scroll to first matching element based on key pres..
14511 if (e.isSpecialKey()) {
14514 var k = String.fromCharCode(e.getKey()).toUpperCase();
14517 var csel = this.view.getSelectedNodes();
14518 var cselitem = false;
14520 var ix = this.view.indexOf(csel[0]);
14521 cselitem = this.store.getAt(ix);
14522 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14528 this.store.each(function(v) {
14530 // start at existing selection.
14531 if (cselitem.id == v.id) {
14537 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14538 match = this.store.indexOf(v);
14544 if (match === false) {
14545 return true; // no more action?
14548 this.view.select(match);
14549 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14550 sn.scrollIntoView(sn.dom.parentNode, false);
14553 onViewScroll : function(e, t){
14555 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
14559 this.hasQuery = true;
14561 this.loading = this.list.select('.loading', true).first();
14563 if(this.loading === null){
14564 this.list.createChild({
14566 cls: 'loading roo-select2-more-results roo-select2-active',
14567 html: 'Loading more results...'
14570 this.loading = this.list.select('.loading', true).first();
14572 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14574 this.loading.hide();
14577 this.loading.show();
14582 this.loadNext = true;
14584 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14589 addItem : function(o)
14591 var dv = ''; // display value
14593 if (this.displayField) {
14594 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14596 // this is an error condition!!!
14597 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14604 var choice = this.choices.createChild({
14606 cls: 'roo-select2-search-choice',
14615 cls: 'roo-select2-search-choice-close fa fa-times',
14620 }, this.searchField);
14622 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14624 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14632 this.inputEl().dom.value = '';
14637 onRemoveItem : function(e, _self, o)
14639 e.preventDefault();
14641 this.lastItem = Roo.apply([], this.item);
14643 var index = this.item.indexOf(o.data) * 1;
14646 Roo.log('not this item?!');
14650 this.item.splice(index, 1);
14655 this.fireEvent('remove', this, e);
14661 syncValue : function()
14663 if(!this.item.length){
14670 Roo.each(this.item, function(i){
14671 if(_this.valueField){
14672 value.push(i[_this.valueField]);
14679 this.value = value.join(',');
14681 if(this.hiddenField){
14682 this.hiddenField.dom.value = this.value;
14685 this.store.fireEvent("datachanged", this.store);
14690 clearItem : function()
14692 if(!this.multiple){
14698 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14706 if(this.tickable && !Roo.isTouch){
14707 this.view.refresh();
14711 inputEl: function ()
14713 if(Roo.isIOS && this.useNativeIOS){
14714 return this.el.select('select.roo-ios-select', true).first();
14717 if(Roo.isTouch && this.mobileTouchView){
14718 return this.el.select('input.form-control',true).first();
14722 return this.searchField;
14725 return this.el.select('input.form-control',true).first();
14728 onTickableFooterButtonClick : function(e, btn, el)
14730 e.preventDefault();
14732 this.lastItem = Roo.apply([], this.item);
14734 if(btn && btn.name == 'cancel'){
14735 this.tickItems = Roo.apply([], this.item);
14744 Roo.each(this.tickItems, function(o){
14752 validate : function()
14754 if(this.getVisibilityEl().hasClass('hidden')){
14758 var v = this.getRawValue();
14761 v = this.getValue();
14764 if(this.disabled || this.allowBlank || v.length){
14769 this.markInvalid();
14773 tickableInputEl : function()
14775 if(!this.tickable || !this.editable){
14776 return this.inputEl();
14779 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14783 getAutoCreateTouchView : function()
14788 cls: 'form-group' //input-group
14794 type : this.inputType,
14795 cls : 'form-control x-combo-noedit',
14796 autocomplete: 'new-password',
14797 placeholder : this.placeholder || '',
14802 input.name = this.name;
14806 input.cls += ' input-' + this.size;
14809 if (this.disabled) {
14810 input.disabled = true;
14821 inputblock.cls += ' input-group';
14823 inputblock.cn.unshift({
14825 cls : 'input-group-addon',
14830 if(this.removable && !this.multiple){
14831 inputblock.cls += ' roo-removable';
14833 inputblock.cn.push({
14836 cls : 'roo-combo-removable-btn close'
14840 if(this.hasFeedback && !this.allowBlank){
14842 inputblock.cls += ' has-feedback';
14844 inputblock.cn.push({
14846 cls: 'glyphicon form-control-feedback'
14853 inputblock.cls += (this.before) ? '' : ' input-group';
14855 inputblock.cn.push({
14857 cls : 'input-group-addon',
14868 cls: 'form-hidden-field'
14882 cls: 'form-hidden-field'
14886 cls: 'roo-select2-choices',
14890 cls: 'roo-select2-search-field',
14903 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14909 if(!this.multiple && this.showToggleBtn){
14916 if (this.caret != false) {
14919 cls: 'fa fa-' + this.caret
14926 cls : 'input-group-addon btn dropdown-toggle',
14931 cls: 'combobox-clear',
14945 combobox.cls += ' roo-select2-container-multi';
14948 var align = this.labelAlign || this.parentLabelAlign();
14950 if (align ==='left' && this.fieldLabel.length) {
14955 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14956 tooltip : 'This field is required'
14960 cls : 'control-label',
14961 html : this.fieldLabel
14972 var labelCfg = cfg.cn[1];
14973 var contentCfg = cfg.cn[2];
14976 if(this.indicatorpos == 'right'){
14981 cls : 'control-label',
14985 html : this.fieldLabel
14989 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14990 tooltip : 'This field is required'
15003 labelCfg = cfg.cn[0];
15004 contentCfg = cfg.cn[1];
15009 if(this.labelWidth > 12){
15010 labelCfg.style = "width: " + this.labelWidth + 'px';
15013 if(this.labelWidth < 13 && this.labelmd == 0){
15014 this.labelmd = this.labelWidth;
15017 if(this.labellg > 0){
15018 labelCfg.cls += ' col-lg-' + this.labellg;
15019 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15022 if(this.labelmd > 0){
15023 labelCfg.cls += ' col-md-' + this.labelmd;
15024 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15027 if(this.labelsm > 0){
15028 labelCfg.cls += ' col-sm-' + this.labelsm;
15029 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15032 if(this.labelxs > 0){
15033 labelCfg.cls += ' col-xs-' + this.labelxs;
15034 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15038 } else if ( this.fieldLabel.length) {
15042 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15043 tooltip : 'This field is required'
15047 cls : 'control-label',
15048 html : this.fieldLabel
15059 if(this.indicatorpos == 'right'){
15063 cls : 'control-label',
15064 html : this.fieldLabel,
15068 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15069 tooltip : 'This field is required'
15086 var settings = this;
15088 ['xs','sm','md','lg'].map(function(size){
15089 if (settings[size]) {
15090 cfg.cls += ' col-' + size + '-' + settings[size];
15097 initTouchView : function()
15099 this.renderTouchView();
15101 this.touchViewEl.on('scroll', function(){
15102 this.el.dom.scrollTop = 0;
15105 this.originalValue = this.getValue();
15107 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15109 this.inputEl().on("click", this.showTouchView, this);
15110 if (this.triggerEl) {
15111 this.triggerEl.on("click", this.showTouchView, this);
15115 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15116 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15118 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15120 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15121 this.store.on('load', this.onTouchViewLoad, this);
15122 this.store.on('loadexception', this.onTouchViewLoadException, this);
15124 if(this.hiddenName){
15126 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15128 this.hiddenField.dom.value =
15129 this.hiddenValue !== undefined ? this.hiddenValue :
15130 this.value !== undefined ? this.value : '';
15132 this.el.dom.removeAttribute('name');
15133 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15137 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15138 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15141 if(this.removable && !this.multiple){
15142 var close = this.closeTriggerEl();
15144 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15145 close.on('click', this.removeBtnClick, this, close);
15149 * fix the bug in Safari iOS8
15151 this.inputEl().on("focus", function(e){
15152 document.activeElement.blur();
15160 renderTouchView : function()
15162 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15163 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15165 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15166 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15168 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15169 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15170 this.touchViewBodyEl.setStyle('overflow', 'auto');
15172 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15173 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15175 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15176 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15180 showTouchView : function()
15186 this.touchViewHeaderEl.hide();
15188 if(this.modalTitle.length){
15189 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15190 this.touchViewHeaderEl.show();
15193 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15194 this.touchViewEl.show();
15196 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15198 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15199 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15201 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15203 if(this.modalTitle.length){
15204 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15207 this.touchViewBodyEl.setHeight(bodyHeight);
15211 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15213 this.touchViewEl.addClass('in');
15216 this.doTouchViewQuery();
15220 hideTouchView : function()
15222 this.touchViewEl.removeClass('in');
15226 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15228 this.touchViewEl.setStyle('display', 'none');
15233 setTouchViewValue : function()
15240 Roo.each(this.tickItems, function(o){
15245 this.hideTouchView();
15248 doTouchViewQuery : function()
15257 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15261 if(!this.alwaysQuery || this.mode == 'local'){
15262 this.onTouchViewLoad();
15269 onTouchViewBeforeLoad : function(combo,opts)
15275 onTouchViewLoad : function()
15277 if(this.store.getCount() < 1){
15278 this.onTouchViewEmptyResults();
15282 this.clearTouchView();
15284 var rawValue = this.getRawValue();
15286 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15288 this.tickItems = [];
15290 this.store.data.each(function(d, rowIndex){
15291 var row = this.touchViewListGroup.createChild(template);
15293 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15294 row.addClass(d.data.cls);
15297 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15300 html : d.data[this.displayField]
15303 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15304 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15307 row.removeClass('selected');
15308 if(!this.multiple && this.valueField &&
15309 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15312 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15313 row.addClass('selected');
15316 if(this.multiple && this.valueField &&
15317 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15321 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15322 this.tickItems.push(d.data);
15325 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15329 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15331 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15333 if(this.modalTitle.length){
15334 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15337 var listHeight = this.touchViewListGroup.getHeight();
15341 if(firstChecked && listHeight > bodyHeight){
15342 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15347 onTouchViewLoadException : function()
15349 this.hideTouchView();
15352 onTouchViewEmptyResults : function()
15354 this.clearTouchView();
15356 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15358 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15362 clearTouchView : function()
15364 this.touchViewListGroup.dom.innerHTML = '';
15367 onTouchViewClick : function(e, el, o)
15369 e.preventDefault();
15372 var rowIndex = o.rowIndex;
15374 var r = this.store.getAt(rowIndex);
15376 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15378 if(!this.multiple){
15379 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15380 c.dom.removeAttribute('checked');
15383 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15385 this.setFromData(r.data);
15387 var close = this.closeTriggerEl();
15393 this.hideTouchView();
15395 this.fireEvent('select', this, r, rowIndex);
15400 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15401 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15402 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15406 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15407 this.addItem(r.data);
15408 this.tickItems.push(r.data);
15412 getAutoCreateNativeIOS : function()
15415 cls: 'form-group' //input-group,
15420 cls : 'roo-ios-select'
15424 combobox.name = this.name;
15427 if (this.disabled) {
15428 combobox.disabled = true;
15431 var settings = this;
15433 ['xs','sm','md','lg'].map(function(size){
15434 if (settings[size]) {
15435 cfg.cls += ' col-' + size + '-' + settings[size];
15445 initIOSView : function()
15447 this.store.on('load', this.onIOSViewLoad, this);
15452 onIOSViewLoad : function()
15454 if(this.store.getCount() < 1){
15458 this.clearIOSView();
15460 if(this.allowBlank) {
15462 var default_text = '-- SELECT --';
15464 if(this.placeholder.length){
15465 default_text = this.placeholder;
15468 if(this.emptyTitle.length){
15469 default_text += ' - ' + this.emptyTitle + ' -';
15472 var opt = this.inputEl().createChild({
15475 html : default_text
15479 o[this.valueField] = 0;
15480 o[this.displayField] = default_text;
15482 this.ios_options.push({
15489 this.store.data.each(function(d, rowIndex){
15493 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15494 html = d.data[this.displayField];
15499 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15500 value = d.data[this.valueField];
15509 if(this.value == d.data[this.valueField]){
15510 option['selected'] = true;
15513 var opt = this.inputEl().createChild(option);
15515 this.ios_options.push({
15522 this.inputEl().on('change', function(){
15523 this.fireEvent('select', this);
15528 clearIOSView: function()
15530 this.inputEl().dom.innerHTML = '';
15532 this.ios_options = [];
15535 setIOSValue: function(v)
15539 if(!this.ios_options){
15543 Roo.each(this.ios_options, function(opts){
15545 opts.el.dom.removeAttribute('selected');
15547 if(opts.data[this.valueField] != v){
15551 opts.el.dom.setAttribute('selected', true);
15557 * @cfg {Boolean} grow
15561 * @cfg {Number} growMin
15565 * @cfg {Number} growMax
15574 Roo.apply(Roo.bootstrap.ComboBox, {
15578 cls: 'modal-header',
15600 cls: 'list-group-item',
15604 cls: 'roo-combobox-list-group-item-value'
15608 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15622 listItemCheckbox : {
15624 cls: 'list-group-item',
15628 cls: 'roo-combobox-list-group-item-value'
15632 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15648 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15653 cls: 'modal-footer',
15661 cls: 'col-xs-6 text-left',
15664 cls: 'btn btn-danger roo-touch-view-cancel',
15670 cls: 'col-xs-6 text-right',
15673 cls: 'btn btn-success roo-touch-view-ok',
15684 Roo.apply(Roo.bootstrap.ComboBox, {
15686 touchViewTemplate : {
15688 cls: 'modal fade roo-combobox-touch-view',
15692 cls: 'modal-dialog',
15693 style : 'position:fixed', // we have to fix position....
15697 cls: 'modal-content',
15699 Roo.bootstrap.ComboBox.header,
15700 Roo.bootstrap.ComboBox.body,
15701 Roo.bootstrap.ComboBox.footer
15710 * Ext JS Library 1.1.1
15711 * Copyright(c) 2006-2007, Ext JS, LLC.
15713 * Originally Released Under LGPL - original licence link has changed is not relivant.
15716 * <script type="text/javascript">
15721 * @extends Roo.util.Observable
15722 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15723 * This class also supports single and multi selection modes. <br>
15724 * Create a data model bound view:
15726 var store = new Roo.data.Store(...);
15728 var view = new Roo.View({
15730 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15732 singleSelect: true,
15733 selectedClass: "ydataview-selected",
15737 // listen for node click?
15738 view.on("click", function(vw, index, node, e){
15739 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15743 dataModel.load("foobar.xml");
15745 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15747 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15748 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15750 * Note: old style constructor is still suported (container, template, config)
15753 * Create a new View
15754 * @param {Object} config The config object
15757 Roo.View = function(config, depreciated_tpl, depreciated_config){
15759 this.parent = false;
15761 if (typeof(depreciated_tpl) == 'undefined') {
15762 // new way.. - universal constructor.
15763 Roo.apply(this, config);
15764 this.el = Roo.get(this.el);
15767 this.el = Roo.get(config);
15768 this.tpl = depreciated_tpl;
15769 Roo.apply(this, depreciated_config);
15771 this.wrapEl = this.el.wrap().wrap();
15772 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15775 if(typeof(this.tpl) == "string"){
15776 this.tpl = new Roo.Template(this.tpl);
15778 // support xtype ctors..
15779 this.tpl = new Roo.factory(this.tpl, Roo);
15783 this.tpl.compile();
15788 * @event beforeclick
15789 * Fires before a click is processed. Returns false to cancel the default action.
15790 * @param {Roo.View} this
15791 * @param {Number} index The index of the target node
15792 * @param {HTMLElement} node The target node
15793 * @param {Roo.EventObject} e The raw event object
15795 "beforeclick" : true,
15798 * Fires when a template node is clicked.
15799 * @param {Roo.View} this
15800 * @param {Number} index The index of the target node
15801 * @param {HTMLElement} node The target node
15802 * @param {Roo.EventObject} e The raw event object
15807 * Fires when a template node is double clicked.
15808 * @param {Roo.View} this
15809 * @param {Number} index The index of the target node
15810 * @param {HTMLElement} node The target node
15811 * @param {Roo.EventObject} e The raw event object
15815 * @event contextmenu
15816 * Fires when a template node is right clicked.
15817 * @param {Roo.View} this
15818 * @param {Number} index The index of the target node
15819 * @param {HTMLElement} node The target node
15820 * @param {Roo.EventObject} e The raw event object
15822 "contextmenu" : true,
15824 * @event selectionchange
15825 * Fires when the selected nodes change.
15826 * @param {Roo.View} this
15827 * @param {Array} selections Array of the selected nodes
15829 "selectionchange" : true,
15832 * @event beforeselect
15833 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15834 * @param {Roo.View} this
15835 * @param {HTMLElement} node The node to be selected
15836 * @param {Array} selections Array of currently selected nodes
15838 "beforeselect" : true,
15840 * @event preparedata
15841 * Fires on every row to render, to allow you to change the data.
15842 * @param {Roo.View} this
15843 * @param {Object} data to be rendered (change this)
15845 "preparedata" : true
15853 "click": this.onClick,
15854 "dblclick": this.onDblClick,
15855 "contextmenu": this.onContextMenu,
15859 this.selections = [];
15861 this.cmp = new Roo.CompositeElementLite([]);
15863 this.store = Roo.factory(this.store, Roo.data);
15864 this.setStore(this.store, true);
15867 if ( this.footer && this.footer.xtype) {
15869 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15871 this.footer.dataSource = this.store;
15872 this.footer.container = fctr;
15873 this.footer = Roo.factory(this.footer, Roo);
15874 fctr.insertFirst(this.el);
15876 // this is a bit insane - as the paging toolbar seems to detach the el..
15877 // dom.parentNode.parentNode.parentNode
15878 // they get detached?
15882 Roo.View.superclass.constructor.call(this);
15887 Roo.extend(Roo.View, Roo.util.Observable, {
15890 * @cfg {Roo.data.Store} store Data store to load data from.
15895 * @cfg {String|Roo.Element} el The container element.
15900 * @cfg {String|Roo.Template} tpl The template used by this View
15904 * @cfg {String} dataName the named area of the template to use as the data area
15905 * Works with domtemplates roo-name="name"
15909 * @cfg {String} selectedClass The css class to add to selected nodes
15911 selectedClass : "x-view-selected",
15913 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15918 * @cfg {String} text to display on mask (default Loading)
15922 * @cfg {Boolean} multiSelect Allow multiple selection
15924 multiSelect : false,
15926 * @cfg {Boolean} singleSelect Allow single selection
15928 singleSelect: false,
15931 * @cfg {Boolean} toggleSelect - selecting
15933 toggleSelect : false,
15936 * @cfg {Boolean} tickable - selecting
15941 * Returns the element this view is bound to.
15942 * @return {Roo.Element}
15944 getEl : function(){
15945 return this.wrapEl;
15951 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15953 refresh : function(){
15954 //Roo.log('refresh');
15957 // if we are using something like 'domtemplate', then
15958 // the what gets used is:
15959 // t.applySubtemplate(NAME, data, wrapping data..)
15960 // the outer template then get' applied with
15961 // the store 'extra data'
15962 // and the body get's added to the
15963 // roo-name="data" node?
15964 // <span class='roo-tpl-{name}'></span> ?????
15968 this.clearSelections();
15969 this.el.update("");
15971 var records = this.store.getRange();
15972 if(records.length < 1) {
15974 // is this valid?? = should it render a template??
15976 this.el.update(this.emptyText);
15980 if (this.dataName) {
15981 this.el.update(t.apply(this.store.meta)); //????
15982 el = this.el.child('.roo-tpl-' + this.dataName);
15985 for(var i = 0, len = records.length; i < len; i++){
15986 var data = this.prepareData(records[i].data, i, records[i]);
15987 this.fireEvent("preparedata", this, data, i, records[i]);
15989 var d = Roo.apply({}, data);
15992 Roo.apply(d, {'roo-id' : Roo.id()});
15996 Roo.each(this.parent.item, function(item){
15997 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16000 Roo.apply(d, {'roo-data-checked' : 'checked'});
16004 html[html.length] = Roo.util.Format.trim(
16006 t.applySubtemplate(this.dataName, d, this.store.meta) :
16013 el.update(html.join(""));
16014 this.nodes = el.dom.childNodes;
16015 this.updateIndexes(0);
16020 * Function to override to reformat the data that is sent to
16021 * the template for each node.
16022 * DEPRICATED - use the preparedata event handler.
16023 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16024 * a JSON object for an UpdateManager bound view).
16026 prepareData : function(data, index, record)
16028 this.fireEvent("preparedata", this, data, index, record);
16032 onUpdate : function(ds, record){
16033 // Roo.log('on update');
16034 this.clearSelections();
16035 var index = this.store.indexOf(record);
16036 var n = this.nodes[index];
16037 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16038 n.parentNode.removeChild(n);
16039 this.updateIndexes(index, index);
16045 onAdd : function(ds, records, index)
16047 //Roo.log(['on Add', ds, records, index] );
16048 this.clearSelections();
16049 if(this.nodes.length == 0){
16053 var n = this.nodes[index];
16054 for(var i = 0, len = records.length; i < len; i++){
16055 var d = this.prepareData(records[i].data, i, records[i]);
16057 this.tpl.insertBefore(n, d);
16060 this.tpl.append(this.el, d);
16063 this.updateIndexes(index);
16066 onRemove : function(ds, record, index){
16067 // Roo.log('onRemove');
16068 this.clearSelections();
16069 var el = this.dataName ?
16070 this.el.child('.roo-tpl-' + this.dataName) :
16073 el.dom.removeChild(this.nodes[index]);
16074 this.updateIndexes(index);
16078 * Refresh an individual node.
16079 * @param {Number} index
16081 refreshNode : function(index){
16082 this.onUpdate(this.store, this.store.getAt(index));
16085 updateIndexes : function(startIndex, endIndex){
16086 var ns = this.nodes;
16087 startIndex = startIndex || 0;
16088 endIndex = endIndex || ns.length - 1;
16089 for(var i = startIndex; i <= endIndex; i++){
16090 ns[i].nodeIndex = i;
16095 * Changes the data store this view uses and refresh the view.
16096 * @param {Store} store
16098 setStore : function(store, initial){
16099 if(!initial && this.store){
16100 this.store.un("datachanged", this.refresh);
16101 this.store.un("add", this.onAdd);
16102 this.store.un("remove", this.onRemove);
16103 this.store.un("update", this.onUpdate);
16104 this.store.un("clear", this.refresh);
16105 this.store.un("beforeload", this.onBeforeLoad);
16106 this.store.un("load", this.onLoad);
16107 this.store.un("loadexception", this.onLoad);
16111 store.on("datachanged", this.refresh, this);
16112 store.on("add", this.onAdd, this);
16113 store.on("remove", this.onRemove, this);
16114 store.on("update", this.onUpdate, this);
16115 store.on("clear", this.refresh, this);
16116 store.on("beforeload", this.onBeforeLoad, this);
16117 store.on("load", this.onLoad, this);
16118 store.on("loadexception", this.onLoad, this);
16126 * onbeforeLoad - masks the loading area.
16129 onBeforeLoad : function(store,opts)
16131 //Roo.log('onBeforeLoad');
16133 this.el.update("");
16135 this.el.mask(this.mask ? this.mask : "Loading" );
16137 onLoad : function ()
16144 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16145 * @param {HTMLElement} node
16146 * @return {HTMLElement} The template node
16148 findItemFromChild : function(node){
16149 var el = this.dataName ?
16150 this.el.child('.roo-tpl-' + this.dataName,true) :
16153 if(!node || node.parentNode == el){
16156 var p = node.parentNode;
16157 while(p && p != el){
16158 if(p.parentNode == el){
16167 onClick : function(e){
16168 var item = this.findItemFromChild(e.getTarget());
16170 var index = this.indexOf(item);
16171 if(this.onItemClick(item, index, e) !== false){
16172 this.fireEvent("click", this, index, item, e);
16175 this.clearSelections();
16180 onContextMenu : function(e){
16181 var item = this.findItemFromChild(e.getTarget());
16183 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16188 onDblClick : function(e){
16189 var item = this.findItemFromChild(e.getTarget());
16191 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16195 onItemClick : function(item, index, e)
16197 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16200 if (this.toggleSelect) {
16201 var m = this.isSelected(item) ? 'unselect' : 'select';
16204 _t[m](item, true, false);
16207 if(this.multiSelect || this.singleSelect){
16208 if(this.multiSelect && e.shiftKey && this.lastSelection){
16209 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16211 this.select(item, this.multiSelect && e.ctrlKey);
16212 this.lastSelection = item;
16215 if(!this.tickable){
16216 e.preventDefault();
16224 * Get the number of selected nodes.
16227 getSelectionCount : function(){
16228 return this.selections.length;
16232 * Get the currently selected nodes.
16233 * @return {Array} An array of HTMLElements
16235 getSelectedNodes : function(){
16236 return this.selections;
16240 * Get the indexes of the selected nodes.
16243 getSelectedIndexes : function(){
16244 var indexes = [], s = this.selections;
16245 for(var i = 0, len = s.length; i < len; i++){
16246 indexes.push(s[i].nodeIndex);
16252 * Clear all selections
16253 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16255 clearSelections : function(suppressEvent){
16256 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16257 this.cmp.elements = this.selections;
16258 this.cmp.removeClass(this.selectedClass);
16259 this.selections = [];
16260 if(!suppressEvent){
16261 this.fireEvent("selectionchange", this, this.selections);
16267 * Returns true if the passed node is selected
16268 * @param {HTMLElement/Number} node The node or node index
16269 * @return {Boolean}
16271 isSelected : function(node){
16272 var s = this.selections;
16276 node = this.getNode(node);
16277 return s.indexOf(node) !== -1;
16282 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16283 * @param {Boolean} keepExisting (optional) true to keep existing selections
16284 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16286 select : function(nodeInfo, keepExisting, suppressEvent){
16287 if(nodeInfo instanceof Array){
16289 this.clearSelections(true);
16291 for(var i = 0, len = nodeInfo.length; i < len; i++){
16292 this.select(nodeInfo[i], true, true);
16296 var node = this.getNode(nodeInfo);
16297 if(!node || this.isSelected(node)){
16298 return; // already selected.
16301 this.clearSelections(true);
16304 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16305 Roo.fly(node).addClass(this.selectedClass);
16306 this.selections.push(node);
16307 if(!suppressEvent){
16308 this.fireEvent("selectionchange", this, this.selections);
16316 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16317 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16318 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16320 unselect : function(nodeInfo, keepExisting, suppressEvent)
16322 if(nodeInfo instanceof Array){
16323 Roo.each(this.selections, function(s) {
16324 this.unselect(s, nodeInfo);
16328 var node = this.getNode(nodeInfo);
16329 if(!node || !this.isSelected(node)){
16330 //Roo.log("not selected");
16331 return; // not selected.
16335 Roo.each(this.selections, function(s) {
16337 Roo.fly(node).removeClass(this.selectedClass);
16344 this.selections= ns;
16345 this.fireEvent("selectionchange", this, this.selections);
16349 * Gets a template node.
16350 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16351 * @return {HTMLElement} The node or null if it wasn't found
16353 getNode : function(nodeInfo){
16354 if(typeof nodeInfo == "string"){
16355 return document.getElementById(nodeInfo);
16356 }else if(typeof nodeInfo == "number"){
16357 return this.nodes[nodeInfo];
16363 * Gets a range template nodes.
16364 * @param {Number} startIndex
16365 * @param {Number} endIndex
16366 * @return {Array} An array of nodes
16368 getNodes : function(start, end){
16369 var ns = this.nodes;
16370 start = start || 0;
16371 end = typeof end == "undefined" ? ns.length - 1 : end;
16374 for(var i = start; i <= end; i++){
16378 for(var i = start; i >= end; i--){
16386 * Finds the index of the passed node
16387 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16388 * @return {Number} The index of the node or -1
16390 indexOf : function(node){
16391 node = this.getNode(node);
16392 if(typeof node.nodeIndex == "number"){
16393 return node.nodeIndex;
16395 var ns = this.nodes;
16396 for(var i = 0, len = ns.length; i < len; i++){
16407 * based on jquery fullcalendar
16411 Roo.bootstrap = Roo.bootstrap || {};
16413 * @class Roo.bootstrap.Calendar
16414 * @extends Roo.bootstrap.Component
16415 * Bootstrap Calendar class
16416 * @cfg {Boolean} loadMask (true|false) default false
16417 * @cfg {Object} header generate the user specific header of the calendar, default false
16420 * Create a new Container
16421 * @param {Object} config The config object
16426 Roo.bootstrap.Calendar = function(config){
16427 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16431 * Fires when a date is selected
16432 * @param {DatePicker} this
16433 * @param {Date} date The selected date
16437 * @event monthchange
16438 * Fires when the displayed month changes
16439 * @param {DatePicker} this
16440 * @param {Date} date The selected month
16442 'monthchange': true,
16444 * @event evententer
16445 * Fires when mouse over an event
16446 * @param {Calendar} this
16447 * @param {event} Event
16449 'evententer': true,
16451 * @event eventleave
16452 * Fires when the mouse leaves an
16453 * @param {Calendar} this
16456 'eventleave': true,
16458 * @event eventclick
16459 * Fires when the mouse click an
16460 * @param {Calendar} this
16469 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16472 * @cfg {Number} startDay
16473 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16481 getAutoCreate : function(){
16484 var fc_button = function(name, corner, style, content ) {
16485 return Roo.apply({},{
16487 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16489 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16492 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16503 style : 'width:100%',
16510 cls : 'fc-header-left',
16512 fc_button('prev', 'left', 'arrow', '‹' ),
16513 fc_button('next', 'right', 'arrow', '›' ),
16514 { tag: 'span', cls: 'fc-header-space' },
16515 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16523 cls : 'fc-header-center',
16527 cls: 'fc-header-title',
16530 html : 'month / year'
16538 cls : 'fc-header-right',
16540 /* fc_button('month', 'left', '', 'month' ),
16541 fc_button('week', '', '', 'week' ),
16542 fc_button('day', 'right', '', 'day' )
16554 header = this.header;
16557 var cal_heads = function() {
16559 // fixme - handle this.
16561 for (var i =0; i < Date.dayNames.length; i++) {
16562 var d = Date.dayNames[i];
16565 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16566 html : d.substring(0,3)
16570 ret[0].cls += ' fc-first';
16571 ret[6].cls += ' fc-last';
16574 var cal_cell = function(n) {
16577 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16582 cls: 'fc-day-number',
16586 cls: 'fc-day-content',
16590 style: 'position: relative;' // height: 17px;
16602 var cal_rows = function() {
16605 for (var r = 0; r < 6; r++) {
16612 for (var i =0; i < Date.dayNames.length; i++) {
16613 var d = Date.dayNames[i];
16614 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16617 row.cn[0].cls+=' fc-first';
16618 row.cn[0].cn[0].style = 'min-height:90px';
16619 row.cn[6].cls+=' fc-last';
16623 ret[0].cls += ' fc-first';
16624 ret[4].cls += ' fc-prev-last';
16625 ret[5].cls += ' fc-last';
16632 cls: 'fc-border-separate',
16633 style : 'width:100%',
16641 cls : 'fc-first fc-last',
16659 cls : 'fc-content',
16660 style : "position: relative;",
16663 cls : 'fc-view fc-view-month fc-grid',
16664 style : 'position: relative',
16665 unselectable : 'on',
16668 cls : 'fc-event-container',
16669 style : 'position:absolute;z-index:8;top:0;left:0;'
16687 initEvents : function()
16690 throw "can not find store for calendar";
16696 style: "text-align:center",
16700 style: "background-color:white;width:50%;margin:250 auto",
16704 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16715 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16717 var size = this.el.select('.fc-content', true).first().getSize();
16718 this.maskEl.setSize(size.width, size.height);
16719 this.maskEl.enableDisplayMode("block");
16720 if(!this.loadMask){
16721 this.maskEl.hide();
16724 this.store = Roo.factory(this.store, Roo.data);
16725 this.store.on('load', this.onLoad, this);
16726 this.store.on('beforeload', this.onBeforeLoad, this);
16730 this.cells = this.el.select('.fc-day',true);
16731 //Roo.log(this.cells);
16732 this.textNodes = this.el.query('.fc-day-number');
16733 this.cells.addClassOnOver('fc-state-hover');
16735 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16736 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16737 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16738 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16740 this.on('monthchange', this.onMonthChange, this);
16742 this.update(new Date().clearTime());
16745 resize : function() {
16746 var sz = this.el.getSize();
16748 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16749 this.el.select('.fc-day-content div',true).setHeight(34);
16754 showPrevMonth : function(e){
16755 this.update(this.activeDate.add("mo", -1));
16757 showToday : function(e){
16758 this.update(new Date().clearTime());
16761 showNextMonth : function(e){
16762 this.update(this.activeDate.add("mo", 1));
16766 showPrevYear : function(){
16767 this.update(this.activeDate.add("y", -1));
16771 showNextYear : function(){
16772 this.update(this.activeDate.add("y", 1));
16777 update : function(date)
16779 var vd = this.activeDate;
16780 this.activeDate = date;
16781 // if(vd && this.el){
16782 // var t = date.getTime();
16783 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16784 // Roo.log('using add remove');
16786 // this.fireEvent('monthchange', this, date);
16788 // this.cells.removeClass("fc-state-highlight");
16789 // this.cells.each(function(c){
16790 // if(c.dateValue == t){
16791 // c.addClass("fc-state-highlight");
16792 // setTimeout(function(){
16793 // try{c.dom.firstChild.focus();}catch(e){}
16803 var days = date.getDaysInMonth();
16805 var firstOfMonth = date.getFirstDateOfMonth();
16806 var startingPos = firstOfMonth.getDay()-this.startDay;
16808 if(startingPos < this.startDay){
16812 var pm = date.add(Date.MONTH, -1);
16813 var prevStart = pm.getDaysInMonth()-startingPos;
16815 this.cells = this.el.select('.fc-day',true);
16816 this.textNodes = this.el.query('.fc-day-number');
16817 this.cells.addClassOnOver('fc-state-hover');
16819 var cells = this.cells.elements;
16820 var textEls = this.textNodes;
16822 Roo.each(cells, function(cell){
16823 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16826 days += startingPos;
16828 // convert everything to numbers so it's fast
16829 var day = 86400000;
16830 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16833 //Roo.log(prevStart);
16835 var today = new Date().clearTime().getTime();
16836 var sel = date.clearTime().getTime();
16837 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16838 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16839 var ddMatch = this.disabledDatesRE;
16840 var ddText = this.disabledDatesText;
16841 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16842 var ddaysText = this.disabledDaysText;
16843 var format = this.format;
16845 var setCellClass = function(cal, cell){
16849 //Roo.log('set Cell Class');
16851 var t = d.getTime();
16855 cell.dateValue = t;
16857 cell.className += " fc-today";
16858 cell.className += " fc-state-highlight";
16859 cell.title = cal.todayText;
16862 // disable highlight in other month..
16863 //cell.className += " fc-state-highlight";
16868 cell.className = " fc-state-disabled";
16869 cell.title = cal.minText;
16873 cell.className = " fc-state-disabled";
16874 cell.title = cal.maxText;
16878 if(ddays.indexOf(d.getDay()) != -1){
16879 cell.title = ddaysText;
16880 cell.className = " fc-state-disabled";
16883 if(ddMatch && format){
16884 var fvalue = d.dateFormat(format);
16885 if(ddMatch.test(fvalue)){
16886 cell.title = ddText.replace("%0", fvalue);
16887 cell.className = " fc-state-disabled";
16891 if (!cell.initialClassName) {
16892 cell.initialClassName = cell.dom.className;
16895 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16900 for(; i < startingPos; i++) {
16901 textEls[i].innerHTML = (++prevStart);
16902 d.setDate(d.getDate()+1);
16904 cells[i].className = "fc-past fc-other-month";
16905 setCellClass(this, cells[i]);
16910 for(; i < days; i++){
16911 intDay = i - startingPos + 1;
16912 textEls[i].innerHTML = (intDay);
16913 d.setDate(d.getDate()+1);
16915 cells[i].className = ''; // "x-date-active";
16916 setCellClass(this, cells[i]);
16920 for(; i < 42; i++) {
16921 textEls[i].innerHTML = (++extraDays);
16922 d.setDate(d.getDate()+1);
16924 cells[i].className = "fc-future fc-other-month";
16925 setCellClass(this, cells[i]);
16928 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16930 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16932 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16933 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16935 if(totalRows != 6){
16936 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16937 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16940 this.fireEvent('monthchange', this, date);
16944 if(!this.internalRender){
16945 var main = this.el.dom.firstChild;
16946 var w = main.offsetWidth;
16947 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16948 Roo.fly(main).setWidth(w);
16949 this.internalRender = true;
16950 // opera does not respect the auto grow header center column
16951 // then, after it gets a width opera refuses to recalculate
16952 // without a second pass
16953 if(Roo.isOpera && !this.secondPass){
16954 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16955 this.secondPass = true;
16956 this.update.defer(10, this, [date]);
16963 findCell : function(dt) {
16964 dt = dt.clearTime().getTime();
16966 this.cells.each(function(c){
16967 //Roo.log("check " +c.dateValue + '?=' + dt);
16968 if(c.dateValue == dt){
16978 findCells : function(ev) {
16979 var s = ev.start.clone().clearTime().getTime();
16981 var e= ev.end.clone().clearTime().getTime();
16984 this.cells.each(function(c){
16985 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16987 if(c.dateValue > e){
16990 if(c.dateValue < s){
16999 // findBestRow: function(cells)
17003 // for (var i =0 ; i < cells.length;i++) {
17004 // ret = Math.max(cells[i].rows || 0,ret);
17011 addItem : function(ev)
17013 // look for vertical location slot in
17014 var cells = this.findCells(ev);
17016 // ev.row = this.findBestRow(cells);
17018 // work out the location.
17022 for(var i =0; i < cells.length; i++) {
17024 cells[i].row = cells[0].row;
17027 cells[i].row = cells[i].row + 1;
17037 if (crow.start.getY() == cells[i].getY()) {
17039 crow.end = cells[i];
17056 cells[0].events.push(ev);
17058 this.calevents.push(ev);
17061 clearEvents: function() {
17063 if(!this.calevents){
17067 Roo.each(this.cells.elements, function(c){
17073 Roo.each(this.calevents, function(e) {
17074 Roo.each(e.els, function(el) {
17075 el.un('mouseenter' ,this.onEventEnter, this);
17076 el.un('mouseleave' ,this.onEventLeave, this);
17081 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17087 renderEvents: function()
17091 this.cells.each(function(c) {
17100 if(c.row != c.events.length){
17101 r = 4 - (4 - (c.row - c.events.length));
17104 c.events = ev.slice(0, r);
17105 c.more = ev.slice(r);
17107 if(c.more.length && c.more.length == 1){
17108 c.events.push(c.more.pop());
17111 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17115 this.cells.each(function(c) {
17117 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17120 for (var e = 0; e < c.events.length; e++){
17121 var ev = c.events[e];
17122 var rows = ev.rows;
17124 for(var i = 0; i < rows.length; i++) {
17126 // how many rows should it span..
17129 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17130 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17132 unselectable : "on",
17135 cls: 'fc-event-inner',
17139 // cls: 'fc-event-time',
17140 // html : cells.length > 1 ? '' : ev.time
17144 cls: 'fc-event-title',
17145 html : String.format('{0}', ev.title)
17152 cls: 'ui-resizable-handle ui-resizable-e',
17153 html : '  '
17160 cfg.cls += ' fc-event-start';
17162 if ((i+1) == rows.length) {
17163 cfg.cls += ' fc-event-end';
17166 var ctr = _this.el.select('.fc-event-container',true).first();
17167 var cg = ctr.createChild(cfg);
17169 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17170 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17172 var r = (c.more.length) ? 1 : 0;
17173 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17174 cg.setWidth(ebox.right - sbox.x -2);
17176 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17177 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17178 cg.on('click', _this.onEventClick, _this, ev);
17189 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17190 style : 'position: absolute',
17191 unselectable : "on",
17194 cls: 'fc-event-inner',
17198 cls: 'fc-event-title',
17206 cls: 'ui-resizable-handle ui-resizable-e',
17207 html : '  '
17213 var ctr = _this.el.select('.fc-event-container',true).first();
17214 var cg = ctr.createChild(cfg);
17216 var sbox = c.select('.fc-day-content',true).first().getBox();
17217 var ebox = c.select('.fc-day-content',true).first().getBox();
17219 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17220 cg.setWidth(ebox.right - sbox.x -2);
17222 cg.on('click', _this.onMoreEventClick, _this, c.more);
17232 onEventEnter: function (e, el,event,d) {
17233 this.fireEvent('evententer', this, el, event);
17236 onEventLeave: function (e, el,event,d) {
17237 this.fireEvent('eventleave', this, el, event);
17240 onEventClick: function (e, el,event,d) {
17241 this.fireEvent('eventclick', this, el, event);
17244 onMonthChange: function () {
17248 onMoreEventClick: function(e, el, more)
17252 this.calpopover.placement = 'right';
17253 this.calpopover.setTitle('More');
17255 this.calpopover.setContent('');
17257 var ctr = this.calpopover.el.select('.popover-content', true).first();
17259 Roo.each(more, function(m){
17261 cls : 'fc-event-hori fc-event-draggable',
17264 var cg = ctr.createChild(cfg);
17266 cg.on('click', _this.onEventClick, _this, m);
17269 this.calpopover.show(el);
17274 onLoad: function ()
17276 this.calevents = [];
17279 if(this.store.getCount() > 0){
17280 this.store.data.each(function(d){
17283 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17284 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17285 time : d.data.start_time,
17286 title : d.data.title,
17287 description : d.data.description,
17288 venue : d.data.venue
17293 this.renderEvents();
17295 if(this.calevents.length && this.loadMask){
17296 this.maskEl.hide();
17300 onBeforeLoad: function()
17302 this.clearEvents();
17304 this.maskEl.show();
17318 * @class Roo.bootstrap.Popover
17319 * @extends Roo.bootstrap.Component
17320 * Bootstrap Popover class
17321 * @cfg {String} html contents of the popover (or false to use children..)
17322 * @cfg {String} title of popover (or false to hide)
17323 * @cfg {String} placement how it is placed
17324 * @cfg {String} trigger click || hover (or false to trigger manually)
17325 * @cfg {String} over what (parent or false to trigger manually.)
17326 * @cfg {Number} delay - delay before showing
17329 * Create a new Popover
17330 * @param {Object} config The config object
17333 Roo.bootstrap.Popover = function(config){
17334 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17340 * After the popover show
17342 * @param {Roo.bootstrap.Popover} this
17347 * After the popover hide
17349 * @param {Roo.bootstrap.Popover} this
17355 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17357 title: 'Fill in a title',
17360 placement : 'right',
17361 trigger : 'hover', // hover
17367 can_build_overlaid : false,
17369 getChildContainer : function()
17371 return this.el.select('.popover-content',true).first();
17374 getAutoCreate : function(){
17377 cls : 'popover roo-dynamic',
17378 style: 'display:block',
17384 cls : 'popover-inner',
17388 cls: 'popover-title',
17392 cls : 'popover-content',
17403 setTitle: function(str)
17406 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17408 setContent: function(str)
17411 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17413 // as it get's added to the bottom of the page.
17414 onRender : function(ct, position)
17416 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17418 var cfg = Roo.apply({}, this.getAutoCreate());
17422 cfg.cls += ' ' + this.cls;
17425 cfg.style = this.style;
17427 //Roo.log("adding to ");
17428 this.el = Roo.get(document.body).createChild(cfg, position);
17429 // Roo.log(this.el);
17434 initEvents : function()
17436 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17437 this.el.enableDisplayMode('block');
17439 if (this.over === false) {
17442 if (this.triggers === false) {
17445 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17446 var triggers = this.trigger ? this.trigger.split(' ') : [];
17447 Roo.each(triggers, function(trigger) {
17449 if (trigger == 'click') {
17450 on_el.on('click', this.toggle, this);
17451 } else if (trigger != 'manual') {
17452 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17453 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17455 on_el.on(eventIn ,this.enter, this);
17456 on_el.on(eventOut, this.leave, this);
17467 toggle : function () {
17468 this.hoverState == 'in' ? this.leave() : this.enter();
17471 enter : function () {
17473 clearTimeout(this.timeout);
17475 this.hoverState = 'in';
17477 if (!this.delay || !this.delay.show) {
17482 this.timeout = setTimeout(function () {
17483 if (_t.hoverState == 'in') {
17486 }, this.delay.show)
17489 leave : function() {
17490 clearTimeout(this.timeout);
17492 this.hoverState = 'out';
17494 if (!this.delay || !this.delay.hide) {
17499 this.timeout = setTimeout(function () {
17500 if (_t.hoverState == 'out') {
17503 }, this.delay.hide)
17506 show : function (on_el)
17509 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17513 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17514 if (this.html !== false) {
17515 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17517 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17518 if (!this.title.length) {
17519 this.el.select('.popover-title',true).hide();
17522 var placement = typeof this.placement == 'function' ?
17523 this.placement.call(this, this.el, on_el) :
17526 var autoToken = /\s?auto?\s?/i;
17527 var autoPlace = autoToken.test(placement);
17529 placement = placement.replace(autoToken, '') || 'top';
17533 //this.el.setXY([0,0]);
17535 this.el.dom.style.display='block';
17536 this.el.addClass(placement);
17538 //this.el.appendTo(on_el);
17540 var p = this.getPosition();
17541 var box = this.el.getBox();
17546 var align = Roo.bootstrap.Popover.alignment[placement];
17549 this.el.alignTo(on_el, align[0],align[1]);
17550 //var arrow = this.el.select('.arrow',true).first();
17551 //arrow.set(align[2],
17553 this.el.addClass('in');
17556 if (this.el.hasClass('fade')) {
17560 this.hoverState = 'in';
17562 this.fireEvent('show', this);
17567 this.el.setXY([0,0]);
17568 this.el.removeClass('in');
17570 this.hoverState = null;
17572 this.fireEvent('hide', this);
17577 Roo.bootstrap.Popover.alignment = {
17578 'left' : ['r-l', [-10,0], 'right'],
17579 'right' : ['l-r', [10,0], 'left'],
17580 'bottom' : ['t-b', [0,10], 'top'],
17581 'top' : [ 'b-t', [0,-10], 'bottom']
17592 * @class Roo.bootstrap.Progress
17593 * @extends Roo.bootstrap.Component
17594 * Bootstrap Progress class
17595 * @cfg {Boolean} striped striped of the progress bar
17596 * @cfg {Boolean} active animated of the progress bar
17600 * Create a new Progress
17601 * @param {Object} config The config object
17604 Roo.bootstrap.Progress = function(config){
17605 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17608 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17613 getAutoCreate : function(){
17621 cfg.cls += ' progress-striped';
17625 cfg.cls += ' active';
17644 * @class Roo.bootstrap.ProgressBar
17645 * @extends Roo.bootstrap.Component
17646 * Bootstrap ProgressBar class
17647 * @cfg {Number} aria_valuenow aria-value now
17648 * @cfg {Number} aria_valuemin aria-value min
17649 * @cfg {Number} aria_valuemax aria-value max
17650 * @cfg {String} label label for the progress bar
17651 * @cfg {String} panel (success | info | warning | danger )
17652 * @cfg {String} role role of the progress bar
17653 * @cfg {String} sr_only text
17657 * Create a new ProgressBar
17658 * @param {Object} config The config object
17661 Roo.bootstrap.ProgressBar = function(config){
17662 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17665 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17669 aria_valuemax : 100,
17675 getAutoCreate : function()
17680 cls: 'progress-bar',
17681 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17693 cfg.role = this.role;
17696 if(this.aria_valuenow){
17697 cfg['aria-valuenow'] = this.aria_valuenow;
17700 if(this.aria_valuemin){
17701 cfg['aria-valuemin'] = this.aria_valuemin;
17704 if(this.aria_valuemax){
17705 cfg['aria-valuemax'] = this.aria_valuemax;
17708 if(this.label && !this.sr_only){
17709 cfg.html = this.label;
17713 cfg.cls += ' progress-bar-' + this.panel;
17719 update : function(aria_valuenow)
17721 this.aria_valuenow = aria_valuenow;
17723 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17738 * @class Roo.bootstrap.TabGroup
17739 * @extends Roo.bootstrap.Column
17740 * Bootstrap Column class
17741 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17742 * @cfg {Boolean} carousel true to make the group behave like a carousel
17743 * @cfg {Boolean} bullets show bullets for the panels
17744 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17745 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17746 * @cfg {Boolean} showarrow (true|false) show arrow default true
17749 * Create a new TabGroup
17750 * @param {Object} config The config object
17753 Roo.bootstrap.TabGroup = function(config){
17754 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17756 this.navId = Roo.id();
17759 Roo.bootstrap.TabGroup.register(this);
17763 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17766 transition : false,
17771 slideOnTouch : false,
17774 getAutoCreate : function()
17776 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17778 cfg.cls += ' tab-content';
17780 if (this.carousel) {
17781 cfg.cls += ' carousel slide';
17784 cls : 'carousel-inner',
17788 if(this.bullets && !Roo.isTouch){
17791 cls : 'carousel-bullets',
17795 if(this.bullets_cls){
17796 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17803 cfg.cn[0].cn.push(bullets);
17806 if(this.showarrow){
17807 cfg.cn[0].cn.push({
17809 class : 'carousel-arrow',
17813 class : 'carousel-prev',
17817 class : 'fa fa-chevron-left'
17823 class : 'carousel-next',
17827 class : 'fa fa-chevron-right'
17840 initEvents: function()
17842 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17843 // this.el.on("touchstart", this.onTouchStart, this);
17846 if(this.autoslide){
17849 this.slideFn = window.setInterval(function() {
17850 _this.showPanelNext();
17854 if(this.showarrow){
17855 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17856 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17862 // onTouchStart : function(e, el, o)
17864 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17868 // this.showPanelNext();
17872 getChildContainer : function()
17874 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17878 * register a Navigation item
17879 * @param {Roo.bootstrap.NavItem} the navitem to add
17881 register : function(item)
17883 this.tabs.push( item);
17884 item.navId = this.navId; // not really needed..
17889 getActivePanel : function()
17892 Roo.each(this.tabs, function(t) {
17902 getPanelByName : function(n)
17905 Roo.each(this.tabs, function(t) {
17906 if (t.tabId == n) {
17914 indexOfPanel : function(p)
17917 Roo.each(this.tabs, function(t,i) {
17918 if (t.tabId == p.tabId) {
17927 * show a specific panel
17928 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17929 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17931 showPanel : function (pan)
17933 if(this.transition || typeof(pan) == 'undefined'){
17934 Roo.log("waiting for the transitionend");
17938 if (typeof(pan) == 'number') {
17939 pan = this.tabs[pan];
17942 if (typeof(pan) == 'string') {
17943 pan = this.getPanelByName(pan);
17946 var cur = this.getActivePanel();
17949 Roo.log('pan or acitve pan is undefined');
17953 if (pan.tabId == this.getActivePanel().tabId) {
17957 if (false === cur.fireEvent('beforedeactivate')) {
17961 if(this.bullets > 0 && !Roo.isTouch){
17962 this.setActiveBullet(this.indexOfPanel(pan));
17965 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17967 this.transition = true;
17968 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17969 var lr = dir == 'next' ? 'left' : 'right';
17970 pan.el.addClass(dir); // or prev
17971 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17972 cur.el.addClass(lr); // or right
17973 pan.el.addClass(lr);
17976 cur.el.on('transitionend', function() {
17977 Roo.log("trans end?");
17979 pan.el.removeClass([lr,dir]);
17980 pan.setActive(true);
17982 cur.el.removeClass([lr]);
17983 cur.setActive(false);
17985 _this.transition = false;
17987 }, this, { single: true } );
17992 cur.setActive(false);
17993 pan.setActive(true);
17998 showPanelNext : function()
18000 var i = this.indexOfPanel(this.getActivePanel());
18002 if (i >= this.tabs.length - 1 && !this.autoslide) {
18006 if (i >= this.tabs.length - 1 && this.autoslide) {
18010 this.showPanel(this.tabs[i+1]);
18013 showPanelPrev : function()
18015 var i = this.indexOfPanel(this.getActivePanel());
18017 if (i < 1 && !this.autoslide) {
18021 if (i < 1 && this.autoslide) {
18022 i = this.tabs.length;
18025 this.showPanel(this.tabs[i-1]);
18029 addBullet: function()
18031 if(!this.bullets || Roo.isTouch){
18034 var ctr = this.el.select('.carousel-bullets',true).first();
18035 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18036 var bullet = ctr.createChild({
18037 cls : 'bullet bullet-' + i
18038 },ctr.dom.lastChild);
18043 bullet.on('click', (function(e, el, o, ii, t){
18045 e.preventDefault();
18047 this.showPanel(ii);
18049 if(this.autoslide && this.slideFn){
18050 clearInterval(this.slideFn);
18051 this.slideFn = window.setInterval(function() {
18052 _this.showPanelNext();
18056 }).createDelegate(this, [i, bullet], true));
18061 setActiveBullet : function(i)
18067 Roo.each(this.el.select('.bullet', true).elements, function(el){
18068 el.removeClass('selected');
18071 var bullet = this.el.select('.bullet-' + i, true).first();
18077 bullet.addClass('selected');
18088 Roo.apply(Roo.bootstrap.TabGroup, {
18092 * register a Navigation Group
18093 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18095 register : function(navgrp)
18097 this.groups[navgrp.navId] = navgrp;
18101 * fetch a Navigation Group based on the navigation ID
18102 * if one does not exist , it will get created.
18103 * @param {string} the navgroup to add
18104 * @returns {Roo.bootstrap.NavGroup} the navgroup
18106 get: function(navId) {
18107 if (typeof(this.groups[navId]) == 'undefined') {
18108 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18110 return this.groups[navId] ;
18125 * @class Roo.bootstrap.TabPanel
18126 * @extends Roo.bootstrap.Component
18127 * Bootstrap TabPanel class
18128 * @cfg {Boolean} active panel active
18129 * @cfg {String} html panel content
18130 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18131 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18132 * @cfg {String} href click to link..
18136 * Create a new TabPanel
18137 * @param {Object} config The config object
18140 Roo.bootstrap.TabPanel = function(config){
18141 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18145 * Fires when the active status changes
18146 * @param {Roo.bootstrap.TabPanel} this
18147 * @param {Boolean} state the new state
18152 * @event beforedeactivate
18153 * Fires before a tab is de-activated - can be used to do validation on a form.
18154 * @param {Roo.bootstrap.TabPanel} this
18155 * @return {Boolean} false if there is an error
18158 'beforedeactivate': true
18161 this.tabId = this.tabId || Roo.id();
18165 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18173 getAutoCreate : function(){
18176 // item is needed for carousel - not sure if it has any effect otherwise
18177 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18178 html: this.html || ''
18182 cfg.cls += ' active';
18186 cfg.tabId = this.tabId;
18193 initEvents: function()
18195 var p = this.parent();
18197 this.navId = this.navId || p.navId;
18199 if (typeof(this.navId) != 'undefined') {
18200 // not really needed.. but just in case.. parent should be a NavGroup.
18201 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18205 var i = tg.tabs.length - 1;
18207 if(this.active && tg.bullets > 0 && i < tg.bullets){
18208 tg.setActiveBullet(i);
18212 this.el.on('click', this.onClick, this);
18215 this.el.on("touchstart", this.onTouchStart, this);
18216 this.el.on("touchmove", this.onTouchMove, this);
18217 this.el.on("touchend", this.onTouchEnd, this);
18222 onRender : function(ct, position)
18224 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18227 setActive : function(state)
18229 Roo.log("panel - set active " + this.tabId + "=" + state);
18231 this.active = state;
18233 this.el.removeClass('active');
18235 } else if (!this.el.hasClass('active')) {
18236 this.el.addClass('active');
18239 this.fireEvent('changed', this, state);
18242 onClick : function(e)
18244 e.preventDefault();
18246 if(!this.href.length){
18250 window.location.href = this.href;
18259 onTouchStart : function(e)
18261 this.swiping = false;
18263 this.startX = e.browserEvent.touches[0].clientX;
18264 this.startY = e.browserEvent.touches[0].clientY;
18267 onTouchMove : function(e)
18269 this.swiping = true;
18271 this.endX = e.browserEvent.touches[0].clientX;
18272 this.endY = e.browserEvent.touches[0].clientY;
18275 onTouchEnd : function(e)
18282 var tabGroup = this.parent();
18284 if(this.endX > this.startX){ // swiping right
18285 tabGroup.showPanelPrev();
18289 if(this.startX > this.endX){ // swiping left
18290 tabGroup.showPanelNext();
18309 * @class Roo.bootstrap.DateField
18310 * @extends Roo.bootstrap.Input
18311 * Bootstrap DateField class
18312 * @cfg {Number} weekStart default 0
18313 * @cfg {String} viewMode default empty, (months|years)
18314 * @cfg {String} minViewMode default empty, (months|years)
18315 * @cfg {Number} startDate default -Infinity
18316 * @cfg {Number} endDate default Infinity
18317 * @cfg {Boolean} todayHighlight default false
18318 * @cfg {Boolean} todayBtn default false
18319 * @cfg {Boolean} calendarWeeks default false
18320 * @cfg {Object} daysOfWeekDisabled default empty
18321 * @cfg {Boolean} singleMode default false (true | false)
18323 * @cfg {Boolean} keyboardNavigation default true
18324 * @cfg {String} language default en
18327 * Create a new DateField
18328 * @param {Object} config The config object
18331 Roo.bootstrap.DateField = function(config){
18332 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18336 * Fires when this field show.
18337 * @param {Roo.bootstrap.DateField} this
18338 * @param {Mixed} date The date value
18343 * Fires when this field hide.
18344 * @param {Roo.bootstrap.DateField} this
18345 * @param {Mixed} date The date value
18350 * Fires when select a date.
18351 * @param {Roo.bootstrap.DateField} this
18352 * @param {Mixed} date The date value
18356 * @event beforeselect
18357 * Fires when before select a date.
18358 * @param {Roo.bootstrap.DateField} this
18359 * @param {Mixed} date The date value
18361 beforeselect : true
18365 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18368 * @cfg {String} format
18369 * The default date format string which can be overriden for localization support. The format must be
18370 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18374 * @cfg {String} altFormats
18375 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18376 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18378 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18386 todayHighlight : false,
18392 keyboardNavigation: true,
18394 calendarWeeks: false,
18396 startDate: -Infinity,
18400 daysOfWeekDisabled: [],
18404 singleMode : false,
18406 UTCDate: function()
18408 return new Date(Date.UTC.apply(Date, arguments));
18411 UTCToday: function()
18413 var today = new Date();
18414 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18417 getDate: function() {
18418 var d = this.getUTCDate();
18419 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18422 getUTCDate: function() {
18426 setDate: function(d) {
18427 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18430 setUTCDate: function(d) {
18432 this.setValue(this.formatDate(this.date));
18435 onRender: function(ct, position)
18438 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18440 this.language = this.language || 'en';
18441 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18442 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18444 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18445 this.format = this.format || 'm/d/y';
18446 this.isInline = false;
18447 this.isInput = true;
18448 this.component = this.el.select('.add-on', true).first() || false;
18449 this.component = (this.component && this.component.length === 0) ? false : this.component;
18450 this.hasInput = this.component && this.inputEl().length;
18452 if (typeof(this.minViewMode === 'string')) {
18453 switch (this.minViewMode) {
18455 this.minViewMode = 1;
18458 this.minViewMode = 2;
18461 this.minViewMode = 0;
18466 if (typeof(this.viewMode === 'string')) {
18467 switch (this.viewMode) {
18480 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18482 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18484 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18486 this.picker().on('mousedown', this.onMousedown, this);
18487 this.picker().on('click', this.onClick, this);
18489 this.picker().addClass('datepicker-dropdown');
18491 this.startViewMode = this.viewMode;
18493 if(this.singleMode){
18494 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18495 v.setVisibilityMode(Roo.Element.DISPLAY);
18499 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18500 v.setStyle('width', '189px');
18504 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18505 if(!this.calendarWeeks){
18510 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18511 v.attr('colspan', function(i, val){
18512 return parseInt(val) + 1;
18517 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18519 this.setStartDate(this.startDate);
18520 this.setEndDate(this.endDate);
18522 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18529 if(this.isInline) {
18534 picker : function()
18536 return this.pickerEl;
18537 // return this.el.select('.datepicker', true).first();
18540 fillDow: function()
18542 var dowCnt = this.weekStart;
18551 if(this.calendarWeeks){
18559 while (dowCnt < this.weekStart + 7) {
18563 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18567 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18570 fillMonths: function()
18573 var months = this.picker().select('>.datepicker-months td', true).first();
18575 months.dom.innerHTML = '';
18581 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18584 months.createChild(month);
18591 this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
18593 if (this.date < this.startDate) {
18594 this.viewDate = new Date(this.startDate);
18595 } else if (this.date > this.endDate) {
18596 this.viewDate = new Date(this.endDate);
18598 this.viewDate = new Date(this.date);
18606 var d = new Date(this.viewDate),
18607 year = d.getUTCFullYear(),
18608 month = d.getUTCMonth(),
18609 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18610 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18611 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18612 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18613 currentDate = this.date && this.date.valueOf(),
18614 today = this.UTCToday();
18616 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18618 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18620 // this.picker.select('>tfoot th.today').
18621 // .text(dates[this.language].today)
18622 // .toggle(this.todayBtn !== false);
18624 this.updateNavArrows();
18627 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18629 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18631 prevMonth.setUTCDate(day);
18633 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18635 var nextMonth = new Date(prevMonth);
18637 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18639 nextMonth = nextMonth.valueOf();
18641 var fillMonths = false;
18643 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18645 while(prevMonth.valueOf() <= nextMonth) {
18648 if (prevMonth.getUTCDay() === this.weekStart) {
18650 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18658 if(this.calendarWeeks){
18659 // ISO 8601: First week contains first thursday.
18660 // ISO also states week starts on Monday, but we can be more abstract here.
18662 // Start of current week: based on weekstart/current date
18663 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18664 // Thursday of this week
18665 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18666 // First Thursday of year, year from thursday
18667 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18668 // Calendar week: ms between thursdays, div ms per day, div 7 days
18669 calWeek = (th - yth) / 864e5 / 7 + 1;
18671 fillMonths.cn.push({
18679 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18681 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18684 if (this.todayHighlight &&
18685 prevMonth.getUTCFullYear() == today.getFullYear() &&
18686 prevMonth.getUTCMonth() == today.getMonth() &&
18687 prevMonth.getUTCDate() == today.getDate()) {
18688 clsName += ' today';
18691 if (currentDate && prevMonth.valueOf() === currentDate) {
18692 clsName += ' active';
18695 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18696 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18697 clsName += ' disabled';
18700 fillMonths.cn.push({
18702 cls: 'day ' + clsName,
18703 html: prevMonth.getDate()
18706 prevMonth.setDate(prevMonth.getDate()+1);
18709 var currentYear = this.date && this.date.getUTCFullYear();
18710 var currentMonth = this.date && this.date.getUTCMonth();
18712 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18714 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18715 v.removeClass('active');
18717 if(currentYear === year && k === currentMonth){
18718 v.addClass('active');
18721 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18722 v.addClass('disabled');
18728 year = parseInt(year/10, 10) * 10;
18730 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18732 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18735 for (var i = -1; i < 11; i++) {
18736 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18738 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18746 showMode: function(dir)
18749 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18752 Roo.each(this.picker().select('>div',true).elements, function(v){
18753 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18756 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18761 if(this.isInline) {
18765 this.picker().removeClass(['bottom', 'top']);
18767 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18769 * place to the top of element!
18773 this.picker().addClass('top');
18774 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18779 this.picker().addClass('bottom');
18781 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18784 parseDate : function(value)
18786 if(!value || value instanceof Date){
18789 var v = Date.parseDate(value, this.format);
18790 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18791 v = Date.parseDate(value, 'Y-m-d');
18793 if(!v && this.altFormats){
18794 if(!this.altFormatsArray){
18795 this.altFormatsArray = this.altFormats.split("|");
18797 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18798 v = Date.parseDate(value, this.altFormatsArray[i]);
18804 formatDate : function(date, fmt)
18806 return (!date || !(date instanceof Date)) ?
18807 date : date.dateFormat(fmt || this.format);
18810 onFocus : function()
18812 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18816 onBlur : function()
18818 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18820 var d = this.inputEl().getValue();
18829 this.picker().show();
18833 this.fireEvent('show', this, this.date);
18838 if(this.isInline) {
18841 this.picker().hide();
18842 this.viewMode = this.startViewMode;
18845 this.fireEvent('hide', this, this.date);
18849 onMousedown: function(e)
18851 e.stopPropagation();
18852 e.preventDefault();
18857 Roo.bootstrap.DateField.superclass.keyup.call(this);
18861 setValue: function(v)
18863 if(this.fireEvent('beforeselect', this, v) !== false){
18864 var d = new Date(this.parseDate(v) ).clearTime();
18866 if(isNaN(d.getTime())){
18867 this.date = this.viewDate = '';
18868 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18872 v = this.formatDate(d);
18874 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18876 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18880 this.fireEvent('select', this, this.date);
18884 getValue: function()
18886 return this.formatDate(this.date);
18889 fireKey: function(e)
18891 if (!this.picker().isVisible()){
18892 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18898 var dateChanged = false,
18900 newDate, newViewDate;
18905 e.preventDefault();
18909 if (!this.keyboardNavigation) {
18912 dir = e.keyCode == 37 ? -1 : 1;
18915 newDate = this.moveYear(this.date, dir);
18916 newViewDate = this.moveYear(this.viewDate, dir);
18917 } else if (e.shiftKey){
18918 newDate = this.moveMonth(this.date, dir);
18919 newViewDate = this.moveMonth(this.viewDate, dir);
18921 newDate = new Date(this.date);
18922 newDate.setUTCDate(this.date.getUTCDate() + dir);
18923 newViewDate = new Date(this.viewDate);
18924 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18926 if (this.dateWithinRange(newDate)){
18927 this.date = newDate;
18928 this.viewDate = newViewDate;
18929 this.setValue(this.formatDate(this.date));
18931 e.preventDefault();
18932 dateChanged = true;
18937 if (!this.keyboardNavigation) {
18940 dir = e.keyCode == 38 ? -1 : 1;
18942 newDate = this.moveYear(this.date, dir);
18943 newViewDate = this.moveYear(this.viewDate, dir);
18944 } else if (e.shiftKey){
18945 newDate = this.moveMonth(this.date, dir);
18946 newViewDate = this.moveMonth(this.viewDate, dir);
18948 newDate = new Date(this.date);
18949 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18950 newViewDate = new Date(this.viewDate);
18951 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18953 if (this.dateWithinRange(newDate)){
18954 this.date = newDate;
18955 this.viewDate = newViewDate;
18956 this.setValue(this.formatDate(this.date));
18958 e.preventDefault();
18959 dateChanged = true;
18963 this.setValue(this.formatDate(this.date));
18965 e.preventDefault();
18968 this.setValue(this.formatDate(this.date));
18982 onClick: function(e)
18984 e.stopPropagation();
18985 e.preventDefault();
18987 var target = e.getTarget();
18989 if(target.nodeName.toLowerCase() === 'i'){
18990 target = Roo.get(target).dom.parentNode;
18993 var nodeName = target.nodeName;
18994 var className = target.className;
18995 var html = target.innerHTML;
18996 //Roo.log(nodeName);
18998 switch(nodeName.toLowerCase()) {
19000 switch(className) {
19006 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19007 switch(this.viewMode){
19009 this.viewDate = this.moveMonth(this.viewDate, dir);
19013 this.viewDate = this.moveYear(this.viewDate, dir);
19019 var date = new Date();
19020 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19022 this.setValue(this.formatDate(this.date));
19029 if (className.indexOf('disabled') < 0) {
19030 this.viewDate.setUTCDate(1);
19031 if (className.indexOf('month') > -1) {
19032 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19034 var year = parseInt(html, 10) || 0;
19035 this.viewDate.setUTCFullYear(year);
19039 if(this.singleMode){
19040 this.setValue(this.formatDate(this.viewDate));
19051 //Roo.log(className);
19052 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19053 var day = parseInt(html, 10) || 1;
19054 var year = this.viewDate.getUTCFullYear(),
19055 month = this.viewDate.getUTCMonth();
19057 if (className.indexOf('old') > -1) {
19064 } else if (className.indexOf('new') > -1) {
19072 //Roo.log([year,month,day]);
19073 this.date = this.UTCDate(year, month, day,0,0,0,0);
19074 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19076 //Roo.log(this.formatDate(this.date));
19077 this.setValue(this.formatDate(this.date));
19084 setStartDate: function(startDate)
19086 this.startDate = startDate || -Infinity;
19087 if (this.startDate !== -Infinity) {
19088 this.startDate = this.parseDate(this.startDate);
19091 this.updateNavArrows();
19094 setEndDate: function(endDate)
19096 this.endDate = endDate || Infinity;
19097 if (this.endDate !== Infinity) {
19098 this.endDate = this.parseDate(this.endDate);
19101 this.updateNavArrows();
19104 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19106 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19107 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19108 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19110 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19111 return parseInt(d, 10);
19114 this.updateNavArrows();
19117 updateNavArrows: function()
19119 if(this.singleMode){
19123 var d = new Date(this.viewDate),
19124 year = d.getUTCFullYear(),
19125 month = d.getUTCMonth();
19127 Roo.each(this.picker().select('.prev', true).elements, function(v){
19129 switch (this.viewMode) {
19132 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19138 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19145 Roo.each(this.picker().select('.next', true).elements, function(v){
19147 switch (this.viewMode) {
19150 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19156 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19164 moveMonth: function(date, dir)
19169 var new_date = new Date(date.valueOf()),
19170 day = new_date.getUTCDate(),
19171 month = new_date.getUTCMonth(),
19172 mag = Math.abs(dir),
19174 dir = dir > 0 ? 1 : -1;
19177 // If going back one month, make sure month is not current month
19178 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19180 return new_date.getUTCMonth() == month;
19182 // If going forward one month, make sure month is as expected
19183 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19185 return new_date.getUTCMonth() != new_month;
19187 new_month = month + dir;
19188 new_date.setUTCMonth(new_month);
19189 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19190 if (new_month < 0 || new_month > 11) {
19191 new_month = (new_month + 12) % 12;
19194 // For magnitudes >1, move one month at a time...
19195 for (var i=0; i<mag; i++) {
19196 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19197 new_date = this.moveMonth(new_date, dir);
19199 // ...then reset the day, keeping it in the new month
19200 new_month = new_date.getUTCMonth();
19201 new_date.setUTCDate(day);
19203 return new_month != new_date.getUTCMonth();
19206 // Common date-resetting loop -- if date is beyond end of month, make it
19209 new_date.setUTCDate(--day);
19210 new_date.setUTCMonth(new_month);
19215 moveYear: function(date, dir)
19217 return this.moveMonth(date, dir*12);
19220 dateWithinRange: function(date)
19222 return date >= this.startDate && date <= this.endDate;
19228 this.picker().remove();
19231 validateValue : function(value)
19233 if(this.getVisibilityEl().hasClass('hidden')){
19237 if(value.length < 1) {
19238 if(this.allowBlank){
19244 if(value.length < this.minLength){
19247 if(value.length > this.maxLength){
19251 var vt = Roo.form.VTypes;
19252 if(!vt[this.vtype](value, this)){
19256 if(typeof this.validator == "function"){
19257 var msg = this.validator(value);
19263 if(this.regex && !this.regex.test(value)){
19267 if(typeof(this.parseDate(value)) == 'undefined'){
19271 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19275 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19285 this.date = this.viewDate = '';
19287 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19290 setVisible : function(visible)
19296 this.getEl().removeClass('hidden');
19302 this.getEl().addClass('hidden');
19307 Roo.apply(Roo.bootstrap.DateField, {
19318 html: '<i class="fa fa-arrow-left"/>'
19328 html: '<i class="fa fa-arrow-right"/>'
19370 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19371 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19372 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19373 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19374 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19387 navFnc: 'FullYear',
19392 navFnc: 'FullYear',
19397 Roo.apply(Roo.bootstrap.DateField, {
19401 cls: 'datepicker dropdown-menu roo-dynamic',
19405 cls: 'datepicker-days',
19409 cls: 'table-condensed',
19411 Roo.bootstrap.DateField.head,
19415 Roo.bootstrap.DateField.footer
19422 cls: 'datepicker-months',
19426 cls: 'table-condensed',
19428 Roo.bootstrap.DateField.head,
19429 Roo.bootstrap.DateField.content,
19430 Roo.bootstrap.DateField.footer
19437 cls: 'datepicker-years',
19441 cls: 'table-condensed',
19443 Roo.bootstrap.DateField.head,
19444 Roo.bootstrap.DateField.content,
19445 Roo.bootstrap.DateField.footer
19464 * @class Roo.bootstrap.TimeField
19465 * @extends Roo.bootstrap.Input
19466 * Bootstrap DateField class
19470 * Create a new TimeField
19471 * @param {Object} config The config object
19474 Roo.bootstrap.TimeField = function(config){
19475 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19479 * Fires when this field show.
19480 * @param {Roo.bootstrap.DateField} thisthis
19481 * @param {Mixed} date The date value
19486 * Fires when this field hide.
19487 * @param {Roo.bootstrap.DateField} this
19488 * @param {Mixed} date The date value
19493 * Fires when select a date.
19494 * @param {Roo.bootstrap.DateField} this
19495 * @param {Mixed} date The date value
19501 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19504 * @cfg {String} format
19505 * The default time format string which can be overriden for localization support. The format must be
19506 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19510 onRender: function(ct, position)
19513 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19515 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19517 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19519 this.pop = this.picker().select('>.datepicker-time',true).first();
19520 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19522 this.picker().on('mousedown', this.onMousedown, this);
19523 this.picker().on('click', this.onClick, this);
19525 this.picker().addClass('datepicker-dropdown');
19530 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19531 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19532 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19533 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19534 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19535 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19539 fireKey: function(e){
19540 if (!this.picker().isVisible()){
19541 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19547 e.preventDefault();
19555 this.onTogglePeriod();
19558 this.onIncrementMinutes();
19561 this.onDecrementMinutes();
19570 onClick: function(e) {
19571 e.stopPropagation();
19572 e.preventDefault();
19575 picker : function()
19577 return this.el.select('.datepicker', true).first();
19580 fillTime: function()
19582 var time = this.pop.select('tbody', true).first();
19584 time.dom.innerHTML = '';
19599 cls: 'hours-up glyphicon glyphicon-chevron-up'
19619 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19640 cls: 'timepicker-hour',
19655 cls: 'timepicker-minute',
19670 cls: 'btn btn-primary period',
19692 cls: 'hours-down glyphicon glyphicon-chevron-down'
19712 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19730 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19737 var hours = this.time.getHours();
19738 var minutes = this.time.getMinutes();
19751 hours = hours - 12;
19755 hours = '0' + hours;
19759 minutes = '0' + minutes;
19762 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19763 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19764 this.pop.select('button', true).first().dom.innerHTML = period;
19770 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19772 var cls = ['bottom'];
19774 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19781 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19786 this.picker().addClass(cls.join('-'));
19790 Roo.each(cls, function(c){
19792 _this.picker().setTop(_this.inputEl().getHeight());
19796 _this.picker().setTop(0 - _this.picker().getHeight());
19801 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19805 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19812 onFocus : function()
19814 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19818 onBlur : function()
19820 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19826 this.picker().show();
19831 this.fireEvent('show', this, this.date);
19836 this.picker().hide();
19839 this.fireEvent('hide', this, this.date);
19842 setTime : function()
19845 this.setValue(this.time.format(this.format));
19847 this.fireEvent('select', this, this.date);
19852 onMousedown: function(e){
19853 e.stopPropagation();
19854 e.preventDefault();
19857 onIncrementHours: function()
19859 Roo.log('onIncrementHours');
19860 this.time = this.time.add(Date.HOUR, 1);
19865 onDecrementHours: function()
19867 Roo.log('onDecrementHours');
19868 this.time = this.time.add(Date.HOUR, -1);
19872 onIncrementMinutes: function()
19874 Roo.log('onIncrementMinutes');
19875 this.time = this.time.add(Date.MINUTE, 1);
19879 onDecrementMinutes: function()
19881 Roo.log('onDecrementMinutes');
19882 this.time = this.time.add(Date.MINUTE, -1);
19886 onTogglePeriod: function()
19888 Roo.log('onTogglePeriod');
19889 this.time = this.time.add(Date.HOUR, 12);
19896 Roo.apply(Roo.bootstrap.TimeField, {
19926 cls: 'btn btn-info ok',
19938 Roo.apply(Roo.bootstrap.TimeField, {
19942 cls: 'datepicker dropdown-menu',
19946 cls: 'datepicker-time',
19950 cls: 'table-condensed',
19952 Roo.bootstrap.TimeField.content,
19953 Roo.bootstrap.TimeField.footer
19972 * @class Roo.bootstrap.MonthField
19973 * @extends Roo.bootstrap.Input
19974 * Bootstrap MonthField class
19976 * @cfg {String} language default en
19979 * Create a new MonthField
19980 * @param {Object} config The config object
19983 Roo.bootstrap.MonthField = function(config){
19984 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19989 * Fires when this field show.
19990 * @param {Roo.bootstrap.MonthField} this
19991 * @param {Mixed} date The date value
19996 * Fires when this field hide.
19997 * @param {Roo.bootstrap.MonthField} this
19998 * @param {Mixed} date The date value
20003 * Fires when select a date.
20004 * @param {Roo.bootstrap.MonthField} this
20005 * @param {String} oldvalue The old value
20006 * @param {String} newvalue The new value
20012 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20014 onRender: function(ct, position)
20017 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20019 this.language = this.language || 'en';
20020 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20021 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20023 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20024 this.isInline = false;
20025 this.isInput = true;
20026 this.component = this.el.select('.add-on', true).first() || false;
20027 this.component = (this.component && this.component.length === 0) ? false : this.component;
20028 this.hasInput = this.component && this.inputEL().length;
20030 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20032 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20034 this.picker().on('mousedown', this.onMousedown, this);
20035 this.picker().on('click', this.onClick, this);
20037 this.picker().addClass('datepicker-dropdown');
20039 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20040 v.setStyle('width', '189px');
20047 if(this.isInline) {
20053 setValue: function(v, suppressEvent)
20055 var o = this.getValue();
20057 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20061 if(suppressEvent !== true){
20062 this.fireEvent('select', this, o, v);
20067 getValue: function()
20072 onClick: function(e)
20074 e.stopPropagation();
20075 e.preventDefault();
20077 var target = e.getTarget();
20079 if(target.nodeName.toLowerCase() === 'i'){
20080 target = Roo.get(target).dom.parentNode;
20083 var nodeName = target.nodeName;
20084 var className = target.className;
20085 var html = target.innerHTML;
20087 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20091 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20093 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20099 picker : function()
20101 return this.pickerEl;
20104 fillMonths: function()
20107 var months = this.picker().select('>.datepicker-months td', true).first();
20109 months.dom.innerHTML = '';
20115 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20118 months.createChild(month);
20127 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20128 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20131 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20132 e.removeClass('active');
20134 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20135 e.addClass('active');
20142 if(this.isInline) {
20146 this.picker().removeClass(['bottom', 'top']);
20148 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20150 * place to the top of element!
20154 this.picker().addClass('top');
20155 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20160 this.picker().addClass('bottom');
20162 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20165 onFocus : function()
20167 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20171 onBlur : function()
20173 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20175 var d = this.inputEl().getValue();
20184 this.picker().show();
20185 this.picker().select('>.datepicker-months', true).first().show();
20189 this.fireEvent('show', this, this.date);
20194 if(this.isInline) {
20197 this.picker().hide();
20198 this.fireEvent('hide', this, this.date);
20202 onMousedown: function(e)
20204 e.stopPropagation();
20205 e.preventDefault();
20210 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20214 fireKey: function(e)
20216 if (!this.picker().isVisible()){
20217 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20228 e.preventDefault();
20232 dir = e.keyCode == 37 ? -1 : 1;
20234 this.vIndex = this.vIndex + dir;
20236 if(this.vIndex < 0){
20240 if(this.vIndex > 11){
20244 if(isNaN(this.vIndex)){
20248 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20254 dir = e.keyCode == 38 ? -1 : 1;
20256 this.vIndex = this.vIndex + dir * 4;
20258 if(this.vIndex < 0){
20262 if(this.vIndex > 11){
20266 if(isNaN(this.vIndex)){
20270 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20275 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20276 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20280 e.preventDefault();
20283 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20284 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20300 this.picker().remove();
20305 Roo.apply(Roo.bootstrap.MonthField, {
20324 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20325 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20330 Roo.apply(Roo.bootstrap.MonthField, {
20334 cls: 'datepicker dropdown-menu roo-dynamic',
20338 cls: 'datepicker-months',
20342 cls: 'table-condensed',
20344 Roo.bootstrap.DateField.content
20364 * @class Roo.bootstrap.CheckBox
20365 * @extends Roo.bootstrap.Input
20366 * Bootstrap CheckBox class
20368 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20369 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20370 * @cfg {String} boxLabel The text that appears beside the checkbox
20371 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20372 * @cfg {Boolean} checked initnal the element
20373 * @cfg {Boolean} inline inline the element (default false)
20374 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20375 * @cfg {String} tooltip label tooltip
20378 * Create a new CheckBox
20379 * @param {Object} config The config object
20382 Roo.bootstrap.CheckBox = function(config){
20383 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20388 * Fires when the element is checked or unchecked.
20389 * @param {Roo.bootstrap.CheckBox} this This input
20390 * @param {Boolean} checked The new checked value
20395 * Fires when the element is click.
20396 * @param {Roo.bootstrap.CheckBox} this This input
20403 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20405 inputType: 'checkbox',
20414 getAutoCreate : function()
20416 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20422 cfg.cls = 'form-group ' + this.inputType; //input-group
20425 cfg.cls += ' ' + this.inputType + '-inline';
20431 type : this.inputType,
20432 value : this.inputValue,
20433 cls : 'roo-' + this.inputType, //'form-box',
20434 placeholder : this.placeholder || ''
20438 if(this.inputType != 'radio'){
20442 cls : 'roo-hidden-value',
20443 value : this.checked ? this.inputValue : this.valueOff
20448 if (this.weight) { // Validity check?
20449 cfg.cls += " " + this.inputType + "-" + this.weight;
20452 if (this.disabled) {
20453 input.disabled=true;
20457 input.checked = this.checked;
20462 input.name = this.name;
20464 if(this.inputType != 'radio'){
20465 hidden.name = this.name;
20466 input.name = '_hidden_' + this.name;
20471 input.cls += ' input-' + this.size;
20476 ['xs','sm','md','lg'].map(function(size){
20477 if (settings[size]) {
20478 cfg.cls += ' col-' + size + '-' + settings[size];
20482 var inputblock = input;
20484 if (this.before || this.after) {
20487 cls : 'input-group',
20492 inputblock.cn.push({
20494 cls : 'input-group-addon',
20499 inputblock.cn.push(input);
20501 if(this.inputType != 'radio'){
20502 inputblock.cn.push(hidden);
20506 inputblock.cn.push({
20508 cls : 'input-group-addon',
20515 if (align ==='left' && this.fieldLabel.length) {
20516 // Roo.log("left and has label");
20521 cls : 'control-label',
20522 html : this.fieldLabel
20532 if(this.labelWidth > 12){
20533 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20536 if(this.labelWidth < 13 && this.labelmd == 0){
20537 this.labelmd = this.labelWidth;
20540 if(this.labellg > 0){
20541 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20542 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20545 if(this.labelmd > 0){
20546 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20547 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20550 if(this.labelsm > 0){
20551 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20552 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20555 if(this.labelxs > 0){
20556 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20557 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20560 } else if ( this.fieldLabel.length) {
20561 // Roo.log(" label");
20565 tag: this.boxLabel ? 'span' : 'label',
20567 cls: 'control-label box-input-label',
20568 //cls : 'input-group-addon',
20569 html : this.fieldLabel
20578 // Roo.log(" no label && no align");
20579 cfg.cn = [ inputblock ] ;
20585 var boxLabelCfg = {
20587 //'for': id, // box label is handled by onclick - so no for...
20589 html: this.boxLabel
20593 boxLabelCfg.tooltip = this.tooltip;
20596 cfg.cn.push(boxLabelCfg);
20599 if(this.inputType != 'radio'){
20600 cfg.cn.push(hidden);
20608 * return the real input element.
20610 inputEl: function ()
20612 return this.el.select('input.roo-' + this.inputType,true).first();
20614 hiddenEl: function ()
20616 return this.el.select('input.roo-hidden-value',true).first();
20619 labelEl: function()
20621 return this.el.select('label.control-label',true).first();
20623 /* depricated... */
20627 return this.labelEl();
20630 boxLabelEl: function()
20632 return this.el.select('label.box-label',true).first();
20635 initEvents : function()
20637 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20639 this.inputEl().on('click', this.onClick, this);
20641 if (this.boxLabel) {
20642 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20645 this.startValue = this.getValue();
20648 Roo.bootstrap.CheckBox.register(this);
20652 onClick : function(e)
20654 if(this.fireEvent('click', this, e) !== false){
20655 this.setChecked(!this.checked);
20660 setChecked : function(state,suppressEvent)
20662 this.startValue = this.getValue();
20664 if(this.inputType == 'radio'){
20666 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20667 e.dom.checked = false;
20670 this.inputEl().dom.checked = true;
20672 this.inputEl().dom.value = this.inputValue;
20674 if(suppressEvent !== true){
20675 this.fireEvent('check', this, true);
20683 this.checked = state;
20685 this.inputEl().dom.checked = state;
20688 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20690 if(suppressEvent !== true){
20691 this.fireEvent('check', this, state);
20697 getValue : function()
20699 if(this.inputType == 'radio'){
20700 return this.getGroupValue();
20703 return this.hiddenEl().dom.value;
20707 getGroupValue : function()
20709 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20713 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20716 setValue : function(v,suppressEvent)
20718 if(this.inputType == 'radio'){
20719 this.setGroupValue(v, suppressEvent);
20723 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20728 setGroupValue : function(v, suppressEvent)
20730 this.startValue = this.getValue();
20732 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20733 e.dom.checked = false;
20735 if(e.dom.value == v){
20736 e.dom.checked = true;
20740 if(suppressEvent !== true){
20741 this.fireEvent('check', this, true);
20749 validate : function()
20751 if(this.getVisibilityEl().hasClass('hidden')){
20757 (this.inputType == 'radio' && this.validateRadio()) ||
20758 (this.inputType == 'checkbox' && this.validateCheckbox())
20764 this.markInvalid();
20768 validateRadio : function()
20770 if(this.getVisibilityEl().hasClass('hidden')){
20774 if(this.allowBlank){
20780 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20781 if(!e.dom.checked){
20793 validateCheckbox : function()
20796 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20797 //return (this.getValue() == this.inputValue) ? true : false;
20800 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20808 for(var i in group){
20809 if(group[i].el.isVisible(true)){
20817 for(var i in group){
20822 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20829 * Mark this field as valid
20831 markValid : function()
20835 this.fireEvent('valid', this);
20837 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20840 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20847 if(this.inputType == 'radio'){
20848 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20849 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20850 e.findParent('.form-group', false, true).addClass(_this.validClass);
20857 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20858 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20862 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20868 for(var i in group){
20869 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20870 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20875 * Mark this field as invalid
20876 * @param {String} msg The validation message
20878 markInvalid : function(msg)
20880 if(this.allowBlank){
20886 this.fireEvent('invalid', this, msg);
20888 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20891 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20895 label.markInvalid();
20898 if(this.inputType == 'radio'){
20899 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20900 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20901 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20908 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20909 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20913 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20919 for(var i in group){
20920 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20921 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20926 clearInvalid : function()
20928 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20930 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20932 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20934 if (label && label.iconEl) {
20935 label.iconEl.removeClass(label.validClass);
20936 label.iconEl.removeClass(label.invalidClass);
20940 disable : function()
20942 if(this.inputType != 'radio'){
20943 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20950 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20951 _this.getActionEl().addClass(this.disabledClass);
20952 e.dom.disabled = true;
20956 this.disabled = true;
20957 this.fireEvent("disable", this);
20961 enable : function()
20963 if(this.inputType != 'radio'){
20964 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20971 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20972 _this.getActionEl().removeClass(this.disabledClass);
20973 e.dom.disabled = false;
20977 this.disabled = false;
20978 this.fireEvent("enable", this);
20982 setBoxLabel : function(v)
20987 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20993 Roo.apply(Roo.bootstrap.CheckBox, {
20998 * register a CheckBox Group
20999 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21001 register : function(checkbox)
21003 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21004 this.groups[checkbox.groupId] = {};
21007 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21011 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21015 * fetch a CheckBox Group based on the group ID
21016 * @param {string} the group ID
21017 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21019 get: function(groupId) {
21020 if (typeof(this.groups[groupId]) == 'undefined') {
21024 return this.groups[groupId] ;
21037 * @class Roo.bootstrap.Radio
21038 * @extends Roo.bootstrap.Component
21039 * Bootstrap Radio class
21040 * @cfg {String} boxLabel - the label associated
21041 * @cfg {String} value - the value of radio
21044 * Create a new Radio
21045 * @param {Object} config The config object
21047 Roo.bootstrap.Radio = function(config){
21048 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21052 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21058 getAutoCreate : function()
21062 cls : 'form-group radio',
21067 html : this.boxLabel
21075 initEvents : function()
21077 this.parent().register(this);
21079 this.el.on('click', this.onClick, this);
21083 onClick : function(e)
21085 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21086 this.setChecked(true);
21090 setChecked : function(state, suppressEvent)
21092 this.parent().setValue(this.value, suppressEvent);
21096 setBoxLabel : function(v)
21101 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21116 * @class Roo.bootstrap.SecurePass
21117 * @extends Roo.bootstrap.Input
21118 * Bootstrap SecurePass class
21122 * Create a new SecurePass
21123 * @param {Object} config The config object
21126 Roo.bootstrap.SecurePass = function (config) {
21127 // these go here, so the translation tool can replace them..
21129 PwdEmpty: "Please type a password, and then retype it to confirm.",
21130 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21131 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21132 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21133 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21134 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21135 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21136 TooWeak: "Your password is Too Weak."
21138 this.meterLabel = "Password strength:";
21139 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21140 this.meterClass = [
21141 "roo-password-meter-tooweak",
21142 "roo-password-meter-weak",
21143 "roo-password-meter-medium",
21144 "roo-password-meter-strong",
21145 "roo-password-meter-grey"
21150 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21153 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21155 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21157 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21158 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21159 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21160 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21161 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21162 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21163 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21173 * @cfg {String/Object} Label for the strength meter (defaults to
21174 * 'Password strength:')
21179 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21180 * ['Weak', 'Medium', 'Strong'])
21183 pwdStrengths: false,
21196 initEvents: function ()
21198 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21200 if (this.el.is('input[type=password]') && Roo.isSafari) {
21201 this.el.on('keydown', this.SafariOnKeyDown, this);
21204 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21207 onRender: function (ct, position)
21209 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21210 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21211 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21213 this.trigger.createChild({
21218 cls: 'roo-password-meter-grey col-xs-12',
21221 //width: this.meterWidth + 'px'
21225 cls: 'roo-password-meter-text'
21231 if (this.hideTrigger) {
21232 this.trigger.setDisplayed(false);
21234 this.setSize(this.width || '', this.height || '');
21237 onDestroy: function ()
21239 if (this.trigger) {
21240 this.trigger.removeAllListeners();
21241 this.trigger.remove();
21244 this.wrap.remove();
21246 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21249 checkStrength: function ()
21251 var pwd = this.inputEl().getValue();
21252 if (pwd == this._lastPwd) {
21257 if (this.ClientSideStrongPassword(pwd)) {
21259 } else if (this.ClientSideMediumPassword(pwd)) {
21261 } else if (this.ClientSideWeakPassword(pwd)) {
21267 Roo.log('strength1: ' + strength);
21269 //var pm = this.trigger.child('div/div/div').dom;
21270 var pm = this.trigger.child('div/div');
21271 pm.removeClass(this.meterClass);
21272 pm.addClass(this.meterClass[strength]);
21275 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21277 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21279 this._lastPwd = pwd;
21283 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21285 this._lastPwd = '';
21287 var pm = this.trigger.child('div/div');
21288 pm.removeClass(this.meterClass);
21289 pm.addClass('roo-password-meter-grey');
21292 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21295 this.inputEl().dom.type='password';
21298 validateValue: function (value)
21301 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21304 if (value.length == 0) {
21305 if (this.allowBlank) {
21306 this.clearInvalid();
21310 this.markInvalid(this.errors.PwdEmpty);
21311 this.errorMsg = this.errors.PwdEmpty;
21319 if ('[\x21-\x7e]*'.match(value)) {
21320 this.markInvalid(this.errors.PwdBadChar);
21321 this.errorMsg = this.errors.PwdBadChar;
21324 if (value.length < 6) {
21325 this.markInvalid(this.errors.PwdShort);
21326 this.errorMsg = this.errors.PwdShort;
21329 if (value.length > 16) {
21330 this.markInvalid(this.errors.PwdLong);
21331 this.errorMsg = this.errors.PwdLong;
21335 if (this.ClientSideStrongPassword(value)) {
21337 } else if (this.ClientSideMediumPassword(value)) {
21339 } else if (this.ClientSideWeakPassword(value)) {
21346 if (strength < 2) {
21347 //this.markInvalid(this.errors.TooWeak);
21348 this.errorMsg = this.errors.TooWeak;
21353 console.log('strength2: ' + strength);
21355 //var pm = this.trigger.child('div/div/div').dom;
21357 var pm = this.trigger.child('div/div');
21358 pm.removeClass(this.meterClass);
21359 pm.addClass(this.meterClass[strength]);
21361 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21363 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21365 this.errorMsg = '';
21369 CharacterSetChecks: function (type)
21372 this.fResult = false;
21375 isctype: function (character, type)
21378 case this.kCapitalLetter:
21379 if (character >= 'A' && character <= 'Z') {
21384 case this.kSmallLetter:
21385 if (character >= 'a' && character <= 'z') {
21391 if (character >= '0' && character <= '9') {
21396 case this.kPunctuation:
21397 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21408 IsLongEnough: function (pwd, size)
21410 return !(pwd == null || isNaN(size) || pwd.length < size);
21413 SpansEnoughCharacterSets: function (word, nb)
21415 if (!this.IsLongEnough(word, nb))
21420 var characterSetChecks = new Array(
21421 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21422 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21425 for (var index = 0; index < word.length; ++index) {
21426 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21427 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21428 characterSetChecks[nCharSet].fResult = true;
21435 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21436 if (characterSetChecks[nCharSet].fResult) {
21441 if (nCharSets < nb) {
21447 ClientSideStrongPassword: function (pwd)
21449 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21452 ClientSideMediumPassword: function (pwd)
21454 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21457 ClientSideWeakPassword: function (pwd)
21459 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21462 })//<script type="text/javascript">
21465 * Based Ext JS Library 1.1.1
21466 * Copyright(c) 2006-2007, Ext JS, LLC.
21472 * @class Roo.HtmlEditorCore
21473 * @extends Roo.Component
21474 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21476 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21479 Roo.HtmlEditorCore = function(config){
21482 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21487 * @event initialize
21488 * Fires when the editor is fully initialized (including the iframe)
21489 * @param {Roo.HtmlEditorCore} this
21494 * Fires when the editor is first receives the focus. Any insertion must wait
21495 * until after this event.
21496 * @param {Roo.HtmlEditorCore} this
21500 * @event beforesync
21501 * Fires before the textarea is updated with content from the editor iframe. Return false
21502 * to cancel the sync.
21503 * @param {Roo.HtmlEditorCore} this
21504 * @param {String} html
21508 * @event beforepush
21509 * Fires before the iframe editor is updated with content from the textarea. Return false
21510 * to cancel the push.
21511 * @param {Roo.HtmlEditorCore} this
21512 * @param {String} html
21517 * Fires when the textarea is updated with content from the editor iframe.
21518 * @param {Roo.HtmlEditorCore} this
21519 * @param {String} html
21524 * Fires when the iframe editor is updated with content from the textarea.
21525 * @param {Roo.HtmlEditorCore} this
21526 * @param {String} html
21531 * @event editorevent
21532 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21533 * @param {Roo.HtmlEditorCore} this
21539 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21541 // defaults : white / black...
21542 this.applyBlacklists();
21549 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21553 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21559 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21564 * @cfg {Number} height (in pixels)
21568 * @cfg {Number} width (in pixels)
21573 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21576 stylesheets: false,
21581 // private properties
21582 validationEvent : false,
21584 initialized : false,
21586 sourceEditMode : false,
21587 onFocus : Roo.emptyFn,
21589 hideMode:'offsets',
21593 // blacklist + whitelisted elements..
21600 * Protected method that will not generally be called directly. It
21601 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21602 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21604 getDocMarkup : function(){
21608 // inherit styels from page...??
21609 if (this.stylesheets === false) {
21611 Roo.get(document.head).select('style').each(function(node) {
21612 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21615 Roo.get(document.head).select('link').each(function(node) {
21616 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21619 } else if (!this.stylesheets.length) {
21621 st = '<style type="text/css">' +
21622 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21625 st = '<style type="text/css">' +
21630 st += '<style type="text/css">' +
21631 'IMG { cursor: pointer } ' +
21634 var cls = 'roo-htmleditor-body';
21636 if(this.bodyCls.length){
21637 cls += ' ' + this.bodyCls;
21640 return '<html><head>' + st +
21641 //<style type="text/css">' +
21642 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21644 ' </head><body class="' + cls + '"></body></html>';
21648 onRender : function(ct, position)
21651 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21652 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21655 this.el.dom.style.border = '0 none';
21656 this.el.dom.setAttribute('tabIndex', -1);
21657 this.el.addClass('x-hidden hide');
21661 if(Roo.isIE){ // fix IE 1px bogus margin
21662 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21666 this.frameId = Roo.id();
21670 var iframe = this.owner.wrap.createChild({
21672 cls: 'form-control', // bootstrap..
21674 name: this.frameId,
21675 frameBorder : 'no',
21676 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21681 this.iframe = iframe.dom;
21683 this.assignDocWin();
21685 this.doc.designMode = 'on';
21688 this.doc.write(this.getDocMarkup());
21692 var task = { // must defer to wait for browser to be ready
21694 //console.log("run task?" + this.doc.readyState);
21695 this.assignDocWin();
21696 if(this.doc.body || this.doc.readyState == 'complete'){
21698 this.doc.designMode="on";
21702 Roo.TaskMgr.stop(task);
21703 this.initEditor.defer(10, this);
21710 Roo.TaskMgr.start(task);
21715 onResize : function(w, h)
21717 Roo.log('resize: ' +w + ',' + h );
21718 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21722 if(typeof w == 'number'){
21724 this.iframe.style.width = w + 'px';
21726 if(typeof h == 'number'){
21728 this.iframe.style.height = h + 'px';
21730 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21737 * Toggles the editor between standard and source edit mode.
21738 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21740 toggleSourceEdit : function(sourceEditMode){
21742 this.sourceEditMode = sourceEditMode === true;
21744 if(this.sourceEditMode){
21746 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21749 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21750 //this.iframe.className = '';
21753 //this.setSize(this.owner.wrap.getSize());
21754 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21761 * Protected method that will not generally be called directly. If you need/want
21762 * custom HTML cleanup, this is the method you should override.
21763 * @param {String} html The HTML to be cleaned
21764 * return {String} The cleaned HTML
21766 cleanHtml : function(html){
21767 html = String(html);
21768 if(html.length > 5){
21769 if(Roo.isSafari){ // strip safari nonsense
21770 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21773 if(html == ' '){
21780 * HTML Editor -> Textarea
21781 * Protected method that will not generally be called directly. Syncs the contents
21782 * of the editor iframe with the textarea.
21784 syncValue : function(){
21785 if(this.initialized){
21786 var bd = (this.doc.body || this.doc.documentElement);
21787 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21788 var html = bd.innerHTML;
21790 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21791 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21793 html = '<div style="'+m[0]+'">' + html + '</div>';
21796 html = this.cleanHtml(html);
21797 // fix up the special chars.. normaly like back quotes in word...
21798 // however we do not want to do this with chinese..
21799 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21800 var cc = b.charCodeAt();
21802 (cc >= 0x4E00 && cc < 0xA000 ) ||
21803 (cc >= 0x3400 && cc < 0x4E00 ) ||
21804 (cc >= 0xf900 && cc < 0xfb00 )
21810 if(this.owner.fireEvent('beforesync', this, html) !== false){
21811 this.el.dom.value = html;
21812 this.owner.fireEvent('sync', this, html);
21818 * Protected method that will not generally be called directly. Pushes the value of the textarea
21819 * into the iframe editor.
21821 pushValue : function(){
21822 if(this.initialized){
21823 var v = this.el.dom.value.trim();
21825 // if(v.length < 1){
21829 if(this.owner.fireEvent('beforepush', this, v) !== false){
21830 var d = (this.doc.body || this.doc.documentElement);
21832 this.cleanUpPaste();
21833 this.el.dom.value = d.innerHTML;
21834 this.owner.fireEvent('push', this, v);
21840 deferFocus : function(){
21841 this.focus.defer(10, this);
21845 focus : function(){
21846 if(this.win && !this.sourceEditMode){
21853 assignDocWin: function()
21855 var iframe = this.iframe;
21858 this.doc = iframe.contentWindow.document;
21859 this.win = iframe.contentWindow;
21861 // if (!Roo.get(this.frameId)) {
21864 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21865 // this.win = Roo.get(this.frameId).dom.contentWindow;
21867 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21871 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21872 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21877 initEditor : function(){
21878 //console.log("INIT EDITOR");
21879 this.assignDocWin();
21883 this.doc.designMode="on";
21885 this.doc.write(this.getDocMarkup());
21888 var dbody = (this.doc.body || this.doc.documentElement);
21889 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21890 // this copies styles from the containing element into thsi one..
21891 // not sure why we need all of this..
21892 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21894 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21895 //ss['background-attachment'] = 'fixed'; // w3c
21896 dbody.bgProperties = 'fixed'; // ie
21897 //Roo.DomHelper.applyStyles(dbody, ss);
21898 Roo.EventManager.on(this.doc, {
21899 //'mousedown': this.onEditorEvent,
21900 'mouseup': this.onEditorEvent,
21901 'dblclick': this.onEditorEvent,
21902 'click': this.onEditorEvent,
21903 'keyup': this.onEditorEvent,
21908 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21910 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21911 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21913 this.initialized = true;
21915 this.owner.fireEvent('initialize', this);
21920 onDestroy : function(){
21926 //for (var i =0; i < this.toolbars.length;i++) {
21927 // // fixme - ask toolbars for heights?
21928 // this.toolbars[i].onDestroy();
21931 //this.wrap.dom.innerHTML = '';
21932 //this.wrap.remove();
21937 onFirstFocus : function(){
21939 this.assignDocWin();
21942 this.activated = true;
21945 if(Roo.isGecko){ // prevent silly gecko errors
21947 var s = this.win.getSelection();
21948 if(!s.focusNode || s.focusNode.nodeType != 3){
21949 var r = s.getRangeAt(0);
21950 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21955 this.execCmd('useCSS', true);
21956 this.execCmd('styleWithCSS', false);
21959 this.owner.fireEvent('activate', this);
21963 adjustFont: function(btn){
21964 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21965 //if(Roo.isSafari){ // safari
21968 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21969 if(Roo.isSafari){ // safari
21970 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21971 v = (v < 10) ? 10 : v;
21972 v = (v > 48) ? 48 : v;
21973 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21978 v = Math.max(1, v+adjust);
21980 this.execCmd('FontSize', v );
21983 onEditorEvent : function(e)
21985 this.owner.fireEvent('editorevent', this, e);
21986 // this.updateToolbar();
21987 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21990 insertTag : function(tg)
21992 // could be a bit smarter... -> wrap the current selected tRoo..
21993 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21995 range = this.createRange(this.getSelection());
21996 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21997 wrappingNode.appendChild(range.extractContents());
21998 range.insertNode(wrappingNode);
22005 this.execCmd("formatblock", tg);
22009 insertText : function(txt)
22013 var range = this.createRange();
22014 range.deleteContents();
22015 //alert(Sender.getAttribute('label'));
22017 range.insertNode(this.doc.createTextNode(txt));
22023 * Executes a Midas editor command on the editor document and performs necessary focus and
22024 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22025 * @param {String} cmd The Midas command
22026 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22028 relayCmd : function(cmd, value){
22030 this.execCmd(cmd, value);
22031 this.owner.fireEvent('editorevent', this);
22032 //this.updateToolbar();
22033 this.owner.deferFocus();
22037 * Executes a Midas editor command directly on the editor document.
22038 * For visual commands, you should use {@link #relayCmd} instead.
22039 * <b>This should only be called after the editor is initialized.</b>
22040 * @param {String} cmd The Midas command
22041 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22043 execCmd : function(cmd, value){
22044 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22051 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22053 * @param {String} text | dom node..
22055 insertAtCursor : function(text)
22058 if(!this.activated){
22064 var r = this.doc.selection.createRange();
22075 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22079 // from jquery ui (MIT licenced)
22081 var win = this.win;
22083 if (win.getSelection && win.getSelection().getRangeAt) {
22084 range = win.getSelection().getRangeAt(0);
22085 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22086 range.insertNode(node);
22087 } else if (win.document.selection && win.document.selection.createRange) {
22088 // no firefox support
22089 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22090 win.document.selection.createRange().pasteHTML(txt);
22092 // no firefox support
22093 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22094 this.execCmd('InsertHTML', txt);
22103 mozKeyPress : function(e){
22105 var c = e.getCharCode(), cmd;
22108 c = String.fromCharCode(c).toLowerCase();
22122 this.cleanUpPaste.defer(100, this);
22130 e.preventDefault();
22138 fixKeys : function(){ // load time branching for fastest keydown performance
22140 return function(e){
22141 var k = e.getKey(), r;
22144 r = this.doc.selection.createRange();
22147 r.pasteHTML('    ');
22154 r = this.doc.selection.createRange();
22156 var target = r.parentElement();
22157 if(!target || target.tagName.toLowerCase() != 'li'){
22159 r.pasteHTML('<br />');
22165 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22166 this.cleanUpPaste.defer(100, this);
22172 }else if(Roo.isOpera){
22173 return function(e){
22174 var k = e.getKey();
22178 this.execCmd('InsertHTML','    ');
22181 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22182 this.cleanUpPaste.defer(100, this);
22187 }else if(Roo.isSafari){
22188 return function(e){
22189 var k = e.getKey();
22193 this.execCmd('InsertText','\t');
22197 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22198 this.cleanUpPaste.defer(100, this);
22206 getAllAncestors: function()
22208 var p = this.getSelectedNode();
22211 a.push(p); // push blank onto stack..
22212 p = this.getParentElement();
22216 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22220 a.push(this.doc.body);
22224 lastSelNode : false,
22227 getSelection : function()
22229 this.assignDocWin();
22230 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22233 getSelectedNode: function()
22235 // this may only work on Gecko!!!
22237 // should we cache this!!!!
22242 var range = this.createRange(this.getSelection()).cloneRange();
22245 var parent = range.parentElement();
22247 var testRange = range.duplicate();
22248 testRange.moveToElementText(parent);
22249 if (testRange.inRange(range)) {
22252 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22255 parent = parent.parentElement;
22260 // is ancestor a text element.
22261 var ac = range.commonAncestorContainer;
22262 if (ac.nodeType == 3) {
22263 ac = ac.parentNode;
22266 var ar = ac.childNodes;
22269 var other_nodes = [];
22270 var has_other_nodes = false;
22271 for (var i=0;i<ar.length;i++) {
22272 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22275 // fullly contained node.
22277 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22282 // probably selected..
22283 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22284 other_nodes.push(ar[i]);
22288 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22293 has_other_nodes = true;
22295 if (!nodes.length && other_nodes.length) {
22296 nodes= other_nodes;
22298 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22304 createRange: function(sel)
22306 // this has strange effects when using with
22307 // top toolbar - not sure if it's a great idea.
22308 //this.editor.contentWindow.focus();
22309 if (typeof sel != "undefined") {
22311 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22313 return this.doc.createRange();
22316 return this.doc.createRange();
22319 getParentElement: function()
22322 this.assignDocWin();
22323 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22325 var range = this.createRange(sel);
22328 var p = range.commonAncestorContainer;
22329 while (p.nodeType == 3) { // text node
22340 * Range intersection.. the hard stuff...
22344 * [ -- selected range --- ]
22348 * if end is before start or hits it. fail.
22349 * if start is after end or hits it fail.
22351 * if either hits (but other is outside. - then it's not
22357 // @see http://www.thismuchiknow.co.uk/?p=64.
22358 rangeIntersectsNode : function(range, node)
22360 var nodeRange = node.ownerDocument.createRange();
22362 nodeRange.selectNode(node);
22364 nodeRange.selectNodeContents(node);
22367 var rangeStartRange = range.cloneRange();
22368 rangeStartRange.collapse(true);
22370 var rangeEndRange = range.cloneRange();
22371 rangeEndRange.collapse(false);
22373 var nodeStartRange = nodeRange.cloneRange();
22374 nodeStartRange.collapse(true);
22376 var nodeEndRange = nodeRange.cloneRange();
22377 nodeEndRange.collapse(false);
22379 return rangeStartRange.compareBoundaryPoints(
22380 Range.START_TO_START, nodeEndRange) == -1 &&
22381 rangeEndRange.compareBoundaryPoints(
22382 Range.START_TO_START, nodeStartRange) == 1;
22386 rangeCompareNode : function(range, node)
22388 var nodeRange = node.ownerDocument.createRange();
22390 nodeRange.selectNode(node);
22392 nodeRange.selectNodeContents(node);
22396 range.collapse(true);
22398 nodeRange.collapse(true);
22400 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22401 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22403 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22405 var nodeIsBefore = ss == 1;
22406 var nodeIsAfter = ee == -1;
22408 if (nodeIsBefore && nodeIsAfter) {
22411 if (!nodeIsBefore && nodeIsAfter) {
22412 return 1; //right trailed.
22415 if (nodeIsBefore && !nodeIsAfter) {
22416 return 2; // left trailed.
22422 // private? - in a new class?
22423 cleanUpPaste : function()
22425 // cleans up the whole document..
22426 Roo.log('cleanuppaste');
22428 this.cleanUpChildren(this.doc.body);
22429 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22430 if (clean != this.doc.body.innerHTML) {
22431 this.doc.body.innerHTML = clean;
22436 cleanWordChars : function(input) {// change the chars to hex code
22437 var he = Roo.HtmlEditorCore;
22439 var output = input;
22440 Roo.each(he.swapCodes, function(sw) {
22441 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22443 output = output.replace(swapper, sw[1]);
22450 cleanUpChildren : function (n)
22452 if (!n.childNodes.length) {
22455 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22456 this.cleanUpChild(n.childNodes[i]);
22463 cleanUpChild : function (node)
22466 //console.log(node);
22467 if (node.nodeName == "#text") {
22468 // clean up silly Windows -- stuff?
22471 if (node.nodeName == "#comment") {
22472 node.parentNode.removeChild(node);
22473 // clean up silly Windows -- stuff?
22476 var lcname = node.tagName.toLowerCase();
22477 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22478 // whitelist of tags..
22480 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22482 node.parentNode.removeChild(node);
22487 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22489 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22490 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22492 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22493 // remove_keep_children = true;
22496 if (remove_keep_children) {
22497 this.cleanUpChildren(node);
22498 // inserts everything just before this node...
22499 while (node.childNodes.length) {
22500 var cn = node.childNodes[0];
22501 node.removeChild(cn);
22502 node.parentNode.insertBefore(cn, node);
22504 node.parentNode.removeChild(node);
22508 if (!node.attributes || !node.attributes.length) {
22509 this.cleanUpChildren(node);
22513 function cleanAttr(n,v)
22516 if (v.match(/^\./) || v.match(/^\//)) {
22519 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22522 if (v.match(/^#/)) {
22525 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22526 node.removeAttribute(n);
22530 var cwhite = this.cwhite;
22531 var cblack = this.cblack;
22533 function cleanStyle(n,v)
22535 if (v.match(/expression/)) { //XSS?? should we even bother..
22536 node.removeAttribute(n);
22540 var parts = v.split(/;/);
22543 Roo.each(parts, function(p) {
22544 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22548 var l = p.split(':').shift().replace(/\s+/g,'');
22549 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22551 if ( cwhite.length && cblack.indexOf(l) > -1) {
22552 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22553 //node.removeAttribute(n);
22557 // only allow 'c whitelisted system attributes'
22558 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22559 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22560 //node.removeAttribute(n);
22570 if (clean.length) {
22571 node.setAttribute(n, clean.join(';'));
22573 node.removeAttribute(n);
22579 for (var i = node.attributes.length-1; i > -1 ; i--) {
22580 var a = node.attributes[i];
22583 if (a.name.toLowerCase().substr(0,2)=='on') {
22584 node.removeAttribute(a.name);
22587 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22588 node.removeAttribute(a.name);
22591 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22592 cleanAttr(a.name,a.value); // fixme..
22595 if (a.name == 'style') {
22596 cleanStyle(a.name,a.value);
22599 /// clean up MS crap..
22600 // tecnically this should be a list of valid class'es..
22603 if (a.name == 'class') {
22604 if (a.value.match(/^Mso/)) {
22605 node.className = '';
22608 if (a.value.match(/^body$/)) {
22609 node.className = '';
22620 this.cleanUpChildren(node);
22626 * Clean up MS wordisms...
22628 cleanWord : function(node)
22633 this.cleanWord(this.doc.body);
22636 if (node.nodeName == "#text") {
22637 // clean up silly Windows -- stuff?
22640 if (node.nodeName == "#comment") {
22641 node.parentNode.removeChild(node);
22642 // clean up silly Windows -- stuff?
22646 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22647 node.parentNode.removeChild(node);
22651 // remove - but keep children..
22652 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22653 while (node.childNodes.length) {
22654 var cn = node.childNodes[0];
22655 node.removeChild(cn);
22656 node.parentNode.insertBefore(cn, node);
22658 node.parentNode.removeChild(node);
22659 this.iterateChildren(node, this.cleanWord);
22663 if (node.className.length) {
22665 var cn = node.className.split(/\W+/);
22667 Roo.each(cn, function(cls) {
22668 if (cls.match(/Mso[a-zA-Z]+/)) {
22673 node.className = cna.length ? cna.join(' ') : '';
22675 node.removeAttribute("class");
22679 if (node.hasAttribute("lang")) {
22680 node.removeAttribute("lang");
22683 if (node.hasAttribute("style")) {
22685 var styles = node.getAttribute("style").split(";");
22687 Roo.each(styles, function(s) {
22688 if (!s.match(/:/)) {
22691 var kv = s.split(":");
22692 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22695 // what ever is left... we allow.
22698 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22699 if (!nstyle.length) {
22700 node.removeAttribute('style');
22703 this.iterateChildren(node, this.cleanWord);
22709 * iterateChildren of a Node, calling fn each time, using this as the scole..
22710 * @param {DomNode} node node to iterate children of.
22711 * @param {Function} fn method of this class to call on each item.
22713 iterateChildren : function(node, fn)
22715 if (!node.childNodes.length) {
22718 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22719 fn.call(this, node.childNodes[i])
22725 * cleanTableWidths.
22727 * Quite often pasting from word etc.. results in tables with column and widths.
22728 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22731 cleanTableWidths : function(node)
22736 this.cleanTableWidths(this.doc.body);
22741 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22744 Roo.log(node.tagName);
22745 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22746 this.iterateChildren(node, this.cleanTableWidths);
22749 if (node.hasAttribute('width')) {
22750 node.removeAttribute('width');
22754 if (node.hasAttribute("style")) {
22757 var styles = node.getAttribute("style").split(";");
22759 Roo.each(styles, function(s) {
22760 if (!s.match(/:/)) {
22763 var kv = s.split(":");
22764 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22767 // what ever is left... we allow.
22770 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22771 if (!nstyle.length) {
22772 node.removeAttribute('style');
22776 this.iterateChildren(node, this.cleanTableWidths);
22784 domToHTML : function(currentElement, depth, nopadtext) {
22786 depth = depth || 0;
22787 nopadtext = nopadtext || false;
22789 if (!currentElement) {
22790 return this.domToHTML(this.doc.body);
22793 //Roo.log(currentElement);
22795 var allText = false;
22796 var nodeName = currentElement.nodeName;
22797 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22799 if (nodeName == '#text') {
22801 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22806 if (nodeName != 'BODY') {
22809 // Prints the node tagName, such as <A>, <IMG>, etc
22812 for(i = 0; i < currentElement.attributes.length;i++) {
22814 var aname = currentElement.attributes.item(i).name;
22815 if (!currentElement.attributes.item(i).value.length) {
22818 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22821 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22830 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22833 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22838 // Traverse the tree
22840 var currentElementChild = currentElement.childNodes.item(i);
22841 var allText = true;
22842 var innerHTML = '';
22844 while (currentElementChild) {
22845 // Formatting code (indent the tree so it looks nice on the screen)
22846 var nopad = nopadtext;
22847 if (lastnode == 'SPAN') {
22851 if (currentElementChild.nodeName == '#text') {
22852 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22853 toadd = nopadtext ? toadd : toadd.trim();
22854 if (!nopad && toadd.length > 80) {
22855 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22857 innerHTML += toadd;
22860 currentElementChild = currentElement.childNodes.item(i);
22866 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22868 // Recursively traverse the tree structure of the child node
22869 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22870 lastnode = currentElementChild.nodeName;
22872 currentElementChild=currentElement.childNodes.item(i);
22878 // The remaining code is mostly for formatting the tree
22879 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22884 ret+= "</"+tagName+">";
22890 applyBlacklists : function()
22892 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22893 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22897 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22898 if (b.indexOf(tag) > -1) {
22901 this.white.push(tag);
22905 Roo.each(w, function(tag) {
22906 if (b.indexOf(tag) > -1) {
22909 if (this.white.indexOf(tag) > -1) {
22912 this.white.push(tag);
22917 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22918 if (w.indexOf(tag) > -1) {
22921 this.black.push(tag);
22925 Roo.each(b, function(tag) {
22926 if (w.indexOf(tag) > -1) {
22929 if (this.black.indexOf(tag) > -1) {
22932 this.black.push(tag);
22937 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22938 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22942 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22943 if (b.indexOf(tag) > -1) {
22946 this.cwhite.push(tag);
22950 Roo.each(w, function(tag) {
22951 if (b.indexOf(tag) > -1) {
22954 if (this.cwhite.indexOf(tag) > -1) {
22957 this.cwhite.push(tag);
22962 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22963 if (w.indexOf(tag) > -1) {
22966 this.cblack.push(tag);
22970 Roo.each(b, function(tag) {
22971 if (w.indexOf(tag) > -1) {
22974 if (this.cblack.indexOf(tag) > -1) {
22977 this.cblack.push(tag);
22982 setStylesheets : function(stylesheets)
22984 if(typeof(stylesheets) == 'string'){
22985 Roo.get(this.iframe.contentDocument.head).createChild({
22987 rel : 'stylesheet',
22996 Roo.each(stylesheets, function(s) {
23001 Roo.get(_this.iframe.contentDocument.head).createChild({
23003 rel : 'stylesheet',
23012 removeStylesheets : function()
23016 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23021 setStyle : function(style)
23023 Roo.get(this.iframe.contentDocument.head).createChild({
23032 // hide stuff that is not compatible
23046 * @event specialkey
23050 * @cfg {String} fieldClass @hide
23053 * @cfg {String} focusClass @hide
23056 * @cfg {String} autoCreate @hide
23059 * @cfg {String} inputType @hide
23062 * @cfg {String} invalidClass @hide
23065 * @cfg {String} invalidText @hide
23068 * @cfg {String} msgFx @hide
23071 * @cfg {String} validateOnBlur @hide
23075 Roo.HtmlEditorCore.white = [
23076 'area', 'br', 'img', 'input', 'hr', 'wbr',
23078 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23079 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23080 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23081 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23082 'table', 'ul', 'xmp',
23084 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23087 'dir', 'menu', 'ol', 'ul', 'dl',
23093 Roo.HtmlEditorCore.black = [
23094 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23096 'base', 'basefont', 'bgsound', 'blink', 'body',
23097 'frame', 'frameset', 'head', 'html', 'ilayer',
23098 'iframe', 'layer', 'link', 'meta', 'object',
23099 'script', 'style' ,'title', 'xml' // clean later..
23101 Roo.HtmlEditorCore.clean = [
23102 'script', 'style', 'title', 'xml'
23104 Roo.HtmlEditorCore.remove = [
23109 Roo.HtmlEditorCore.ablack = [
23113 Roo.HtmlEditorCore.aclean = [
23114 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23118 Roo.HtmlEditorCore.pwhite= [
23119 'http', 'https', 'mailto'
23122 // white listed style attributes.
23123 Roo.HtmlEditorCore.cwhite= [
23124 // 'text-align', /// default is to allow most things..
23130 // black listed style attributes.
23131 Roo.HtmlEditorCore.cblack= [
23132 // 'font-size' -- this can be set by the project
23136 Roo.HtmlEditorCore.swapCodes =[
23155 * @class Roo.bootstrap.HtmlEditor
23156 * @extends Roo.bootstrap.TextArea
23157 * Bootstrap HtmlEditor class
23160 * Create a new HtmlEditor
23161 * @param {Object} config The config object
23164 Roo.bootstrap.HtmlEditor = function(config){
23165 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23166 if (!this.toolbars) {
23167 this.toolbars = [];
23170 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23173 * @event initialize
23174 * Fires when the editor is fully initialized (including the iframe)
23175 * @param {HtmlEditor} this
23180 * Fires when the editor is first receives the focus. Any insertion must wait
23181 * until after this event.
23182 * @param {HtmlEditor} this
23186 * @event beforesync
23187 * Fires before the textarea is updated with content from the editor iframe. Return false
23188 * to cancel the sync.
23189 * @param {HtmlEditor} this
23190 * @param {String} html
23194 * @event beforepush
23195 * Fires before the iframe editor is updated with content from the textarea. Return false
23196 * to cancel the push.
23197 * @param {HtmlEditor} this
23198 * @param {String} html
23203 * Fires when the textarea is updated with content from the editor iframe.
23204 * @param {HtmlEditor} this
23205 * @param {String} html
23210 * Fires when the iframe editor is updated with content from the textarea.
23211 * @param {HtmlEditor} this
23212 * @param {String} html
23216 * @event editmodechange
23217 * Fires when the editor switches edit modes
23218 * @param {HtmlEditor} this
23219 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23221 editmodechange: true,
23223 * @event editorevent
23224 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23225 * @param {HtmlEditor} this
23229 * @event firstfocus
23230 * Fires when on first focus - needed by toolbars..
23231 * @param {HtmlEditor} this
23236 * Auto save the htmlEditor value as a file into Events
23237 * @param {HtmlEditor} this
23241 * @event savedpreview
23242 * preview the saved version of htmlEditor
23243 * @param {HtmlEditor} this
23250 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23254 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23259 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23264 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23269 * @cfg {Number} height (in pixels)
23273 * @cfg {Number} width (in pixels)
23278 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23281 stylesheets: false,
23286 // private properties
23287 validationEvent : false,
23289 initialized : false,
23292 onFocus : Roo.emptyFn,
23294 hideMode:'offsets',
23296 tbContainer : false,
23300 toolbarContainer :function() {
23301 return this.wrap.select('.x-html-editor-tb',true).first();
23305 * Protected method that will not generally be called directly. It
23306 * is called when the editor creates its toolbar. Override this method if you need to
23307 * add custom toolbar buttons.
23308 * @param {HtmlEditor} editor
23310 createToolbar : function(){
23311 Roo.log('renewing');
23312 Roo.log("create toolbars");
23314 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23315 this.toolbars[0].render(this.toolbarContainer());
23319 // if (!editor.toolbars || !editor.toolbars.length) {
23320 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23323 // for (var i =0 ; i < editor.toolbars.length;i++) {
23324 // editor.toolbars[i] = Roo.factory(
23325 // typeof(editor.toolbars[i]) == 'string' ?
23326 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23327 // Roo.bootstrap.HtmlEditor);
23328 // editor.toolbars[i].init(editor);
23334 onRender : function(ct, position)
23336 // Roo.log("Call onRender: " + this.xtype);
23338 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23340 this.wrap = this.inputEl().wrap({
23341 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23344 this.editorcore.onRender(ct, position);
23346 if (this.resizable) {
23347 this.resizeEl = new Roo.Resizable(this.wrap, {
23351 minHeight : this.height,
23352 height: this.height,
23353 handles : this.resizable,
23356 resize : function(r, w, h) {
23357 _t.onResize(w,h); // -something
23363 this.createToolbar(this);
23366 if(!this.width && this.resizable){
23367 this.setSize(this.wrap.getSize());
23369 if (this.resizeEl) {
23370 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23371 // should trigger onReize..
23377 onResize : function(w, h)
23379 Roo.log('resize: ' +w + ',' + h );
23380 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23384 if(this.inputEl() ){
23385 if(typeof w == 'number'){
23386 var aw = w - this.wrap.getFrameWidth('lr');
23387 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23390 if(typeof h == 'number'){
23391 var tbh = -11; // fixme it needs to tool bar size!
23392 for (var i =0; i < this.toolbars.length;i++) {
23393 // fixme - ask toolbars for heights?
23394 tbh += this.toolbars[i].el.getHeight();
23395 //if (this.toolbars[i].footer) {
23396 // tbh += this.toolbars[i].footer.el.getHeight();
23404 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23405 ah -= 5; // knock a few pixes off for look..
23406 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23410 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23411 this.editorcore.onResize(ew,eh);
23416 * Toggles the editor between standard and source edit mode.
23417 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23419 toggleSourceEdit : function(sourceEditMode)
23421 this.editorcore.toggleSourceEdit(sourceEditMode);
23423 if(this.editorcore.sourceEditMode){
23424 Roo.log('editor - showing textarea');
23427 // Roo.log(this.syncValue());
23429 this.inputEl().removeClass(['hide', 'x-hidden']);
23430 this.inputEl().dom.removeAttribute('tabIndex');
23431 this.inputEl().focus();
23433 Roo.log('editor - hiding textarea');
23435 // Roo.log(this.pushValue());
23438 this.inputEl().addClass(['hide', 'x-hidden']);
23439 this.inputEl().dom.setAttribute('tabIndex', -1);
23440 //this.deferFocus();
23443 if(this.resizable){
23444 this.setSize(this.wrap.getSize());
23447 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23450 // private (for BoxComponent)
23451 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23453 // private (for BoxComponent)
23454 getResizeEl : function(){
23458 // private (for BoxComponent)
23459 getPositionEl : function(){
23464 initEvents : function(){
23465 this.originalValue = this.getValue();
23469 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23472 // markInvalid : Roo.emptyFn,
23474 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23477 // clearInvalid : Roo.emptyFn,
23479 setValue : function(v){
23480 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23481 this.editorcore.pushValue();
23486 deferFocus : function(){
23487 this.focus.defer(10, this);
23491 focus : function(){
23492 this.editorcore.focus();
23498 onDestroy : function(){
23504 for (var i =0; i < this.toolbars.length;i++) {
23505 // fixme - ask toolbars for heights?
23506 this.toolbars[i].onDestroy();
23509 this.wrap.dom.innerHTML = '';
23510 this.wrap.remove();
23515 onFirstFocus : function(){
23516 //Roo.log("onFirstFocus");
23517 this.editorcore.onFirstFocus();
23518 for (var i =0; i < this.toolbars.length;i++) {
23519 this.toolbars[i].onFirstFocus();
23525 syncValue : function()
23527 this.editorcore.syncValue();
23530 pushValue : function()
23532 this.editorcore.pushValue();
23536 // hide stuff that is not compatible
23550 * @event specialkey
23554 * @cfg {String} fieldClass @hide
23557 * @cfg {String} focusClass @hide
23560 * @cfg {String} autoCreate @hide
23563 * @cfg {String} inputType @hide
23566 * @cfg {String} invalidClass @hide
23569 * @cfg {String} invalidText @hide
23572 * @cfg {String} msgFx @hide
23575 * @cfg {String} validateOnBlur @hide
23584 Roo.namespace('Roo.bootstrap.htmleditor');
23586 * @class Roo.bootstrap.HtmlEditorToolbar1
23591 new Roo.bootstrap.HtmlEditor({
23594 new Roo.bootstrap.HtmlEditorToolbar1({
23595 disable : { fonts: 1 , format: 1, ..., ... , ...],
23601 * @cfg {Object} disable List of elements to disable..
23602 * @cfg {Array} btns List of additional buttons.
23606 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23609 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23612 Roo.apply(this, config);
23614 // default disabled, based on 'good practice'..
23615 this.disable = this.disable || {};
23616 Roo.applyIf(this.disable, {
23619 specialElements : true
23621 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23623 this.editor = config.editor;
23624 this.editorcore = config.editor.editorcore;
23626 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23628 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23629 // dont call parent... till later.
23631 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23636 editorcore : false,
23641 "h1","h2","h3","h4","h5","h6",
23643 "abbr", "acronym", "address", "cite", "samp", "var",
23647 onRender : function(ct, position)
23649 // Roo.log("Call onRender: " + this.xtype);
23651 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23653 this.el.dom.style.marginBottom = '0';
23655 var editorcore = this.editorcore;
23656 var editor= this.editor;
23659 var btn = function(id,cmd , toggle, handler, html){
23661 var event = toggle ? 'toggle' : 'click';
23666 xns: Roo.bootstrap,
23669 enableToggle:toggle !== false,
23671 pressed : toggle ? false : null,
23674 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23675 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23681 // var cb_box = function...
23686 xns: Roo.bootstrap,
23687 glyphicon : 'font',
23691 xns: Roo.bootstrap,
23695 Roo.each(this.formats, function(f) {
23696 style.menu.items.push({
23698 xns: Roo.bootstrap,
23699 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23704 editorcore.insertTag(this.tagname);
23711 children.push(style);
23713 btn('bold',false,true);
23714 btn('italic',false,true);
23715 btn('align-left', 'justifyleft',true);
23716 btn('align-center', 'justifycenter',true);
23717 btn('align-right' , 'justifyright',true);
23718 btn('link', false, false, function(btn) {
23719 //Roo.log("create link?");
23720 var url = prompt(this.createLinkText, this.defaultLinkValue);
23721 if(url && url != 'http:/'+'/'){
23722 this.editorcore.relayCmd('createlink', url);
23725 btn('list','insertunorderedlist',true);
23726 btn('pencil', false,true, function(btn){
23728 this.toggleSourceEdit(btn.pressed);
23731 if (this.editor.btns.length > 0) {
23732 for (var i = 0; i<this.editor.btns.length; i++) {
23733 children.push(this.editor.btns[i]);
23741 xns: Roo.bootstrap,
23746 xns: Roo.bootstrap,
23751 cog.menu.items.push({
23753 xns: Roo.bootstrap,
23754 html : Clean styles,
23759 editorcore.insertTag(this.tagname);
23768 this.xtype = 'NavSimplebar';
23770 for(var i=0;i< children.length;i++) {
23772 this.buttons.add(this.addxtypeChild(children[i]));
23776 editor.on('editorevent', this.updateToolbar, this);
23778 onBtnClick : function(id)
23780 this.editorcore.relayCmd(id);
23781 this.editorcore.focus();
23785 * Protected method that will not generally be called directly. It triggers
23786 * a toolbar update by reading the markup state of the current selection in the editor.
23788 updateToolbar: function(){
23790 if(!this.editorcore.activated){
23791 this.editor.onFirstFocus(); // is this neeed?
23795 var btns = this.buttons;
23796 var doc = this.editorcore.doc;
23797 btns.get('bold').setActive(doc.queryCommandState('bold'));
23798 btns.get('italic').setActive(doc.queryCommandState('italic'));
23799 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23801 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23802 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23803 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23805 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23806 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23809 var ans = this.editorcore.getAllAncestors();
23810 if (this.formatCombo) {
23813 var store = this.formatCombo.store;
23814 this.formatCombo.setValue("");
23815 for (var i =0; i < ans.length;i++) {
23816 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23818 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23826 // hides menus... - so this cant be on a menu...
23827 Roo.bootstrap.MenuMgr.hideAll();
23829 Roo.bootstrap.MenuMgr.hideAll();
23830 //this.editorsyncValue();
23832 onFirstFocus: function() {
23833 this.buttons.each(function(item){
23837 toggleSourceEdit : function(sourceEditMode){
23840 if(sourceEditMode){
23841 Roo.log("disabling buttons");
23842 this.buttons.each( function(item){
23843 if(item.cmd != 'pencil'){
23849 Roo.log("enabling buttons");
23850 if(this.editorcore.initialized){
23851 this.buttons.each( function(item){
23857 Roo.log("calling toggole on editor");
23858 // tell the editor that it's been pressed..
23859 this.editor.toggleSourceEdit(sourceEditMode);
23869 * @class Roo.bootstrap.Table.AbstractSelectionModel
23870 * @extends Roo.util.Observable
23871 * Abstract base class for grid SelectionModels. It provides the interface that should be
23872 * implemented by descendant classes. This class should not be directly instantiated.
23875 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23876 this.locked = false;
23877 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23881 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23882 /** @ignore Called by the grid automatically. Do not call directly. */
23883 init : function(grid){
23889 * Locks the selections.
23892 this.locked = true;
23896 * Unlocks the selections.
23898 unlock : function(){
23899 this.locked = false;
23903 * Returns true if the selections are locked.
23904 * @return {Boolean}
23906 isLocked : function(){
23907 return this.locked;
23911 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23912 * @class Roo.bootstrap.Table.RowSelectionModel
23913 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23914 * It supports multiple selections and keyboard selection/navigation.
23916 * @param {Object} config
23919 Roo.bootstrap.Table.RowSelectionModel = function(config){
23920 Roo.apply(this, config);
23921 this.selections = new Roo.util.MixedCollection(false, function(o){
23926 this.lastActive = false;
23930 * @event selectionchange
23931 * Fires when the selection changes
23932 * @param {SelectionModel} this
23934 "selectionchange" : true,
23936 * @event afterselectionchange
23937 * Fires after the selection changes (eg. by key press or clicking)
23938 * @param {SelectionModel} this
23940 "afterselectionchange" : true,
23942 * @event beforerowselect
23943 * Fires when a row is selected being selected, return false to cancel.
23944 * @param {SelectionModel} this
23945 * @param {Number} rowIndex The selected index
23946 * @param {Boolean} keepExisting False if other selections will be cleared
23948 "beforerowselect" : true,
23951 * Fires when a row is selected.
23952 * @param {SelectionModel} this
23953 * @param {Number} rowIndex The selected index
23954 * @param {Roo.data.Record} r The record
23956 "rowselect" : true,
23958 * @event rowdeselect
23959 * Fires when a row is deselected.
23960 * @param {SelectionModel} this
23961 * @param {Number} rowIndex The selected index
23963 "rowdeselect" : true
23965 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23966 this.locked = false;
23969 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23971 * @cfg {Boolean} singleSelect
23972 * True to allow selection of only one row at a time (defaults to false)
23974 singleSelect : false,
23977 initEvents : function()
23980 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23981 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23982 //}else{ // allow click to work like normal
23983 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23985 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23986 this.grid.on("rowclick", this.handleMouseDown, this);
23988 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23989 "up" : function(e){
23991 this.selectPrevious(e.shiftKey);
23992 }else if(this.last !== false && this.lastActive !== false){
23993 var last = this.last;
23994 this.selectRange(this.last, this.lastActive-1);
23995 this.grid.getView().focusRow(this.lastActive);
23996 if(last !== false){
24000 this.selectFirstRow();
24002 this.fireEvent("afterselectionchange", this);
24004 "down" : function(e){
24006 this.selectNext(e.shiftKey);
24007 }else if(this.last !== false && this.lastActive !== false){
24008 var last = this.last;
24009 this.selectRange(this.last, this.lastActive+1);
24010 this.grid.getView().focusRow(this.lastActive);
24011 if(last !== false){
24015 this.selectFirstRow();
24017 this.fireEvent("afterselectionchange", this);
24021 this.grid.store.on('load', function(){
24022 this.selections.clear();
24025 var view = this.grid.view;
24026 view.on("refresh", this.onRefresh, this);
24027 view.on("rowupdated", this.onRowUpdated, this);
24028 view.on("rowremoved", this.onRemove, this);
24033 onRefresh : function()
24035 var ds = this.grid.store, i, v = this.grid.view;
24036 var s = this.selections;
24037 s.each(function(r){
24038 if((i = ds.indexOfId(r.id)) != -1){
24047 onRemove : function(v, index, r){
24048 this.selections.remove(r);
24052 onRowUpdated : function(v, index, r){
24053 if(this.isSelected(r)){
24054 v.onRowSelect(index);
24060 * @param {Array} records The records to select
24061 * @param {Boolean} keepExisting (optional) True to keep existing selections
24063 selectRecords : function(records, keepExisting)
24066 this.clearSelections();
24068 var ds = this.grid.store;
24069 for(var i = 0, len = records.length; i < len; i++){
24070 this.selectRow(ds.indexOf(records[i]), true);
24075 * Gets the number of selected rows.
24078 getCount : function(){
24079 return this.selections.length;
24083 * Selects the first row in the grid.
24085 selectFirstRow : function(){
24090 * Select the last row.
24091 * @param {Boolean} keepExisting (optional) True to keep existing selections
24093 selectLastRow : function(keepExisting){
24094 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24095 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24099 * Selects the row immediately following the last selected row.
24100 * @param {Boolean} keepExisting (optional) True to keep existing selections
24102 selectNext : function(keepExisting)
24104 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24105 this.selectRow(this.last+1, keepExisting);
24106 this.grid.getView().focusRow(this.last);
24111 * Selects the row that precedes the last selected row.
24112 * @param {Boolean} keepExisting (optional) True to keep existing selections
24114 selectPrevious : function(keepExisting){
24116 this.selectRow(this.last-1, keepExisting);
24117 this.grid.getView().focusRow(this.last);
24122 * Returns the selected records
24123 * @return {Array} Array of selected records
24125 getSelections : function(){
24126 return [].concat(this.selections.items);
24130 * Returns the first selected record.
24133 getSelected : function(){
24134 return this.selections.itemAt(0);
24139 * Clears all selections.
24141 clearSelections : function(fast)
24147 var ds = this.grid.store;
24148 var s = this.selections;
24149 s.each(function(r){
24150 this.deselectRow(ds.indexOfId(r.id));
24154 this.selections.clear();
24161 * Selects all rows.
24163 selectAll : function(){
24167 this.selections.clear();
24168 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24169 this.selectRow(i, true);
24174 * Returns True if there is a selection.
24175 * @return {Boolean}
24177 hasSelection : function(){
24178 return this.selections.length > 0;
24182 * Returns True if the specified row is selected.
24183 * @param {Number/Record} record The record or index of the record to check
24184 * @return {Boolean}
24186 isSelected : function(index){
24187 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24188 return (r && this.selections.key(r.id) ? true : false);
24192 * Returns True if the specified record id is selected.
24193 * @param {String} id The id of record to check
24194 * @return {Boolean}
24196 isIdSelected : function(id){
24197 return (this.selections.key(id) ? true : false);
24202 handleMouseDBClick : function(e, t){
24206 handleMouseDown : function(e, t)
24208 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24209 if(this.isLocked() || rowIndex < 0 ){
24212 if(e.shiftKey && this.last !== false){
24213 var last = this.last;
24214 this.selectRange(last, rowIndex, e.ctrlKey);
24215 this.last = last; // reset the last
24219 var isSelected = this.isSelected(rowIndex);
24220 //Roo.log("select row:" + rowIndex);
24222 this.deselectRow(rowIndex);
24224 this.selectRow(rowIndex, true);
24228 if(e.button !== 0 && isSelected){
24229 alert('rowIndex 2: ' + rowIndex);
24230 view.focusRow(rowIndex);
24231 }else if(e.ctrlKey && isSelected){
24232 this.deselectRow(rowIndex);
24233 }else if(!isSelected){
24234 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24235 view.focusRow(rowIndex);
24239 this.fireEvent("afterselectionchange", this);
24242 handleDragableRowClick : function(grid, rowIndex, e)
24244 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24245 this.selectRow(rowIndex, false);
24246 grid.view.focusRow(rowIndex);
24247 this.fireEvent("afterselectionchange", this);
24252 * Selects multiple rows.
24253 * @param {Array} rows Array of the indexes of the row to select
24254 * @param {Boolean} keepExisting (optional) True to keep existing selections
24256 selectRows : function(rows, keepExisting){
24258 this.clearSelections();
24260 for(var i = 0, len = rows.length; i < len; i++){
24261 this.selectRow(rows[i], true);
24266 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24267 * @param {Number} startRow The index of the first row in the range
24268 * @param {Number} endRow The index of the last row in the range
24269 * @param {Boolean} keepExisting (optional) True to retain existing selections
24271 selectRange : function(startRow, endRow, keepExisting){
24276 this.clearSelections();
24278 if(startRow <= endRow){
24279 for(var i = startRow; i <= endRow; i++){
24280 this.selectRow(i, true);
24283 for(var i = startRow; i >= endRow; i--){
24284 this.selectRow(i, true);
24290 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24291 * @param {Number} startRow The index of the first row in the range
24292 * @param {Number} endRow The index of the last row in the range
24294 deselectRange : function(startRow, endRow, preventViewNotify){
24298 for(var i = startRow; i <= endRow; i++){
24299 this.deselectRow(i, preventViewNotify);
24305 * @param {Number} row The index of the row to select
24306 * @param {Boolean} keepExisting (optional) True to keep existing selections
24308 selectRow : function(index, keepExisting, preventViewNotify)
24310 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24313 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24314 if(!keepExisting || this.singleSelect){
24315 this.clearSelections();
24318 var r = this.grid.store.getAt(index);
24319 //console.log('selectRow - record id :' + r.id);
24321 this.selections.add(r);
24322 this.last = this.lastActive = index;
24323 if(!preventViewNotify){
24324 var proxy = new Roo.Element(
24325 this.grid.getRowDom(index)
24327 proxy.addClass('bg-info info');
24329 this.fireEvent("rowselect", this, index, r);
24330 this.fireEvent("selectionchange", this);
24336 * @param {Number} row The index of the row to deselect
24338 deselectRow : function(index, preventViewNotify)
24343 if(this.last == index){
24346 if(this.lastActive == index){
24347 this.lastActive = false;
24350 var r = this.grid.store.getAt(index);
24355 this.selections.remove(r);
24356 //.console.log('deselectRow - record id :' + r.id);
24357 if(!preventViewNotify){
24359 var proxy = new Roo.Element(
24360 this.grid.getRowDom(index)
24362 proxy.removeClass('bg-info info');
24364 this.fireEvent("rowdeselect", this, index);
24365 this.fireEvent("selectionchange", this);
24369 restoreLast : function(){
24371 this.last = this._last;
24376 acceptsNav : function(row, col, cm){
24377 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24381 onEditorKey : function(field, e){
24382 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24387 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24389 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24391 }else if(k == e.ENTER && !e.ctrlKey){
24395 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24397 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24399 }else if(k == e.ESC){
24403 g.startEditing(newCell[0], newCell[1]);
24409 * Ext JS Library 1.1.1
24410 * Copyright(c) 2006-2007, Ext JS, LLC.
24412 * Originally Released Under LGPL - original licence link has changed is not relivant.
24415 * <script type="text/javascript">
24419 * @class Roo.bootstrap.PagingToolbar
24420 * @extends Roo.bootstrap.NavSimplebar
24421 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24423 * Create a new PagingToolbar
24424 * @param {Object} config The config object
24425 * @param {Roo.data.Store} store
24427 Roo.bootstrap.PagingToolbar = function(config)
24429 // old args format still supported... - xtype is prefered..
24430 // created from xtype...
24432 this.ds = config.dataSource;
24434 if (config.store && !this.ds) {
24435 this.store= Roo.factory(config.store, Roo.data);
24436 this.ds = this.store;
24437 this.ds.xmodule = this.xmodule || false;
24440 this.toolbarItems = [];
24441 if (config.items) {
24442 this.toolbarItems = config.items;
24445 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24450 this.bind(this.ds);
24453 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24457 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24459 * @cfg {Roo.data.Store} dataSource
24460 * The underlying data store providing the paged data
24463 * @cfg {String/HTMLElement/Element} container
24464 * container The id or element that will contain the toolbar
24467 * @cfg {Boolean} displayInfo
24468 * True to display the displayMsg (defaults to false)
24471 * @cfg {Number} pageSize
24472 * The number of records to display per page (defaults to 20)
24476 * @cfg {String} displayMsg
24477 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24479 displayMsg : 'Displaying {0} - {1} of {2}',
24481 * @cfg {String} emptyMsg
24482 * The message to display when no records are found (defaults to "No data to display")
24484 emptyMsg : 'No data to display',
24486 * Customizable piece of the default paging text (defaults to "Page")
24489 beforePageText : "Page",
24491 * Customizable piece of the default paging text (defaults to "of %0")
24494 afterPageText : "of {0}",
24496 * Customizable piece of the default paging text (defaults to "First Page")
24499 firstText : "First Page",
24501 * Customizable piece of the default paging text (defaults to "Previous Page")
24504 prevText : "Previous Page",
24506 * Customizable piece of the default paging text (defaults to "Next Page")
24509 nextText : "Next Page",
24511 * Customizable piece of the default paging text (defaults to "Last Page")
24514 lastText : "Last Page",
24516 * Customizable piece of the default paging text (defaults to "Refresh")
24519 refreshText : "Refresh",
24523 onRender : function(ct, position)
24525 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24526 this.navgroup.parentId = this.id;
24527 this.navgroup.onRender(this.el, null);
24528 // add the buttons to the navgroup
24530 if(this.displayInfo){
24531 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24532 this.displayEl = this.el.select('.x-paging-info', true).first();
24533 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24534 // this.displayEl = navel.el.select('span',true).first();
24540 Roo.each(_this.buttons, function(e){ // this might need to use render????
24541 Roo.factory(e).render(_this.el);
24545 Roo.each(_this.toolbarItems, function(e) {
24546 _this.navgroup.addItem(e);
24550 this.first = this.navgroup.addItem({
24551 tooltip: this.firstText,
24553 icon : 'fa fa-backward',
24555 preventDefault: true,
24556 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24559 this.prev = this.navgroup.addItem({
24560 tooltip: this.prevText,
24562 icon : 'fa fa-step-backward',
24564 preventDefault: true,
24565 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24567 //this.addSeparator();
24570 var field = this.navgroup.addItem( {
24572 cls : 'x-paging-position',
24574 html : this.beforePageText +
24575 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24576 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24579 this.field = field.el.select('input', true).first();
24580 this.field.on("keydown", this.onPagingKeydown, this);
24581 this.field.on("focus", function(){this.dom.select();});
24584 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24585 //this.field.setHeight(18);
24586 //this.addSeparator();
24587 this.next = this.navgroup.addItem({
24588 tooltip: this.nextText,
24590 html : ' <i class="fa fa-step-forward">',
24592 preventDefault: true,
24593 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24595 this.last = this.navgroup.addItem({
24596 tooltip: this.lastText,
24597 icon : 'fa fa-forward',
24600 preventDefault: true,
24601 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24603 //this.addSeparator();
24604 this.loading = this.navgroup.addItem({
24605 tooltip: this.refreshText,
24606 icon: 'fa fa-refresh',
24607 preventDefault: true,
24608 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24614 updateInfo : function(){
24615 if(this.displayEl){
24616 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24617 var msg = count == 0 ?
24621 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24623 this.displayEl.update(msg);
24628 onLoad : function(ds, r, o)
24630 this.cursor = o.params.start ? o.params.start : 0;
24632 var d = this.getPageData(),
24637 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24638 this.field.dom.value = ap;
24639 this.first.setDisabled(ap == 1);
24640 this.prev.setDisabled(ap == 1);
24641 this.next.setDisabled(ap == ps);
24642 this.last.setDisabled(ap == ps);
24643 this.loading.enable();
24648 getPageData : function(){
24649 var total = this.ds.getTotalCount();
24652 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24653 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24658 onLoadError : function(){
24659 this.loading.enable();
24663 onPagingKeydown : function(e){
24664 var k = e.getKey();
24665 var d = this.getPageData();
24667 var v = this.field.dom.value, pageNum;
24668 if(!v || isNaN(pageNum = parseInt(v, 10))){
24669 this.field.dom.value = d.activePage;
24672 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24673 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24676 else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
24678 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24679 this.field.dom.value = pageNum;
24680 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24683 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24685 var v = this.field.dom.value, pageNum;
24686 var increment = (e.shiftKey) ? 10 : 1;
24687 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24690 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24691 this.field.dom.value = d.activePage;
24694 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24696 this.field.dom.value = parseInt(v, 10) + increment;
24697 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24698 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24705 beforeLoad : function(){
24707 this.loading.disable();
24712 onClick : function(which){
24721 ds.load({params:{start: 0, limit: this.pageSize}});
24724 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24727 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24730 var total = ds.getTotalCount();
24731 var extra = total % this.pageSize;
24732 var lastStart = extra ? (total - extra) : total-this.pageSize;
24733 ds.load({params:{start: lastStart, limit: this.pageSize}});
24736 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24742 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24743 * @param {Roo.data.Store} store The data store to unbind
24745 unbind : function(ds){
24746 ds.un("beforeload", this.beforeLoad, this);
24747 ds.un("load", this.onLoad, this);
24748 ds.un("loadexception", this.onLoadError, this);
24749 ds.un("remove", this.updateInfo, this);
24750 ds.un("add", this.updateInfo, this);
24751 this.ds = undefined;
24755 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24756 * @param {Roo.data.Store} store The data store to bind
24758 bind : function(ds){
24759 ds.on("beforeload", this.beforeLoad, this);
24760 ds.on("load", this.onLoad, this);
24761 ds.on("loadexception", this.onLoadError, this);
24762 ds.on("remove", this.updateInfo, this);
24763 ds.on("add", this.updateInfo, this);
24774 * @class Roo.bootstrap.MessageBar
24775 * @extends Roo.bootstrap.Component
24776 * Bootstrap MessageBar class
24777 * @cfg {String} html contents of the MessageBar
24778 * @cfg {String} weight (info | success | warning | danger) default info
24779 * @cfg {String} beforeClass insert the bar before the given class
24780 * @cfg {Boolean} closable (true | false) default false
24781 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24784 * Create a new Element
24785 * @param {Object} config The config object
24788 Roo.bootstrap.MessageBar = function(config){
24789 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24792 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24798 beforeClass: 'bootstrap-sticky-wrap',
24800 getAutoCreate : function(){
24804 cls: 'alert alert-dismissable alert-' + this.weight,
24809 html: this.html || ''
24815 cfg.cls += ' alert-messages-fixed';
24829 onRender : function(ct, position)
24831 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24834 var cfg = Roo.apply({}, this.getAutoCreate());
24838 cfg.cls += ' ' + this.cls;
24841 cfg.style = this.style;
24843 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24845 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24848 this.el.select('>button.close').on('click', this.hide, this);
24854 if (!this.rendered) {
24860 this.fireEvent('show', this);
24866 if (!this.rendered) {
24872 this.fireEvent('hide', this);
24875 update : function()
24877 // var e = this.el.dom.firstChild;
24879 // if(this.closable){
24880 // e = e.nextSibling;
24883 // e.data = this.html || '';
24885 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24901 * @class Roo.bootstrap.Graph
24902 * @extends Roo.bootstrap.Component
24903 * Bootstrap Graph class
24907 @cfg {String} graphtype bar | vbar | pie
24908 @cfg {number} g_x coodinator | centre x (pie)
24909 @cfg {number} g_y coodinator | centre y (pie)
24910 @cfg {number} g_r radius (pie)
24911 @cfg {number} g_height height of the chart (respected by all elements in the set)
24912 @cfg {number} g_width width of the chart (respected by all elements in the set)
24913 @cfg {Object} title The title of the chart
24916 -opts (object) options for the chart
24918 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24919 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24921 o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
24922 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24924 o stretch (boolean)
24926 -opts (object) options for the pie
24929 o startAngle (number)
24930 o endAngle (number)
24934 * Create a new Input
24935 * @param {Object} config The config object
24938 Roo.bootstrap.Graph = function(config){
24939 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24945 * The img click event for the img.
24946 * @param {Roo.EventObject} e
24952 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24963 //g_colors: this.colors,
24970 getAutoCreate : function(){
24981 onRender : function(ct,position){
24984 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24986 if (typeof(Raphael) == 'undefined') {
24987 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24991 this.raphael = Raphael(this.el.dom);
24993 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24994 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24995 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24996 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24998 r.text(160, 10, "Single Series Chart").attr(txtattr);
24999 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25000 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25001 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25003 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25004 r.barchart(330, 10, 300, 220, data1);
25005 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25006 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25009 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25010 // r.barchart(30, 30, 560, 250, xdata, {
25011 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25012 // axis : "0 0 1 1",
25013 // axisxlabels : xdata
25014 // //yvalues : cols,
25017 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25019 // this.load(null,xdata,{
25020 // axis : "0 0 1 1",
25021 // axisxlabels : xdata
25026 load : function(graphtype,xdata,opts)
25028 this.raphael.clear();
25030 graphtype = this.graphtype;
25035 var r = this.raphael,
25036 fin = function () {
25037 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25039 fout = function () {
25040 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25042 pfin = function() {
25043 this.sector.stop();
25044 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25047 this.label[0].stop();
25048 this.label[0].attr({ r: 7.5 });
25049 this.label[1].attr({ "font-weight": 800 });
25052 pfout = function() {
25053 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25056 this.label[0].animate({ r: 5 }, 500, "bounce");
25057 this.label[1].attr({ "font-weight": 400 });
25063 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25066 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25069 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25070 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25072 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25079 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25084 setTitle: function(o)
25089 initEvents: function() {
25092 this.el.on('click', this.onClick, this);
25096 onClick : function(e)
25098 Roo.log('img onclick');
25099 this.fireEvent('click', this, e);
25111 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25114 * @class Roo.bootstrap.dash.NumberBox
25115 * @extends Roo.bootstrap.Component
25116 * Bootstrap NumberBox class
25117 * @cfg {String} headline Box headline
25118 * @cfg {String} content Box content
25119 * @cfg {String} icon Box icon
25120 * @cfg {String} footer Footer text
25121 * @cfg {String} fhref Footer href
25124 * Create a new NumberBox
25125 * @param {Object} config The config object
25129 Roo.bootstrap.dash.NumberBox = function(config){
25130 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25134 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25143 getAutoCreate : function(){
25147 cls : 'small-box ',
25155 cls : 'roo-headline',
25156 html : this.headline
25160 cls : 'roo-content',
25161 html : this.content
25175 cls : 'ion ' + this.icon
25184 cls : 'small-box-footer',
25185 href : this.fhref || '#',
25189 cfg.cn.push(footer);
25196 onRender : function(ct,position){
25197 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25204 setHeadline: function (value)
25206 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25209 setFooter: function (value, href)
25211 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25214 this.el.select('a.small-box-footer',true).first().attr('href', href);
25219 setContent: function (value)
25221 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25224 initEvents: function()
25238 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25241 * @class Roo.bootstrap.dash.TabBox
25242 * @extends Roo.bootstrap.Component
25243 * Bootstrap TabBox class
25244 * @cfg {String} title Title of the TabBox
25245 * @cfg {String} icon Icon of the TabBox
25246 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25247 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25250 * Create a new TabBox
25251 * @param {Object} config The config object
25255 Roo.bootstrap.dash.TabBox = function(config){
25256 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25261 * When a pane is added
25262 * @param {Roo.bootstrap.dash.TabPane} pane
25266 * @event activatepane
25267 * When a pane is activated
25268 * @param {Roo.bootstrap.dash.TabPane} pane
25270 "activatepane" : true
25278 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25283 tabScrollable : false,
25285 getChildContainer : function()
25287 return this.el.select('.tab-content', true).first();
25290 getAutoCreate : function(){
25294 cls: 'pull-left header',
25302 cls: 'fa ' + this.icon
25308 cls: 'nav nav-tabs pull-right',
25314 if(this.tabScrollable){
25321 cls: 'nav nav-tabs pull-right',
25332 cls: 'nav-tabs-custom',
25337 cls: 'tab-content no-padding',
25345 initEvents : function()
25347 //Roo.log('add add pane handler');
25348 this.on('addpane', this.onAddPane, this);
25351 * Updates the box title
25352 * @param {String} html to set the title to.
25354 setTitle : function(value)
25356 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25358 onAddPane : function(pane)
25360 this.panes.push(pane);
25361 //Roo.log('addpane');
25363 // tabs are rendere left to right..
25364 if(!this.showtabs){
25368 var ctr = this.el.select('.nav-tabs', true).first();
25371 var existing = ctr.select('.nav-tab',true);
25372 var qty = existing.getCount();;
25375 var tab = ctr.createChild({
25377 cls : 'nav-tab' + (qty ? '' : ' active'),
25385 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25388 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25390 pane.el.addClass('active');
25395 onTabClick : function(ev,un,ob,pane)
25397 //Roo.log('tab - prev default');
25398 ev.preventDefault();
25401 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25402 pane.tab.addClass('active');
25403 //Roo.log(pane.title);
25404 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25405 // technically we should have a deactivate event.. but maybe add later.
25406 // and it should not de-activate the selected tab...
25407 this.fireEvent('activatepane', pane);
25408 pane.el.addClass('active');
25409 pane.fireEvent('activate');
25414 getActivePane : function()
25417 Roo.each(this.panes, function(p) {
25418 if(p.el.hasClass('active')){
25439 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25441 * @class Roo.bootstrap.TabPane
25442 * @extends Roo.bootstrap.Component
25443 * Bootstrap TabPane class
25444 * @cfg {Boolean} active (false | true) Default false
25445 * @cfg {String} title title of panel
25449 * Create a new TabPane
25450 * @param {Object} config The config object
25453 Roo.bootstrap.dash.TabPane = function(config){
25454 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25460 * When a pane is activated
25461 * @param {Roo.bootstrap.dash.TabPane} pane
25468 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25473 // the tabBox that this is attached to.
25476 getAutoCreate : function()
25484 cfg.cls += ' active';
25489 initEvents : function()
25491 //Roo.log('trigger add pane handler');
25492 this.parent().fireEvent('addpane', this)
25496 * Updates the tab title
25497 * @param {String} html to set the title to.
25499 setTitle: function(str)
25505 this.tab.select('a', true).first().dom.innerHTML = str;
25522 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25525 * @class Roo.bootstrap.menu.Menu
25526 * @extends Roo.bootstrap.Component
25527 * Bootstrap Menu class - container for Menu
25528 * @cfg {String} html Text of the menu
25529 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25530 * @cfg {String} icon Font awesome icon
25531 * @cfg {String} pos Menu align to (top | bottom) default bottom
25535 * Create a new Menu
25536 * @param {Object} config The config object
25540 Roo.bootstrap.menu.Menu = function(config){
25541 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25545 * @event beforeshow
25546 * Fires before this menu is displayed
25547 * @param {Roo.bootstrap.menu.Menu} this
25551 * @event beforehide
25552 * Fires before this menu is hidden
25553 * @param {Roo.bootstrap.menu.Menu} this
25558 * Fires after this menu is displayed
25559 * @param {Roo.bootstrap.menu.Menu} this
25564 * Fires after this menu is hidden
25565 * @param {Roo.bootstrap.menu.Menu} this
25570 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25571 * @param {Roo.bootstrap.menu.Menu} this
25572 * @param {Roo.EventObject} e
25579 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25583 weight : 'default',
25588 getChildContainer : function() {
25589 if(this.isSubMenu){
25593 return this.el.select('ul.dropdown-menu', true).first();
25596 getAutoCreate : function()
25601 cls : 'roo-menu-text',
25609 cls : 'fa ' + this.icon
25620 cls : 'dropdown-button btn btn-' + this.weight,
25625 cls : 'dropdown-toggle btn btn-' + this.weight,
25635 cls : 'dropdown-menu'
25641 if(this.pos == 'top'){
25642 cfg.cls += ' dropup';
25645 if(this.isSubMenu){
25648 cls : 'dropdown-menu'
25655 onRender : function(ct, position)
25657 this.isSubMenu = ct.hasClass('dropdown-submenu');
25659 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25662 initEvents : function()
25664 if(this.isSubMenu){
25668 this.hidden = true;
25670 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25671 this.triggerEl.on('click', this.onTriggerPress, this);
25673 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25674 this.buttonEl.on('click', this.onClick, this);
25680 if(this.isSubMenu){
25684 return this.el.select('ul.dropdown-menu', true).first();
25687 onClick : function(e)
25689 this.fireEvent("click", this, e);
25692 onTriggerPress : function(e)
25694 if (this.isVisible()) {
25701 isVisible : function(){
25702 return !this.hidden;
25707 this.fireEvent("beforeshow", this);
25709 this.hidden = false;
25710 this.el.addClass('open');
25712 Roo.get(document).on("mouseup", this.onMouseUp, this);
25714 this.fireEvent("show", this);
25721 this.fireEvent("beforehide", this);
25723 this.hidden = true;
25724 this.el.removeClass('open');
25726 Roo.get(document).un("mouseup", this.onMouseUp);
25728 this.fireEvent("hide", this);
25731 onMouseUp : function()
25745 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25748 * @class Roo.bootstrap.menu.Item
25749 * @extends Roo.bootstrap.Component
25750 * Bootstrap MenuItem class
25751 * @cfg {Boolean} submenu (true | false) default false
25752 * @cfg {String} html text of the item
25753 * @cfg {String} href the link
25754 * @cfg {Boolean} disable (true | false) default false
25755 * @cfg {Boolean} preventDefault (true | false) default true
25756 * @cfg {String} icon Font awesome icon
25757 * @cfg {String} pos Submenu align to (left | right) default right
25761 * Create a new Item
25762 * @param {Object} config The config object
25766 Roo.bootstrap.menu.Item = function(config){
25767 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25771 * Fires when the mouse is hovering over this menu
25772 * @param {Roo.bootstrap.menu.Item} this
25773 * @param {Roo.EventObject} e
25778 * Fires when the mouse exits this menu
25779 * @param {Roo.bootstrap.menu.Item} this
25780 * @param {Roo.EventObject} e
25786 * The raw click event for the entire grid.
25787 * @param {Roo.EventObject} e
25793 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25798 preventDefault: true,
25803 getAutoCreate : function()
25808 cls : 'roo-menu-item-text',
25816 cls : 'fa ' + this.icon
25825 href : this.href || '#',
25832 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25836 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25838 if(this.pos == 'left'){
25839 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25846 initEvents : function()
25848 this.el.on('mouseover', this.onMouseOver, this);
25849 this.el.on('mouseout', this.onMouseOut, this);
25851 this.el.select('a', true).first().on('click', this.onClick, this);
25855 onClick : function(e)
25857 if(this.preventDefault){
25858 e.preventDefault();
25861 this.fireEvent("click", this, e);
25864 onMouseOver : function(e)
25866 if(this.submenu && this.pos == 'left'){
25867 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25870 this.fireEvent("mouseover", this, e);
25873 onMouseOut : function(e)
25875 this.fireEvent("mouseout", this, e);
25887 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25890 * @class Roo.bootstrap.menu.Separator
25891 * @extends Roo.bootstrap.Component
25892 * Bootstrap Separator class
25895 * Create a new Separator
25896 * @param {Object} config The config object
25900 Roo.bootstrap.menu.Separator = function(config){
25901 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25904 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25906 getAutoCreate : function(){
25927 * @class Roo.bootstrap.Tooltip
25928 * Bootstrap Tooltip class
25929 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25930 * to determine which dom element triggers the tooltip.
25932 * It needs to add support for additional attributes like tooltip-position
25935 * Create a new Toolti
25936 * @param {Object} config The config object
25939 Roo.bootstrap.Tooltip = function(config){
25940 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25942 this.alignment = Roo.bootstrap.Tooltip.alignment;
25944 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25945 this.alignment = config.alignment;
25950 Roo.apply(Roo.bootstrap.Tooltip, {
25952 * @function init initialize tooltip monitoring.
25956 currentTip : false,
25957 currentRegion : false,
25963 Roo.get(document).on('mouseover', this.enter ,this);
25964 Roo.get(document).on('mouseout', this.leave, this);
25967 this.currentTip = new Roo.bootstrap.Tooltip();
25970 enter : function(ev)
25972 var dom = ev.getTarget();
25974 //Roo.log(['enter',dom]);
25975 var el = Roo.fly(dom);
25976 if (this.currentEl) {
25978 //Roo.log(this.currentEl);
25979 //Roo.log(this.currentEl.contains(dom));
25980 if (this.currentEl == el) {
25983 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25989 if (this.currentTip.el) {
25990 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25994 if(!el || el.dom == document){
26000 // you can not look for children, as if el is the body.. then everythign is the child..
26001 if (!el.attr('tooltip')) { //
26002 if (!el.select("[tooltip]").elements.length) {
26005 // is the mouse over this child...?
26006 bindEl = el.select("[tooltip]").first();
26007 var xy = ev.getXY();
26008 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26009 //Roo.log("not in region.");
26012 //Roo.log("child element over..");
26015 this.currentEl = bindEl;
26016 this.currentTip.bind(bindEl);
26017 this.currentRegion = Roo.lib.Region.getRegion(dom);
26018 this.currentTip.enter();
26021 leave : function(ev)
26023 var dom = ev.getTarget();
26024 //Roo.log(['leave',dom]);
26025 if (!this.currentEl) {
26030 if (dom != this.currentEl.dom) {
26033 var xy = ev.getXY();
26034 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26037 // only activate leave if mouse cursor is outside... bounding box..
26042 if (this.currentTip) {
26043 this.currentTip.leave();
26045 //Roo.log('clear currentEl');
26046 this.currentEl = false;
26051 'left' : ['r-l', [-2,0], 'right'],
26052 'right' : ['l-r', [2,0], 'left'],
26053 'bottom' : ['t-b', [0,2], 'top'],
26054 'top' : [ 'b-t', [0,-2], 'bottom']
26060 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26065 delay : null, // can be { show : 300 , hide: 500}
26069 hoverState : null, //???
26071 placement : 'bottom',
26075 getAutoCreate : function(){
26082 cls : 'tooltip-arrow'
26085 cls : 'tooltip-inner'
26092 bind : function(el)
26098 enter : function () {
26100 if (this.timeout != null) {
26101 clearTimeout(this.timeout);
26104 this.hoverState = 'in';
26105 //Roo.log("enter - show");
26106 if (!this.delay || !this.delay.show) {
26111 this.timeout = setTimeout(function () {
26112 if (_t.hoverState == 'in') {
26115 }, this.delay.show);
26119 clearTimeout(this.timeout);
26121 this.hoverState = 'out';
26122 if (!this.delay || !this.delay.hide) {
26128 this.timeout = setTimeout(function () {
26129 //Roo.log("leave - timeout");
26131 if (_t.hoverState == 'out') {
26133 Roo.bootstrap.Tooltip.currentEl = false;
26138 show : function (msg)
26141 this.render(document.body);
26144 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26146 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26148 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26150 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26152 var placement = typeof this.placement == 'function' ?
26153 this.placement.call(this, this.el, on_el) :
26156 var autoToken = /\s?auto?\s?/i;
26157 var autoPlace = autoToken.test(placement);
26159 placement = placement.replace(autoToken, '') || 'top';
26163 //this.el.setXY([0,0]);
26165 //this.el.dom.style.display='block';
26167 //this.el.appendTo(on_el);
26169 var p = this.getPosition();
26170 var box = this.el.getBox();
26176 var align = this.alignment[placement];
26178 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26180 if(placement == 'top' || placement == 'bottom'){
26182 placement = 'right';
26185 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26186 placement = 'left';
26189 var scroll = Roo.select('body', true).first().getScroll();
26191 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26197 this.el.alignTo(this.bindEl, align[0],align[1]);
26198 //var arrow = this.el.select('.arrow',true).first();
26199 //arrow.set(align[2],
26201 this.el.addClass(placement);
26203 this.el.addClass('in fade');
26205 this.hoverState = null;
26207 if (this.el.hasClass('fade')) {
26218 //this.el.setXY([0,0]);
26219 this.el.removeClass('in');
26235 * @class Roo.bootstrap.LocationPicker
26236 * @extends Roo.bootstrap.Component
26237 * Bootstrap LocationPicker class
26238 * @cfg {Number} latitude Position when init default 0
26239 * @cfg {Number} longitude Position when init default 0
26240 * @cfg {Number} zoom default 15
26241 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26242 * @cfg {Boolean} mapTypeControl default false
26243 * @cfg {Boolean} disableDoubleClickZoom default false
26244 * @cfg {Boolean} scrollwheel default true
26245 * @cfg {Boolean} streetViewControl default false
26246 * @cfg {Number} radius default 0
26247 * @cfg {String} locationName
26248 * @cfg {Boolean} draggable default true
26249 * @cfg {Boolean} enableAutocomplete default false
26250 * @cfg {Boolean} enableReverseGeocode default true
26251 * @cfg {String} markerTitle
26254 * Create a new LocationPicker
26255 * @param {Object} config The config object
26259 Roo.bootstrap.LocationPicker = function(config){
26261 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26266 * Fires when the picker initialized.
26267 * @param {Roo.bootstrap.LocationPicker} this
26268 * @param {Google Location} location
26272 * @event positionchanged
26273 * Fires when the picker position changed.
26274 * @param {Roo.bootstrap.LocationPicker} this
26275 * @param {Google Location} location
26277 positionchanged : true,
26280 * Fires when the map resize.
26281 * @param {Roo.bootstrap.LocationPicker} this
26286 * Fires when the map show.
26287 * @param {Roo.bootstrap.LocationPicker} this
26292 * Fires when the map hide.
26293 * @param {Roo.bootstrap.LocationPicker} this
26298 * Fires when click the map.
26299 * @param {Roo.bootstrap.LocationPicker} this
26300 * @param {Map event} e
26304 * @event mapRightClick
26305 * Fires when right click the map.
26306 * @param {Roo.bootstrap.LocationPicker} this
26307 * @param {Map event} e
26309 mapRightClick : true,
26311 * @event markerClick
26312 * Fires when click the marker.
26313 * @param {Roo.bootstrap.LocationPicker} this
26314 * @param {Map event} e
26316 markerClick : true,
26318 * @event markerRightClick
26319 * Fires when right click the marker.
26320 * @param {Roo.bootstrap.LocationPicker} this
26321 * @param {Map event} e
26323 markerRightClick : true,
26325 * @event OverlayViewDraw
26326 * Fires when OverlayView Draw
26327 * @param {Roo.bootstrap.LocationPicker} this
26329 OverlayViewDraw : true,
26331 * @event OverlayViewOnAdd
26332 * Fires when OverlayView Draw
26333 * @param {Roo.bootstrap.LocationPicker} this
26335 OverlayViewOnAdd : true,
26337 * @event OverlayViewOnRemove
26338 * Fires when OverlayView Draw
26339 * @param {Roo.bootstrap.LocationPicker} this
26341 OverlayViewOnRemove : true,
26343 * @event OverlayViewShow
26344 * Fires when OverlayView Draw
26345 * @param {Roo.bootstrap.LocationPicker} this
26346 * @param {Pixel} cpx
26348 OverlayViewShow : true,
26350 * @event OverlayViewHide
26351 * Fires when OverlayView Draw
26352 * @param {Roo.bootstrap.LocationPicker} this
26354 OverlayViewHide : true,
26356 * @event loadexception
26357 * Fires when load google lib failed.
26358 * @param {Roo.bootstrap.LocationPicker} this
26360 loadexception : true
26365 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26367 gMapContext: false,
26373 mapTypeControl: false,
26374 disableDoubleClickZoom: false,
26376 streetViewControl: false,
26380 enableAutocomplete: false,
26381 enableReverseGeocode: true,
26384 getAutoCreate: function()
26389 cls: 'roo-location-picker'
26395 initEvents: function(ct, position)
26397 if(!this.el.getWidth() || this.isApplied()){
26401 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26406 initial: function()
26408 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26409 this.fireEvent('loadexception', this);
26413 if(!this.mapTypeId){
26414 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26417 this.gMapContext = this.GMapContext();
26419 this.initOverlayView();
26421 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26425 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26426 _this.setPosition(_this.gMapContext.marker.position);
26429 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26430 _this.fireEvent('mapClick', this, event);
26434 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26435 _this.fireEvent('mapRightClick', this, event);
26439 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26440 _this.fireEvent('markerClick', this, event);
26444 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26445 _this.fireEvent('markerRightClick', this, event);
26449 this.setPosition(this.gMapContext.location);
26451 this.fireEvent('initial', this, this.gMapContext.location);
26454 initOverlayView: function()
26458 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26462 _this.fireEvent('OverlayViewDraw', _this);
26467 _this.fireEvent('OverlayViewOnAdd', _this);
26470 onRemove: function()
26472 _this.fireEvent('OverlayViewOnRemove', _this);
26475 show: function(cpx)
26477 _this.fireEvent('OverlayViewShow', _this, cpx);
26482 _this.fireEvent('OverlayViewHide', _this);
26488 fromLatLngToContainerPixel: function(event)
26490 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26493 isApplied: function()
26495 return this.getGmapContext() == false ? false : true;
26498 getGmapContext: function()
26500 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26503 GMapContext: function()
26505 var position = new google.maps.LatLng(this.latitude, this.longitude);
26507 var _map = new google.maps.Map(this.el.dom, {
26510 mapTypeId: this.mapTypeId,
26511 mapTypeControl: this.mapTypeControl,
26512 disableDoubleClickZoom: this.disableDoubleClickZoom,
26513 scrollwheel: this.scrollwheel,
26514 streetViewControl: this.streetViewControl,
26515 locationName: this.locationName,
26516 draggable: this.draggable,
26517 enableAutocomplete: this.enableAutocomplete,
26518 enableReverseGeocode: this.enableReverseGeocode
26521 var _marker = new google.maps.Marker({
26522 position: position,
26524 title: this.markerTitle,
26525 draggable: this.draggable
26532 location: position,
26533 radius: this.radius,
26534 locationName: this.locationName,
26535 addressComponents: {
26536 formatted_address: null,
26537 addressLine1: null,
26538 addressLine2: null,
26540 streetNumber: null,
26544 stateOrProvince: null
26547 domContainer: this.el.dom,
26548 geodecoder: new google.maps.Geocoder()
26552 drawCircle: function(center, radius, options)
26554 if (this.gMapContext.circle != null) {
26555 this.gMapContext.circle.setMap(null);
26559 options = Roo.apply({}, options, {
26560 strokeColor: "#0000FF",
26561 strokeOpacity: .35,
26563 fillColor: "#0000FF",
26567 options.map = this.gMapContext.map;
26568 options.radius = radius;
26569 options.center = center;
26570 this.gMapContext.circle = new google.maps.Circle(options);
26571 return this.gMapContext.circle;
26577 setPosition: function(location)
26579 this.gMapContext.location = location;
26580 this.gMapContext.marker.setPosition(location);
26581 this.gMapContext.map.panTo(location);
26582 this.drawCircle(location, this.gMapContext.radius, {});
26586 if (this.gMapContext.settings.enableReverseGeocode) {
26587 this.gMapContext.geodecoder.geocode({
26588 latLng: this.gMapContext.location
26589 }, function(results, status) {
26591 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26592 _this.gMapContext.locationName = results[0].formatted_address;
26593 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26595 _this.fireEvent('positionchanged', this, location);
26602 this.fireEvent('positionchanged', this, location);
26607 google.maps.event.trigger(this.gMapContext.map, "resize");
26609 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26611 this.fireEvent('resize', this);
26614 setPositionByLatLng: function(latitude, longitude)
26616 this.setPosition(new google.maps.LatLng(latitude, longitude));
26619 getCurrentPosition: function()
26622 latitude: this.gMapContext.location.lat(),
26623 longitude: this.gMapContext.location.lng()
26627 getAddressName: function()
26629 return this.gMapContext.locationName;
26632 getAddressComponents: function()
26634 return this.gMapContext.addressComponents;
26637 address_component_from_google_geocode: function(address_components)
26641 for (var i = 0; i < address_components.length; i++) {
26642 var component = address_components[i];
26643 if (component.types.indexOf("postal_code") >= 0) {
26644 result.postalCode = component.short_name;
26645 } else if (component.types.indexOf("street_number") >= 0) {
26646 result.streetNumber = component.short_name;
26647 } else if (component.types.indexOf("route") >= 0) {
26648 result.streetName = component.short_name;
26649 } else if (component.types.indexOf("neighborhood") >= 0) {
26650 result.city = component.short_name;
26651 } else if (component.types.indexOf("locality") >= 0) {
26652 result.city = component.short_name;
26653 } else if (component.types.indexOf("sublocality") >= 0) {
26654 result.district = component.short_name;
26655 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26656 result.stateOrProvince = component.short_name;
26657 } else if (component.types.indexOf("country") >= 0) {
26658 result.country = component.short_name;
26662 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26663 result.addressLine2 = "";
26667 setZoomLevel: function(zoom)
26669 this.gMapContext.map.setZoom(zoom);
26682 this.fireEvent('show', this);
26693 this.fireEvent('hide', this);
26698 Roo.apply(Roo.bootstrap.LocationPicker, {
26700 OverlayView : function(map, options)
26702 options = options || {};
26716 * @class Roo.bootstrap.Alert
26717 * @extends Roo.bootstrap.Component
26718 * Bootstrap Alert class
26719 * @cfg {String} title The title of alert
26720 * @cfg {String} html The content of alert
26721 * @cfg {String} weight ( success | info | warning | danger )
26722 * @cfg {String} faicon font-awesomeicon
26725 * Create a new alert
26726 * @param {Object} config The config object
26730 Roo.bootstrap.Alert = function(config){
26731 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26735 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26742 getAutoCreate : function()
26751 cls : 'roo-alert-icon'
26756 cls : 'roo-alert-title',
26761 cls : 'roo-alert-text',
26768 cfg.cn[0].cls += ' fa ' + this.faicon;
26772 cfg.cls += ' alert-' + this.weight;
26778 initEvents: function()
26780 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26783 setTitle : function(str)
26785 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26788 setText : function(str)
26790 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26793 setWeight : function(weight)
26796 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26799 this.weight = weight;
26801 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26804 setIcon : function(icon)
26807 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26810 this.faicon = icon;
26812 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26833 * @class Roo.bootstrap.UploadCropbox
26834 * @extends Roo.bootstrap.Component
26835 * Bootstrap UploadCropbox class
26836 * @cfg {String} emptyText show when image has been loaded
26837 * @cfg {String} rotateNotify show when image too small to rotate
26838 * @cfg {Number} errorTimeout default 3000
26839 * @cfg {Number} minWidth default 300
26840 * @cfg {Number} minHeight default 300
26841 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26842 * @cfg {Boolean} isDocument (true|false) default false
26843 * @cfg {String} url action url
26844 * @cfg {String} paramName default 'imageUpload'
26845 * @cfg {String} method default POST
26846 * @cfg {Boolean} loadMask (true|false) default true
26847 * @cfg {Boolean} loadingText default 'Loading...'
26850 * Create a new UploadCropbox
26851 * @param {Object} config The config object
26854 Roo.bootstrap.UploadCropbox = function(config){
26855 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26859 * @event beforeselectfile
26860 * Fire before select file
26861 * @param {Roo.bootstrap.UploadCropbox} this
26863 "beforeselectfile" : true,
26866 * Fire after initEvent
26867 * @param {Roo.bootstrap.UploadCropbox} this
26872 * Fire after initEvent
26873 * @param {Roo.bootstrap.UploadCropbox} this
26874 * @param {String} data
26879 * Fire when preparing the file data
26880 * @param {Roo.bootstrap.UploadCropbox} this
26881 * @param {Object} file
26886 * Fire when get exception
26887 * @param {Roo.bootstrap.UploadCropbox} this
26888 * @param {XMLHttpRequest} xhr
26890 "exception" : true,
26892 * @event beforeloadcanvas
26893 * Fire before load the canvas
26894 * @param {Roo.bootstrap.UploadCropbox} this
26895 * @param {String} src
26897 "beforeloadcanvas" : true,
26900 * Fire when trash image
26901 * @param {Roo.bootstrap.UploadCropbox} this
26906 * Fire when download the image
26907 * @param {Roo.bootstrap.UploadCropbox} this
26911 * @event footerbuttonclick
26912 * Fire when footerbuttonclick
26913 * @param {Roo.bootstrap.UploadCropbox} this
26914 * @param {String} type
26916 "footerbuttonclick" : true,
26920 * @param {Roo.bootstrap.UploadCropbox} this
26925 * Fire when rotate the image
26926 * @param {Roo.bootstrap.UploadCropbox} this
26927 * @param {String} pos
26932 * Fire when inspect the file
26933 * @param {Roo.bootstrap.UploadCropbox} this
26934 * @param {Object} file
26939 * Fire when xhr upload the file
26940 * @param {Roo.bootstrap.UploadCropbox} this
26941 * @param {Object} data
26946 * Fire when arrange the file data
26947 * @param {Roo.bootstrap.UploadCropbox} this
26948 * @param {Object} formData
26953 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26956 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26958 emptyText : 'Click to upload image',
26959 rotateNotify : 'Image is too small to rotate',
26960 errorTimeout : 3000,
26974 cropType : 'image/jpeg',
26976 canvasLoaded : false,
26977 isDocument : false,
26979 paramName : 'imageUpload',
26981 loadingText : 'Loading...',
26984 getAutoCreate : function()
26988 cls : 'roo-upload-cropbox',
26992 cls : 'roo-upload-cropbox-selector',
26997 cls : 'roo-upload-cropbox-body',
26998 style : 'cursor:pointer',
27002 cls : 'roo-upload-cropbox-preview'
27006 cls : 'roo-upload-cropbox-thumb'
27010 cls : 'roo-upload-cropbox-empty-notify',
27011 html : this.emptyText
27015 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27016 html : this.rotateNotify
27022 cls : 'roo-upload-cropbox-footer',
27025 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27035 onRender : function(ct, position)
27037 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27039 if (this.buttons.length) {
27041 Roo.each(this.buttons, function(bb) {
27043 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27045 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27051 this.maskEl = this.el;
27055 initEvents : function()
27057 this.urlAPI = (window.createObjectURL && window) ||
27058 (window.URL && URL.revokeObjectURL && URL) ||
27059 (window.webkitURL && webkitURL);
27061 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27062 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27064 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27065 this.selectorEl.hide();
27067 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27068 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27070 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27071 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27072 this.thumbEl.hide();
27074 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27075 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27077 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27078 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27079 this.errorEl.hide();
27081 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27082 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27083 this.footerEl.hide();
27085 this.setThumbBoxSize();
27091 this.fireEvent('initial', this);
27098 window.addEventListener("resize", function() { _this.resize(); } );
27100 this.bodyEl.on('click', this.beforeSelectFile, this);
27103 this.bodyEl.on('touchstart', this.onTouchStart, this);
27104 this.bodyEl.on('touchmove', this.onTouchMove, this);
27105 this.bodyEl.on('touchend', this.onTouchEnd, this);
27109 this.bodyEl.on('mousedown', this.onMouseDown, this);
27110 this.bodyEl.on('mousemove', this.onMouseMove, this);
27111 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27112 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27113 Roo.get(document).on('mouseup', this.onMouseUp, this);
27116 this.selectorEl.on('change', this.onFileSelected, this);
27122 this.baseScale = 1;
27124 this.baseRotate = 1;
27125 this.dragable = false;
27126 this.pinching = false;
27129 this.cropData = false;
27130 this.notifyEl.dom.innerHTML = this.emptyText;
27132 this.selectorEl.dom.value = '';
27136 resize : function()
27138 if(this.fireEvent('resize', this) != false){
27139 this.setThumbBoxPosition();
27140 this.setCanvasPosition();
27144 onFooterButtonClick : function(e, el, o, type)
27147 case 'rotate-left' :
27148 this.onRotateLeft(e);
27150 case 'rotate-right' :
27151 this.onRotateRight(e);
27154 this.beforeSelectFile(e);
27169 this.fireEvent('footerbuttonclick', this, type);
27172 beforeSelectFile : function(e)
27174 e.preventDefault();
27176 if(this.fireEvent('beforeselectfile', this) != false){
27177 this.selectorEl.dom.click();
27181 onFileSelected : function(e)
27183 e.preventDefault();
27185 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27189 var file = this.selectorEl.dom.files[0];
27191 if(this.fireEvent('inspect', this, file) != false){
27192 this.prepare(file);
27197 trash : function(e)
27199 this.fireEvent('trash', this);
27202 download : function(e)
27204 this.fireEvent('download', this);
27207 loadCanvas : function(src)
27209 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27213 this.imageEl = document.createElement('img');
27217 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27219 this.imageEl.src = src;
27223 onLoadCanvas : function()
27225 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27226 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27228 this.bodyEl.un('click', this.beforeSelectFile, this);
27230 this.notifyEl.hide();
27231 this.thumbEl.show();
27232 this.footerEl.show();
27234 this.baseRotateLevel();
27236 if(this.isDocument){
27237 this.setThumbBoxSize();
27240 this.setThumbBoxPosition();
27242 this.baseScaleLevel();
27248 this.canvasLoaded = true;
27251 this.maskEl.unmask();
27256 setCanvasPosition : function()
27258 if(!this.canvasEl){
27262 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27263 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27265 this.previewEl.setLeft(pw);
27266 this.previewEl.setTop(ph);
27270 onMouseDown : function(e)
27274 this.dragable = true;
27275 this.pinching = false;
27277 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27278 this.dragable = false;
27282 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27283 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27287 onMouseMove : function(e)
27291 if(!this.canvasLoaded){
27295 if (!this.dragable){
27299 var minX = Math.ceil(this.thumbEl.getLeft(true));
27300 var minY = Math.ceil(this.thumbEl.getTop(true));
27302 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27303 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27305 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27306 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27308 x = x - this.mouseX;
27309 y = y - this.mouseY;
27311 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27312 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27314 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27315 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27317 this.previewEl.setLeft(bgX);
27318 this.previewEl.setTop(bgY);
27320 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27321 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27324 onMouseUp : function(e)
27328 this.dragable = false;
27331 onMouseWheel : function(e)
27335 this.startScale = this.scale;
27337 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27339 if(!this.zoomable()){
27340 this.scale = this.startScale;
27349 zoomable : function()
27351 var minScale = this.thumbEl.getWidth() / this.minWidth;
27353 if(this.minWidth < this.minHeight){
27354 minScale = this.thumbEl.getHeight() / this.minHeight;
27357 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27358 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27362 (this.rotate == 0 || this.rotate == 180) &&
27364 width > this.imageEl.OriginWidth ||
27365 height > this.imageEl.OriginHeight ||
27366 (width < this.minWidth && height < this.minHeight)
27374 (this.rotate == 90 || this.rotate == 270) &&
27376 width > this.imageEl.OriginWidth ||
27377 height > this.imageEl.OriginHeight ||
27378 (width < this.minHeight && height < this.minWidth)
27385 !this.isDocument &&
27386 (this.rotate == 0 || this.rotate == 180) &&
27388 width < this.minWidth ||
27389 width > this.imageEl.OriginWidth ||
27390 height < this.minHeight ||
27391 height > this.imageEl.OriginHeight
27398 !this.isDocument &&
27399 (this.rotate == 90 || this.rotate == 270) &&
27401 width < this.minHeight ||
27402 width > this.imageEl.OriginWidth ||
27403 height < this.minWidth ||
27404 height > this.imageEl.OriginHeight
27414 onRotateLeft : function(e)
27416 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27418 var minScale = this.thumbEl.getWidth() / this.minWidth;
27420 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27421 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27423 this.startScale = this.scale;
27425 while (this.getScaleLevel() < minScale){
27427 this.scale = this.scale + 1;
27429 if(!this.zoomable()){
27434 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27435 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27440 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27447 this.scale = this.startScale;
27449 this.onRotateFail();
27454 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27456 if(this.isDocument){
27457 this.setThumbBoxSize();
27458 this.setThumbBoxPosition();
27459 this.setCanvasPosition();
27464 this.fireEvent('rotate', this, 'left');
27468 onRotateRight : function(e)
27470 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27472 var minScale = this.thumbEl.getWidth() / this.minWidth;
27474 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27475 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27477 this.startScale = this.scale;
27479 while (this.getScaleLevel() < minScale){
27481 this.scale = this.scale + 1;
27483 if(!this.zoomable()){
27488 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27489 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27494 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27501 this.scale = this.startScale;
27503 this.onRotateFail();
27508 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27510 if(this.isDocument){
27511 this.setThumbBoxSize();
27512 this.setThumbBoxPosition();
27513 this.setCanvasPosition();
27518 this.fireEvent('rotate', this, 'right');
27521 onRotateFail : function()
27523 this.errorEl.show(true);
27527 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27532 this.previewEl.dom.innerHTML = '';
27534 var canvasEl = document.createElement("canvas");
27536 var contextEl = canvasEl.getContext("2d");
27538 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27539 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27540 var center = this.imageEl.OriginWidth / 2;
27542 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27543 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27544 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27545 center = this.imageEl.OriginHeight / 2;
27548 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27550 contextEl.translate(center, center);
27551 contextEl.rotate(this.rotate * Math.PI / 180);
27553 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27555 this.canvasEl = document.createElement("canvas");
27557 this.contextEl = this.canvasEl.getContext("2d");
27559 switch (this.rotate) {
27562 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27563 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27565 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27570 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27571 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27573 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27574 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27578 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27583 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27584 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27586 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27587 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27591 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27596 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27597 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27599 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27600 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27604 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27611 this.previewEl.appendChild(this.canvasEl);
27613 this.setCanvasPosition();
27618 if(!this.canvasLoaded){
27622 var imageCanvas = document.createElement("canvas");
27624 var imageContext = imageCanvas.getContext("2d");
27626 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27627 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27629 var center = imageCanvas.width / 2;
27631 imageContext.translate(center, center);
27633 imageContext.rotate(this.rotate * Math.PI / 180);
27635 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27637 var canvas = document.createElement("canvas");
27639 var context = canvas.getContext("2d");
27641 canvas.width = this.minWidth;
27642 canvas.height = this.minHeight;
27644 switch (this.rotate) {
27647 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27648 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27650 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27651 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27653 var targetWidth = this.minWidth - 2 * x;
27654 var targetHeight = this.minHeight - 2 * y;
27658 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27659 scale = targetWidth / width;
27662 if(x > 0 && y == 0){
27663 scale = targetHeight / height;
27666 if(x > 0 && y > 0){
27667 scale = targetWidth / width;
27669 if(width < height){
27670 scale = targetHeight / height;
27674 context.scale(scale, scale);
27676 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27677 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27679 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27680 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27682 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27687 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27688 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27690 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27691 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27693 var targetWidth = this.minWidth - 2 * x;
27694 var targetHeight = this.minHeight - 2 * y;
27698 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27699 scale = targetWidth / width;
27702 if(x > 0 && y == 0){
27703 scale = targetHeight / height;
27706 if(x > 0 && y > 0){
27707 scale = targetWidth / width;
27709 if(width < height){
27710 scale = targetHeight / height;
27714 context.scale(scale, scale);
27716 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27717 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27719 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27720 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27722 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27724 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27729 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27730 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27732 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27733 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27735 var targetWidth = this.minWidth - 2 * x;
27736 var targetHeight = this.minHeight - 2 * y;
27740 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27741 scale = targetWidth / width;
27744 if(x > 0 && y == 0){
27745 scale = targetHeight / height;
27748 if(x > 0 && y > 0){
27749 scale = targetWidth / width;
27751 if(width < height){
27752 scale = targetHeight / height;
27756 context.scale(scale, scale);
27758 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27759 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27761 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27762 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27764 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27765 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27767 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27772 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27773 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27775 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27776 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27778 var targetWidth = this.minWidth - 2 * x;
27779 var targetHeight = this.minHeight - 2 * y;
27783 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27784 scale = targetWidth / width;
27787 if(x > 0 && y == 0){
27788 scale = targetHeight / height;
27791 if(x > 0 && y > 0){
27792 scale = targetWidth / width;
27794 if(width < height){
27795 scale = targetHeight / height;
27799 context.scale(scale, scale);
27801 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27802 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27804 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27805 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27807 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27809 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27816 this.cropData = canvas.toDataURL(this.cropType);
27818 if(this.fireEvent('crop', this, this.cropData) !== false){
27819 this.process(this.file, this.cropData);
27826 setThumbBoxSize : function()
27830 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27831 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27832 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27834 this.minWidth = width;
27835 this.minHeight = height;
27837 if(this.rotate == 90 || this.rotate == 270){
27838 this.minWidth = height;
27839 this.minHeight = width;
27844 width = Math.ceil(this.minWidth * height / this.minHeight);
27846 if(this.minWidth > this.minHeight){
27848 height = Math.ceil(this.minHeight * width / this.minWidth);
27851 this.thumbEl.setStyle({
27852 width : width + 'px',
27853 height : height + 'px'
27860 setThumbBoxPosition : function()
27862 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27863 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27865 this.thumbEl.setLeft(x);
27866 this.thumbEl.setTop(y);
27870 baseRotateLevel : function()
27872 this.baseRotate = 1;
27875 typeof(this.exif) != 'undefined' &&
27876 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27877 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27879 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27882 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27886 baseScaleLevel : function()
27890 if(this.isDocument){
27892 if(this.baseRotate == 6 || this.baseRotate == 8){
27894 height = this.thumbEl.getHeight();
27895 this.baseScale = height / this.imageEl.OriginWidth;
27897 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27898 width = this.thumbEl.getWidth();
27899 this.baseScale = width / this.imageEl.OriginHeight;
27905 height = this.thumbEl.getHeight();
27906 this.baseScale = height / this.imageEl.OriginHeight;
27908 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27909 width = this.thumbEl.getWidth();
27910 this.baseScale = width / this.imageEl.OriginWidth;
27916 if(this.baseRotate == 6 || this.baseRotate == 8){
27918 width = this.thumbEl.getHeight();
27919 this.baseScale = width / this.imageEl.OriginHeight;
27921 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27922 height = this.thumbEl.getWidth();
27923 this.baseScale = height / this.imageEl.OriginHeight;
27926 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27927 height = this.thumbEl.getWidth();
27928 this.baseScale = height / this.imageEl.OriginHeight;
27930 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27931 width = this.thumbEl.getHeight();
27932 this.baseScale = width / this.imageEl.OriginWidth;
27939 width = this.thumbEl.getWidth();
27940 this.baseScale = width / this.imageEl.OriginWidth;
27942 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27943 height = this.thumbEl.getHeight();
27944 this.baseScale = height / this.imageEl.OriginHeight;
27947 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27949 height = this.thumbEl.getHeight();
27950 this.baseScale = height / this.imageEl.OriginHeight;
27952 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27953 width = this.thumbEl.getWidth();
27954 this.baseScale = width / this.imageEl.OriginWidth;
27962 getScaleLevel : function()
27964 return this.baseScale * Math.pow(1.1, this.scale);
27967 onTouchStart : function(e)
27969 if(!this.canvasLoaded){
27970 this.beforeSelectFile(e);
27974 var touches = e.browserEvent.touches;
27980 if(touches.length == 1){
27981 this.onMouseDown(e);
27985 if(touches.length != 2){
27991 for(var i = 0, finger; finger = touches[i]; i++){
27992 coords.push(finger.pageX, finger.pageY);
27995 var x = Math.pow(coords[0] - coords[2], 2);
27996 var y = Math.pow(coords[1] - coords[3], 2);
27998 this.startDistance = Math.sqrt(x + y);
28000 this.startScale = this.scale;
28002 this.pinching = true;
28003 this.dragable = false;
28007 onTouchMove : function(e)
28009 if(!this.pinching && !this.dragable){
28013 var touches = e.browserEvent.touches;
28020 this.onMouseMove(e);
28026 for(var i = 0, finger; finger = touches[i]; i++){
28027 coords.push(finger.pageX, finger.pageY);
28030 var x = Math.pow(coords[0] - coords[2], 2);
28031 var y = Math.pow(coords[1] - coords[3], 2);
28033 this.endDistance = Math.sqrt(x + y);
28035 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28037 if(!this.zoomable()){
28038 this.scale = this.startScale;
28046 onTouchEnd : function(e)
28048 this.pinching = false;
28049 this.dragable = false;
28053 process : function(file, crop)
28056 this.maskEl.mask(this.loadingText);
28059 this.xhr = new XMLHttpRequest();
28061 file.xhr = this.xhr;
28063 this.xhr.open(this.method, this.url, true);
28066 "Accept": "application/json",
28067 "Cache-Control": "no-cache",
28068 "X-Requested-With": "XMLHttpRequest"
28071 for (var headerName in headers) {
28072 var headerValue = headers[headerName];
28074 this.xhr.setRequestHeader(headerName, headerValue);
28080 this.xhr.onload = function()
28082 _this.xhrOnLoad(_this.xhr);
28085 this.xhr.onerror = function()
28087 _this.xhrOnError(_this.xhr);
28090 var formData = new FormData();
28092 formData.append('returnHTML', 'NO');
28095 formData.append('crop', crop);
28098 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28099 formData.append(this.paramName, file, file.name);
28102 if(typeof(file.filename) != 'undefined'){
28103 formData.append('filename', file.filename);
28106 if(typeof(file.mimetype) != 'undefined'){
28107 formData.append('mimetype', file.mimetype);
28110 if(this.fireEvent('arrange', this, formData) != false){
28111 this.xhr.send(formData);
28115 xhrOnLoad : function(xhr)
28118 this.maskEl.unmask();
28121 if (xhr.readyState !== 4) {
28122 this.fireEvent('exception', this, xhr);
28126 var response = Roo.decode(xhr.responseText);
28128 if(!response.success){
28129 this.fireEvent('exception', this, xhr);
28133 var response = Roo.decode(xhr.responseText);
28135 this.fireEvent('upload', this, response);
28139 xhrOnError : function()
28142 this.maskEl.unmask();
28145 Roo.log('xhr on error');
28147 var response = Roo.decode(xhr.responseText);
28153 prepare : function(file)
28156 this.maskEl.mask(this.loadingText);
28162 if(typeof(file) === 'string'){
28163 this.loadCanvas(file);
28167 if(!file || !this.urlAPI){
28172 this.cropType = file.type;
28176 if(this.fireEvent('prepare', this, this.file) != false){
28178 var reader = new FileReader();
28180 reader.onload = function (e) {
28181 if (e.target.error) {
28182 Roo.log(e.target.error);
28186 var buffer = e.target.result,
28187 dataView = new DataView(buffer),
28189 maxOffset = dataView.byteLength - 4,
28193 if (dataView.getUint16(0) === 0xffd8) {
28194 while (offset < maxOffset) {
28195 markerBytes = dataView.getUint16(offset);
28197 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28198 markerLength = dataView.getUint16(offset + 2) + 2;
28199 if (offset + markerLength > dataView.byteLength) {
28200 Roo.log('Invalid meta data: Invalid segment size.');
28204 if(markerBytes == 0xffe1){
28205 _this.parseExifData(
28212 offset += markerLength;
28222 var url = _this.urlAPI.createObjectURL(_this.file);
28224 _this.loadCanvas(url);
28229 reader.readAsArrayBuffer(this.file);
28235 parseExifData : function(dataView, offset, length)
28237 var tiffOffset = offset + 10,
28241 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28242 // No Exif data, might be XMP data instead
28246 // Check for the ASCII code for "Exif" (0x45786966):
28247 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28248 // No Exif data, might be XMP data instead
28251 if (tiffOffset + 8 > dataView.byteLength) {
28252 Roo.log('Invalid Exif data: Invalid segment size.');
28255 // Check for the two null bytes:
28256 if (dataView.getUint16(offset + 8) !== 0x0000) {
28257 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28260 // Check the byte alignment:
28261 switch (dataView.getUint16(tiffOffset)) {
28263 littleEndian = true;
28266 littleEndian = false;
28269 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28272 // Check for the TIFF tag marker (0x002A):
28273 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28274 Roo.log('Invalid Exif data: Missing TIFF marker.');
28277 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28278 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28280 this.parseExifTags(
28283 tiffOffset + dirOffset,
28288 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28293 if (dirOffset + 6 > dataView.byteLength) {
28294 Roo.log('Invalid Exif data: Invalid directory offset.');
28297 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28298 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28299 if (dirEndOffset + 4 > dataView.byteLength) {
28300 Roo.log('Invalid Exif data: Invalid directory size.');
28303 for (i = 0; i < tagsNumber; i += 1) {
28307 dirOffset + 2 + 12 * i, // tag offset
28311 // Return the offset to the next directory:
28312 return dataView.getUint32(dirEndOffset, littleEndian);
28315 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28317 var tag = dataView.getUint16(offset, littleEndian);
28319 this.exif[tag] = this.getExifValue(
28323 dataView.getUint16(offset + 2, littleEndian), // tag type
28324 dataView.getUint32(offset + 4, littleEndian), // tag length
28329 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28331 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28340 Roo.log('Invalid Exif data: Invalid tag type.');
28344 tagSize = tagType.size * length;
28345 // Determine if the value is contained in the dataOffset bytes,
28346 // or if the value at the dataOffset is a pointer to the actual data:
28347 dataOffset = tagSize > 4 ?
28348 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28349 if (dataOffset + tagSize > dataView.byteLength) {
28350 Roo.log('Invalid Exif data: Invalid data offset.');
28353 if (length === 1) {
28354 return tagType.getValue(dataView, dataOffset, littleEndian);
28357 for (i = 0; i < length; i += 1) {
28358 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28361 if (tagType.ascii) {
28363 // Concatenate the chars:
28364 for (i = 0; i < values.length; i += 1) {
28366 // Ignore the terminating NULL byte(s):
28367 if (c === '\u0000') {
28379 Roo.apply(Roo.bootstrap.UploadCropbox, {
28381 'Orientation': 0x0112
28385 1: 0, //'top-left',
28387 3: 180, //'bottom-right',
28388 // 4: 'bottom-left',
28390 6: 90, //'right-top',
28391 // 7: 'right-bottom',
28392 8: 270 //'left-bottom'
28396 // byte, 8-bit unsigned int:
28398 getValue: function (dataView, dataOffset) {
28399 return dataView.getUint8(dataOffset);
28403 // ascii, 8-bit byte:
28405 getValue: function (dataView, dataOffset) {
28406 return String.fromCharCode(dataView.getUint8(dataOffset));
28411 // short, 16 bit int:
28413 getValue: function (dataView, dataOffset, littleEndian) {
28414 return dataView.getUint16(dataOffset, littleEndian);
28418 // long, 32 bit int:
28420 getValue: function (dataView, dataOffset, littleEndian) {
28421 return dataView.getUint32(dataOffset, littleEndian);
28425 // rational = two long values, first is numerator, second is denominator:
28427 getValue: function (dataView, dataOffset, littleEndian) {
28428 return dataView.getUint32(dataOffset, littleEndian) /
28429 dataView.getUint32(dataOffset + 4, littleEndian);
28433 // slong, 32 bit signed int:
28435 getValue: function (dataView, dataOffset, littleEndian) {
28436 return dataView.getInt32(dataOffset, littleEndian);
28440 // srational, two slongs, first is numerator, second is denominator:
28442 getValue: function (dataView, dataOffset, littleEndian) {
28443 return dataView.getInt32(dataOffset, littleEndian) /
28444 dataView.getInt32(dataOffset + 4, littleEndian);
28454 cls : 'btn-group roo-upload-cropbox-rotate-left',
28455 action : 'rotate-left',
28459 cls : 'btn btn-default',
28460 html : '<i class="fa fa-undo"></i>'
28466 cls : 'btn-group roo-upload-cropbox-picture',
28467 action : 'picture',
28471 cls : 'btn btn-default',
28472 html : '<i class="fa fa-picture-o"></i>'
28478 cls : 'btn-group roo-upload-cropbox-rotate-right',
28479 action : 'rotate-right',
28483 cls : 'btn btn-default',
28484 html : '<i class="fa fa-repeat"></i>'
28492 cls : 'btn-group roo-upload-cropbox-rotate-left',
28493 action : 'rotate-left',
28497 cls : 'btn btn-default',
28498 html : '<i class="fa fa-undo"></i>'
28504 cls : 'btn-group roo-upload-cropbox-download',
28505 action : 'download',
28509 cls : 'btn btn-default',
28510 html : '<i class="fa fa-download"></i>'
28516 cls : 'btn-group roo-upload-cropbox-crop',
28521 cls : 'btn btn-default',
28522 html : '<i class="fa fa-crop"></i>'
28528 cls : 'btn-group roo-upload-cropbox-trash',
28533 cls : 'btn btn-default',
28534 html : '<i class="fa fa-trash"></i>'
28540 cls : 'btn-group roo-upload-cropbox-rotate-right',
28541 action : 'rotate-right',
28545 cls : 'btn btn-default',
28546 html : '<i class="fa fa-repeat"></i>'
28554 cls : 'btn-group roo-upload-cropbox-rotate-left',
28555 action : 'rotate-left',
28559 cls : 'btn btn-default',
28560 html : '<i class="fa fa-undo"></i>'
28566 cls : 'btn-group roo-upload-cropbox-rotate-right',
28567 action : 'rotate-right',
28571 cls : 'btn btn-default',
28572 html : '<i class="fa fa-repeat"></i>'
28585 * @class Roo.bootstrap.DocumentManager
28586 * @extends Roo.bootstrap.Component
28587 * Bootstrap DocumentManager class
28588 * @cfg {String} paramName default 'imageUpload'
28589 * @cfg {String} toolTipName default 'filename'
28590 * @cfg {String} method default POST
28591 * @cfg {String} url action url
28592 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28593 * @cfg {Boolean} multiple multiple upload default true
28594 * @cfg {Number} thumbSize default 300
28595 * @cfg {String} fieldLabel
28596 * @cfg {Number} labelWidth default 4
28597 * @cfg {String} labelAlign (left|top) default left
28598 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28599 * @cfg {Number} labellg set the width of label (1-12)
28600 * @cfg {Number} labelmd set the width of label (1-12)
28601 * @cfg {Number} labelsm set the width of label (1-12)
28602 * @cfg {Number} labelxs set the width of label (1-12)
28605 * Create a new DocumentManager
28606 * @param {Object} config The config object
28609 Roo.bootstrap.DocumentManager = function(config){
28610 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28613 this.delegates = [];
28618 * Fire when initial the DocumentManager
28619 * @param {Roo.bootstrap.DocumentManager} this
28624 * inspect selected file
28625 * @param {Roo.bootstrap.DocumentManager} this
28626 * @param {File} file
28631 * Fire when xhr load exception
28632 * @param {Roo.bootstrap.DocumentManager} this
28633 * @param {XMLHttpRequest} xhr
28635 "exception" : true,
28637 * @event afterupload
28638 * Fire when xhr load exception
28639 * @param {Roo.bootstrap.DocumentManager} this
28640 * @param {XMLHttpRequest} xhr
28642 "afterupload" : true,
28645 * prepare the form data
28646 * @param {Roo.bootstrap.DocumentManager} this
28647 * @param {Object} formData
28652 * Fire when remove the file
28653 * @param {Roo.bootstrap.DocumentManager} this
28654 * @param {Object} file
28659 * Fire after refresh the file
28660 * @param {Roo.bootstrap.DocumentManager} this
28665 * Fire after click the image
28666 * @param {Roo.bootstrap.DocumentManager} this
28667 * @param {Object} file
28672 * Fire when upload a image and editable set to true
28673 * @param {Roo.bootstrap.DocumentManager} this
28674 * @param {Object} file
28678 * @event beforeselectfile
28679 * Fire before select file
28680 * @param {Roo.bootstrap.DocumentManager} this
28682 "beforeselectfile" : true,
28685 * Fire before process file
28686 * @param {Roo.bootstrap.DocumentManager} this
28687 * @param {Object} file
28691 * @event previewrendered
28692 * Fire when preview rendered
28693 * @param {Roo.bootstrap.DocumentManager} this
28694 * @param {Object} file
28696 "previewrendered" : true
28701 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28710 paramName : 'imageUpload',
28711 toolTipName : 'filename',
28714 labelAlign : 'left',
28724 getAutoCreate : function()
28726 var managerWidget = {
28728 cls : 'roo-document-manager',
28732 cls : 'roo-document-manager-selector',
28737 cls : 'roo-document-manager-uploader',
28741 cls : 'roo-document-manager-upload-btn',
28742 html : '<i class="fa fa-plus"></i>'
28753 cls : 'column col-md-12',
28758 if(this.fieldLabel.length){
28763 cls : 'column col-md-12',
28764 html : this.fieldLabel
28768 cls : 'column col-md-12',
28773 if(this.labelAlign == 'left'){
28778 html : this.fieldLabel
28787 if(this.labelWidth > 12){
28788 content[0].style = "width: " + this.labelWidth + 'px';
28791 if(this.labelWidth < 13 && this.labelmd == 0){
28792 this.labelmd = this.labelWidth;
28795 if(this.labellg > 0){
28796 content[0].cls += ' col-lg-' + this.labellg;
28797 content[1].cls += ' col-lg-' + (12 - this.labellg);
28800 if(this.labelmd > 0){
28801 content[0].cls += ' col-md-' + this.labelmd;
28802 content[1].cls += ' col-md-' + (12 - this.labelmd);
28805 if(this.labelsm > 0){
28806 content[0].cls += ' col-sm-' + this.labelsm;
28807 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28810 if(this.labelxs > 0){
28811 content[0].cls += ' col-xs-' + this.labelxs;
28812 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28820 cls : 'row clearfix',
28828 initEvents : function()
28830 this.managerEl = this.el.select('.roo-document-manager', true).first();
28831 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28833 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28834 this.selectorEl.hide();
28837 this.selectorEl.attr('multiple', 'multiple');
28840 this.selectorEl.on('change', this.onFileSelected, this);
28842 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28843 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28845 this.uploader.on('click', this.onUploaderClick, this);
28847 this.renderProgressDialog();
28851 window.addEventListener("resize", function() { _this.refresh(); } );
28853 this.fireEvent('initial', this);
28856 renderProgressDialog : function()
28860 this.progressDialog = new Roo.bootstrap.Modal({
28861 cls : 'roo-document-manager-progress-dialog',
28862 allow_close : false,
28872 btnclick : function() {
28873 _this.uploadCancel();
28879 this.progressDialog.render(Roo.get(document.body));
28881 this.progress = new Roo.bootstrap.Progress({
28882 cls : 'roo-document-manager-progress',
28887 this.progress.render(this.progressDialog.getChildContainer());
28889 this.progressBar = new Roo.bootstrap.ProgressBar({
28890 cls : 'roo-document-manager-progress-bar',
28893 aria_valuemax : 12,
28897 this.progressBar.render(this.progress.getChildContainer());
28900 onUploaderClick : function(e)
28902 e.preventDefault();
28904 if(this.fireEvent('beforeselectfile', this) != false){
28905 this.selectorEl.dom.click();
28910 onFileSelected : function(e)
28912 e.preventDefault();
28914 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28918 Roo.each(this.selectorEl.dom.files, function(file){
28919 if(this.fireEvent('inspect', this, file) != false){
28920 this.files.push(file);
28930 this.selectorEl.dom.value = '';
28932 if(!this.files || !this.files.length){
28936 if(this.boxes > 0 && this.files.length > this.boxes){
28937 this.files = this.files.slice(0, this.boxes);
28940 this.uploader.show();
28942 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28943 this.uploader.hide();
28952 Roo.each(this.files, function(file){
28954 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28955 var f = this.renderPreview(file);
28960 if(file.type.indexOf('image') != -1){
28961 this.delegates.push(
28963 _this.process(file);
28964 }).createDelegate(this)
28972 _this.process(file);
28973 }).createDelegate(this)
28978 this.files = files;
28980 this.delegates = this.delegates.concat(docs);
28982 if(!this.delegates.length){
28987 this.progressBar.aria_valuemax = this.delegates.length;
28994 arrange : function()
28996 if(!this.delegates.length){
28997 this.progressDialog.hide();
29002 var delegate = this.delegates.shift();
29004 this.progressDialog.show();
29006 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29008 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29013 refresh : function()
29015 this.uploader.show();
29017 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29018 this.uploader.hide();
29021 Roo.isTouch ? this.closable(false) : this.closable(true);
29023 this.fireEvent('refresh', this);
29026 onRemove : function(e, el, o)
29028 e.preventDefault();
29030 this.fireEvent('remove', this, o);
29034 remove : function(o)
29038 Roo.each(this.files, function(file){
29039 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29048 this.files = files;
29055 Roo.each(this.files, function(file){
29060 file.target.remove();
29069 onClick : function(e, el, o)
29071 e.preventDefault();
29073 this.fireEvent('click', this, o);
29077 closable : function(closable)
29079 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29081 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29093 xhrOnLoad : function(xhr)
29095 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29099 if (xhr.readyState !== 4) {
29101 this.fireEvent('exception', this, xhr);
29105 var response = Roo.decode(xhr.responseText);
29107 if(!response.success){
29109 this.fireEvent('exception', this, xhr);
29113 var file = this.renderPreview(response.data);
29115 this.files.push(file);
29119 this.fireEvent('afterupload', this, xhr);
29123 xhrOnError : function(xhr)
29125 Roo.log('xhr on error');
29127 var response = Roo.decode(xhr.responseText);
29134 process : function(file)
29136 if(this.fireEvent('process', this, file) !== false){
29137 if(this.editable && file.type.indexOf('image') != -1){
29138 this.fireEvent('edit', this, file);
29142 this.uploadStart(file, false);
29149 uploadStart : function(file, crop)
29151 this.xhr = new XMLHttpRequest();
29153 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29158 file.xhr = this.xhr;
29160 this.managerEl.createChild({
29162 cls : 'roo-document-manager-loading',
29166 tooltip : file.name,
29167 cls : 'roo-document-manager-thumb',
29168 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29174 this.xhr.open(this.method, this.url, true);
29177 "Accept": "application/json",
29178 "Cache-Control": "no-cache",
29179 "X-Requested-With": "XMLHttpRequest"
29182 for (var headerName in headers) {
29183 var headerValue = headers[headerName];
29185 this.xhr.setRequestHeader(headerName, headerValue);
29191 this.xhr.onload = function()
29193 _this.xhrOnLoad(_this.xhr);
29196 this.xhr.onerror = function()
29198 _this.xhrOnError(_this.xhr);
29201 var formData = new FormData();
29203 formData.append('returnHTML', 'NO');
29206 formData.append('crop', crop);
29209 formData.append(this.paramName, file, file.name);
29216 if(this.fireEvent('prepare', this, formData, options) != false){
29218 if(options.manually){
29222 this.xhr.send(formData);
29226 this.uploadCancel();
29229 uploadCancel : function()
29235 this.delegates = [];
29237 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29244 renderPreview : function(file)
29246 if(typeof(file.target) != 'undefined' && file.target){
29250 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29252 var previewEl = this.managerEl.createChild({
29254 cls : 'roo-document-manager-preview',
29258 tooltip : file[this.toolTipName],
29259 cls : 'roo-document-manager-thumb',
29260 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29265 html : '<i class="fa fa-times-circle"></i>'
29270 var close = previewEl.select('button.close', true).first();
29272 close.on('click', this.onRemove, this, file);
29274 file.target = previewEl;
29276 var image = previewEl.select('img', true).first();
29280 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29282 image.on('click', this.onClick, this, file);
29284 this.fireEvent('previewrendered', this, file);
29290 onPreviewLoad : function(file, image)
29292 if(typeof(file.target) == 'undefined' || !file.target){
29296 var width = image.dom.naturalWidth || image.dom.width;
29297 var height = image.dom.naturalHeight || image.dom.height;
29299 if(width > height){
29300 file.target.addClass('wide');
29304 file.target.addClass('tall');
29309 uploadFromSource : function(file, crop)
29311 this.xhr = new XMLHttpRequest();
29313 this.managerEl.createChild({
29315 cls : 'roo-document-manager-loading',
29319 tooltip : file.name,
29320 cls : 'roo-document-manager-thumb',
29321 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29327 this.xhr.open(this.method, this.url, true);
29330 "Accept": "application/json",
29331 "Cache-Control": "no-cache",
29332 "X-Requested-With": "XMLHttpRequest"
29335 for (var headerName in headers) {
29336 var headerValue = headers[headerName];
29338 this.xhr.setRequestHeader(headerName, headerValue);
29344 this.xhr.onload = function()
29346 _this.xhrOnLoad(_this.xhr);
29349 this.xhr.onerror = function()
29351 _this.xhrOnError(_this.xhr);
29354 var formData = new FormData();
29356 formData.append('returnHTML', 'NO');
29358 formData.append('crop', crop);
29360 if(typeof(file.filename) != 'undefined'){
29361 formData.append('filename', file.filename);
29364 if(typeof(file.mimetype) != 'undefined'){
29365 formData.append('mimetype', file.mimetype);
29370 if(this.fireEvent('prepare', this, formData) != false){
29371 this.xhr.send(formData);
29381 * @class Roo.bootstrap.DocumentViewer
29382 * @extends Roo.bootstrap.Component
29383 * Bootstrap DocumentViewer class
29384 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29385 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29388 * Create a new DocumentViewer
29389 * @param {Object} config The config object
29392 Roo.bootstrap.DocumentViewer = function(config){
29393 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29398 * Fire after initEvent
29399 * @param {Roo.bootstrap.DocumentViewer} this
29405 * @param {Roo.bootstrap.DocumentViewer} this
29410 * Fire after download button
29411 * @param {Roo.bootstrap.DocumentViewer} this
29416 * Fire after trash button
29417 * @param {Roo.bootstrap.DocumentViewer} this
29424 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29426 showDownload : true,
29430 getAutoCreate : function()
29434 cls : 'roo-document-viewer',
29438 cls : 'roo-document-viewer-body',
29442 cls : 'roo-document-viewer-thumb',
29446 cls : 'roo-document-viewer-image'
29454 cls : 'roo-document-viewer-footer',
29457 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29461 cls : 'btn-group roo-document-viewer-download',
29465 cls : 'btn btn-default',
29466 html : '<i class="fa fa-download"></i>'
29472 cls : 'btn-group roo-document-viewer-trash',
29476 cls : 'btn btn-default',
29477 html : '<i class="fa fa-trash"></i>'
29490 initEvents : function()
29492 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29493 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29495 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29496 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29498 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29499 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29501 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29502 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29504 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29505 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29507 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29508 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29510 this.bodyEl.on('click', this.onClick, this);
29511 this.downloadBtn.on('click', this.onDownload, this);
29512 this.trashBtn.on('click', this.onTrash, this);
29514 this.downloadBtn.hide();
29515 this.trashBtn.hide();
29517 if(this.showDownload){
29518 this.downloadBtn.show();
29521 if(this.showTrash){
29522 this.trashBtn.show();
29525 if(!this.showDownload && !this.showTrash) {
29526 this.footerEl.hide();
29531 initial : function()
29533 this.fireEvent('initial', this);
29537 onClick : function(e)
29539 e.preventDefault();
29541 this.fireEvent('click', this);
29544 onDownload : function(e)
29546 e.preventDefault();
29548 this.fireEvent('download', this);
29551 onTrash : function(e)
29553 e.preventDefault();
29555 this.fireEvent('trash', this);
29567 * @class Roo.bootstrap.NavProgressBar
29568 * @extends Roo.bootstrap.Component
29569 * Bootstrap NavProgressBar class
29572 * Create a new nav progress bar
29573 * @param {Object} config The config object
29576 Roo.bootstrap.NavProgressBar = function(config){
29577 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29579 this.bullets = this.bullets || [];
29581 // Roo.bootstrap.NavProgressBar.register(this);
29585 * Fires when the active item changes
29586 * @param {Roo.bootstrap.NavProgressBar} this
29587 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29588 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29595 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29600 getAutoCreate : function()
29602 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29606 cls : 'roo-navigation-bar-group',
29610 cls : 'roo-navigation-top-bar'
29614 cls : 'roo-navigation-bullets-bar',
29618 cls : 'roo-navigation-bar'
29625 cls : 'roo-navigation-bottom-bar'
29635 initEvents: function()
29640 onRender : function(ct, position)
29642 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29644 if(this.bullets.length){
29645 Roo.each(this.bullets, function(b){
29654 addItem : function(cfg)
29656 var item = new Roo.bootstrap.NavProgressItem(cfg);
29658 item.parentId = this.id;
29659 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29662 var top = new Roo.bootstrap.Element({
29664 cls : 'roo-navigation-bar-text'
29667 var bottom = new Roo.bootstrap.Element({
29669 cls : 'roo-navigation-bar-text'
29672 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29673 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29675 var topText = new Roo.bootstrap.Element({
29677 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29680 var bottomText = new Roo.bootstrap.Element({
29682 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29685 topText.onRender(top.el, null);
29686 bottomText.onRender(bottom.el, null);
29689 item.bottomEl = bottom;
29692 this.barItems.push(item);
29697 getActive : function()
29699 var active = false;
29701 Roo.each(this.barItems, function(v){
29703 if (!v.isActive()) {
29715 setActiveItem : function(item)
29719 Roo.each(this.barItems, function(v){
29720 if (v.rid == item.rid) {
29724 if (v.isActive()) {
29725 v.setActive(false);
29730 item.setActive(true);
29732 this.fireEvent('changed', this, item, prev);
29735 getBarItem: function(rid)
29739 Roo.each(this.barItems, function(e) {
29740 if (e.rid != rid) {
29751 indexOfItem : function(item)
29755 Roo.each(this.barItems, function(v, i){
29757 if (v.rid != item.rid) {
29768 setActiveNext : function()
29770 var i = this.indexOfItem(this.getActive());
29772 if (i > this.barItems.length) {
29776 this.setActiveItem(this.barItems[i+1]);
29779 setActivePrev : function()
29781 var i = this.indexOfItem(this.getActive());
29787 this.setActiveItem(this.barItems[i-1]);
29790 format : function()
29792 if(!this.barItems.length){
29796 var width = 100 / this.barItems.length;
29798 Roo.each(this.barItems, function(i){
29799 i.el.setStyle('width', width + '%');
29800 i.topEl.el.setStyle('width', width + '%');
29801 i.bottomEl.el.setStyle('width', width + '%');
29810 * Nav Progress Item
29815 * @class Roo.bootstrap.NavProgressItem
29816 * @extends Roo.bootstrap.Component
29817 * Bootstrap NavProgressItem class
29818 * @cfg {String} rid the reference id
29819 * @cfg {Boolean} active (true|false) Is item active default false
29820 * @cfg {Boolean} disabled (true|false) Is item active default false
29821 * @cfg {String} html
29822 * @cfg {String} position (top|bottom) text position default bottom
29823 * @cfg {String} icon show icon instead of number
29826 * Create a new NavProgressItem
29827 * @param {Object} config The config object
29829 Roo.bootstrap.NavProgressItem = function(config){
29830 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29835 * The raw click event for the entire grid.
29836 * @param {Roo.bootstrap.NavProgressItem} this
29837 * @param {Roo.EventObject} e
29844 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29850 position : 'bottom',
29853 getAutoCreate : function()
29855 var iconCls = 'roo-navigation-bar-item-icon';
29857 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29861 cls: 'roo-navigation-bar-item',
29871 cfg.cls += ' active';
29874 cfg.cls += ' disabled';
29880 disable : function()
29882 this.setDisabled(true);
29885 enable : function()
29887 this.setDisabled(false);
29890 initEvents: function()
29892 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29894 this.iconEl.on('click', this.onClick, this);
29897 onClick : function(e)
29899 e.preventDefault();
29905 if(this.fireEvent('click', this, e) === false){
29909 this.parent().setActiveItem(this);
29912 isActive: function ()
29914 return this.active;
29917 setActive : function(state)
29919 if(this.active == state){
29923 this.active = state;
29926 this.el.addClass('active');
29930 this.el.removeClass('active');
29935 setDisabled : function(state)
29937 if(this.disabled == state){
29941 this.disabled = state;
29944 this.el.addClass('disabled');
29948 this.el.removeClass('disabled');
29951 tooltipEl : function()
29953 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29966 * @class Roo.bootstrap.FieldLabel
29967 * @extends Roo.bootstrap.Component
29968 * Bootstrap FieldLabel class
29969 * @cfg {String} html contents of the element
29970 * @cfg {String} tag tag of the element default label
29971 * @cfg {String} cls class of the element
29972 * @cfg {String} target label target
29973 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29974 * @cfg {String} invalidClass default "text-warning"
29975 * @cfg {String} validClass default "text-success"
29976 * @cfg {String} iconTooltip default "This field is required"
29977 * @cfg {String} indicatorpos (left|right) default left
29980 * Create a new FieldLabel
29981 * @param {Object} config The config object
29984 Roo.bootstrap.FieldLabel = function(config){
29985 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29990 * Fires after the field has been marked as invalid.
29991 * @param {Roo.form.FieldLabel} this
29992 * @param {String} msg The validation message
29997 * Fires after the field has been validated with no errors.
29998 * @param {Roo.form.FieldLabel} this
30004 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30011 invalidClass : 'has-warning',
30012 validClass : 'has-success',
30013 iconTooltip : 'This field is required',
30014 indicatorpos : 'left',
30016 getAutoCreate : function(){
30019 if (!this.allowBlank) {
30025 cls : 'roo-bootstrap-field-label ' + this.cls,
30030 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30031 tooltip : this.iconTooltip
30040 if(this.indicatorpos == 'right'){
30043 cls : 'roo-bootstrap-field-label ' + this.cls,
30052 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30053 tooltip : this.iconTooltip
30062 initEvents: function()
30064 Roo.bootstrap.Element.superclass.initEvents.call(this);
30066 this.indicator = this.indicatorEl();
30068 if(this.indicator){
30069 this.indicator.removeClass('visible');
30070 this.indicator.addClass('invisible');
30073 Roo.bootstrap.FieldLabel.register(this);
30076 indicatorEl : function()
30078 var indicator = this.el.select('i.roo-required-indicator',true).first();
30089 * Mark this field as valid
30091 markValid : function()
30093 if(this.indicator){
30094 this.indicator.removeClass('visible');
30095 this.indicator.addClass('invisible');
30098 this.el.removeClass(this.invalidClass);
30100 this.el.addClass(this.validClass);
30102 this.fireEvent('valid', this);
30106 * Mark this field as invalid
30107 * @param {String} msg The validation message
30109 markInvalid : function(msg)
30111 if(this.indicator){
30112 this.indicator.removeClass('invisible');
30113 this.indicator.addClass('visible');
30116 this.el.removeClass(this.validClass);
30118 this.el.addClass(this.invalidClass);
30120 this.fireEvent('invalid', this, msg);
30126 Roo.apply(Roo.bootstrap.FieldLabel, {
30131 * register a FieldLabel Group
30132 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30134 register : function(label)
30136 if(this.groups.hasOwnProperty(label.target)){
30140 this.groups[label.target] = label;
30144 * fetch a FieldLabel Group based on the target
30145 * @param {string} target
30146 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30148 get: function(target) {
30149 if (typeof(this.groups[target]) == 'undefined') {
30153 return this.groups[target] ;
30162 * page DateSplitField.
30168 * @class Roo.bootstrap.DateSplitField
30169 * @extends Roo.bootstrap.Component
30170 * Bootstrap DateSplitField class
30171 * @cfg {string} fieldLabel - the label associated
30172 * @cfg {Number} labelWidth set the width of label (0-12)
30173 * @cfg {String} labelAlign (top|left)
30174 * @cfg {Boolean} dayAllowBlank (true|false) default false
30175 * @cfg {Boolean} monthAllowBlank (true|false) default false
30176 * @cfg {Boolean} yearAllowBlank (true|false) default false
30177 * @cfg {string} dayPlaceholder
30178 * @cfg {string} monthPlaceholder
30179 * @cfg {string} yearPlaceholder
30180 * @cfg {string} dayFormat default 'd'
30181 * @cfg {string} monthFormat default 'm'
30182 * @cfg {string} yearFormat default 'Y'
30183 * @cfg {Number} labellg set the width of label (1-12)
30184 * @cfg {Number} labelmd set the width of label (1-12)
30185 * @cfg {Number} labelsm set the width of label (1-12)
30186 * @cfg {Number} labelxs set the width of label (1-12)
30190 * Create a new DateSplitField
30191 * @param {Object} config The config object
30194 Roo.bootstrap.DateSplitField = function(config){
30195 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30201 * getting the data of years
30202 * @param {Roo.bootstrap.DateSplitField} this
30203 * @param {Object} years
30208 * getting the data of days
30209 * @param {Roo.bootstrap.DateSplitField} this
30210 * @param {Object} days
30215 * Fires after the field has been marked as invalid.
30216 * @param {Roo.form.Field} this
30217 * @param {String} msg The validation message
30222 * Fires after the field has been validated with no errors.
30223 * @param {Roo.form.Field} this
30229 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30232 labelAlign : 'top',
30234 dayAllowBlank : false,
30235 monthAllowBlank : false,
30236 yearAllowBlank : false,
30237 dayPlaceholder : '',
30238 monthPlaceholder : '',
30239 yearPlaceholder : '',
30243 isFormField : true,
30249 getAutoCreate : function()
30253 cls : 'row roo-date-split-field-group',
30258 cls : 'form-hidden-field roo-date-split-field-group-value',
30264 var labelCls = 'col-md-12';
30265 var contentCls = 'col-md-4';
30267 if(this.fieldLabel){
30271 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30275 html : this.fieldLabel
30280 if(this.labelAlign == 'left'){
30282 if(this.labelWidth > 12){
30283 label.style = "width: " + this.labelWidth + 'px';
30286 if(this.labelWidth < 13 && this.labelmd == 0){
30287 this.labelmd = this.labelWidth;
30290 if(this.labellg > 0){
30291 labelCls = ' col-lg-' + this.labellg;
30292 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30295 if(this.labelmd > 0){
30296 labelCls = ' col-md-' + this.labelmd;
30297 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30300 if(this.labelsm > 0){
30301 labelCls = ' col-sm-' + this.labelsm;
30302 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30305 if(this.labelxs > 0){
30306 labelCls = ' col-xs-' + this.labelxs;
30307 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30311 label.cls += ' ' + labelCls;
30313 cfg.cn.push(label);
30316 Roo.each(['day', 'month', 'year'], function(t){
30319 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30326 inputEl: function ()
30328 return this.el.select('.roo-date-split-field-group-value', true).first();
30331 onRender : function(ct, position)
30335 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30337 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30339 this.dayField = new Roo.bootstrap.ComboBox({
30340 allowBlank : this.dayAllowBlank,
30341 alwaysQuery : true,
30342 displayField : 'value',
30345 forceSelection : true,
30347 placeholder : this.dayPlaceholder,
30348 selectOnFocus : true,
30349 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30350 triggerAction : 'all',
30352 valueField : 'value',
30353 store : new Roo.data.SimpleStore({
30354 data : (function() {
30356 _this.fireEvent('days', _this, days);
30359 fields : [ 'value' ]
30362 select : function (_self, record, index)
30364 _this.setValue(_this.getValue());
30369 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30371 this.monthField = new Roo.bootstrap.MonthField({
30372 after : '<i class=\"fa fa-calendar\"></i>',
30373 allowBlank : this.monthAllowBlank,
30374 placeholder : this.monthPlaceholder,
30377 render : function (_self)
30379 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30380 e.preventDefault();
30384 select : function (_self, oldvalue, newvalue)
30386 _this.setValue(_this.getValue());
30391 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30393 this.yearField = new Roo.bootstrap.ComboBox({
30394 allowBlank : this.yearAllowBlank,
30395 alwaysQuery : true,
30396 displayField : 'value',
30399 forceSelection : true,
30401 placeholder : this.yearPlaceholder,
30402 selectOnFocus : true,
30403 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30404 triggerAction : 'all',
30406 valueField : 'value',
30407 store : new Roo.data.SimpleStore({
30408 data : (function() {
30410 _this.fireEvent('years', _this, years);
30413 fields : [ 'value' ]
30416 select : function (_self, record, index)
30418 _this.setValue(_this.getValue());
30423 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30426 setValue : function(v, format)
30428 this.inputEl.dom.value = v;
30430 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30432 var d = Date.parseDate(v, f);
30439 this.setDay(d.format(this.dayFormat));
30440 this.setMonth(d.format(this.monthFormat));
30441 this.setYear(d.format(this.yearFormat));
30448 setDay : function(v)
30450 this.dayField.setValue(v);
30451 this.inputEl.dom.value = this.getValue();
30456 setMonth : function(v)
30458 this.monthField.setValue(v, true);
30459 this.inputEl.dom.value = this.getValue();
30464 setYear : function(v)
30466 this.yearField.setValue(v);
30467 this.inputEl.dom.value = this.getValue();
30472 getDay : function()
30474 return this.dayField.getValue();
30477 getMonth : function()
30479 return this.monthField.getValue();
30482 getYear : function()
30484 return this.yearField.getValue();
30487 getValue : function()
30489 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30491 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30501 this.inputEl.dom.value = '';
30506 validate : function()
30508 var d = this.dayField.validate();
30509 var m = this.monthField.validate();
30510 var y = this.yearField.validate();
30515 (!this.dayAllowBlank && !d) ||
30516 (!this.monthAllowBlank && !m) ||
30517 (!this.yearAllowBlank && !y)
30522 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30531 this.markInvalid();
30536 markValid : function()
30539 var label = this.el.select('label', true).first();
30540 var icon = this.el.select('i.fa-star', true).first();
30546 this.fireEvent('valid', this);
30550 * Mark this field as invalid
30551 * @param {String} msg The validation message
30553 markInvalid : function(msg)
30556 var label = this.el.select('label', true).first();
30557 var icon = this.el.select('i.fa-star', true).first();
30559 if(label && !icon){
30560 this.el.select('.roo-date-split-field-label', true).createChild({
30562 cls : 'text-danger fa fa-lg fa-star',
30563 tooltip : 'This field is required',
30564 style : 'margin-right:5px;'
30568 this.fireEvent('invalid', this, msg);
30571 clearInvalid : function()
30573 var label = this.el.select('label', true).first();
30574 var icon = this.el.select('i.fa-star', true).first();
30580 this.fireEvent('valid', this);
30583 getName: function()
30593 * http://masonry.desandro.com
30595 * The idea is to render all the bricks based on vertical width...
30597 * The original code extends 'outlayer' - we might need to use that....
30603 * @class Roo.bootstrap.LayoutMasonry
30604 * @extends Roo.bootstrap.Component
30605 * Bootstrap Layout Masonry class
30608 * Create a new Element
30609 * @param {Object} config The config object
30612 Roo.bootstrap.LayoutMasonry = function(config){
30614 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30618 Roo.bootstrap.LayoutMasonry.register(this);
30624 * Fire after layout the items
30625 * @param {Roo.bootstrap.LayoutMasonry} this
30626 * @param {Roo.EventObject} e
30633 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30636 * @cfg {Boolean} isLayoutInstant = no animation?
30638 isLayoutInstant : false, // needed?
30641 * @cfg {Number} boxWidth width of the columns
30646 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30651 * @cfg {Number} padWidth padding below box..
30656 * @cfg {Number} gutter gutter width..
30661 * @cfg {Number} maxCols maximum number of columns
30667 * @cfg {Boolean} isAutoInitial defalut true
30669 isAutoInitial : true,
30674 * @cfg {Boolean} isHorizontal defalut false
30676 isHorizontal : false,
30678 currentSize : null,
30684 bricks: null, //CompositeElement
30688 _isLayoutInited : false,
30690 // isAlternative : false, // only use for vertical layout...
30693 * @cfg {Number} alternativePadWidth padding below box..
30695 alternativePadWidth : 50,
30697 selectedBrick : [],
30699 getAutoCreate : function(){
30701 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30705 cls: 'blog-masonary-wrapper ' + this.cls,
30707 cls : 'mas-boxes masonary'
30714 getChildContainer: function( )
30716 if (this.boxesEl) {
30717 return this.boxesEl;
30720 this.boxesEl = this.el.select('.mas-boxes').first();
30722 return this.boxesEl;
30726 initEvents : function()
30730 if(this.isAutoInitial){
30731 Roo.log('hook children rendered');
30732 this.on('childrenrendered', function() {
30733 Roo.log('children rendered');
30739 initial : function()
30741 this.selectedBrick = [];
30743 this.currentSize = this.el.getBox(true);
30745 Roo.EventManager.onWindowResize(this.resize, this);
30747 if(!this.isAutoInitial){
30755 //this.layout.defer(500,this);
30759 resize : function()
30761 var cs = this.el.getBox(true);
30764 this.currentSize.width == cs.width &&
30765 this.currentSize.x == cs.x &&
30766 this.currentSize.height == cs.height &&
30767 this.currentSize.y == cs.y
30769 Roo.log("no change in with or X or Y");
30773 this.currentSize = cs;
30779 layout : function()
30781 this._resetLayout();
30783 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30785 this.layoutItems( isInstant );
30787 this._isLayoutInited = true;
30789 this.fireEvent('layout', this);
30793 _resetLayout : function()
30795 if(this.isHorizontal){
30796 this.horizontalMeasureColumns();
30800 this.verticalMeasureColumns();
30804 verticalMeasureColumns : function()
30806 this.getContainerWidth();
30808 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30809 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30813 var boxWidth = this.boxWidth + this.padWidth;
30815 if(this.containerWidth < this.boxWidth){
30816 boxWidth = this.containerWidth
30819 var containerWidth = this.containerWidth;
30821 var cols = Math.floor(containerWidth / boxWidth);
30823 this.cols = Math.max( cols, 1 );
30825 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30827 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30829 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30831 this.colWidth = boxWidth + avail - this.padWidth;
30833 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30834 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30837 horizontalMeasureColumns : function()
30839 this.getContainerWidth();
30841 var boxWidth = this.boxWidth;
30843 if(this.containerWidth < boxWidth){
30844 boxWidth = this.containerWidth;
30847 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30849 this.el.setHeight(boxWidth);
30853 getContainerWidth : function()
30855 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30858 layoutItems : function( isInstant )
30860 Roo.log(this.bricks);
30862 var items = Roo.apply([], this.bricks);
30864 if(this.isHorizontal){
30865 this._horizontalLayoutItems( items , isInstant );
30869 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30870 // this._verticalAlternativeLayoutItems( items , isInstant );
30874 this._verticalLayoutItems( items , isInstant );
30878 _verticalLayoutItems : function ( items , isInstant)
30880 if ( !items || !items.length ) {
30885 ['xs', 'xs', 'xs', 'tall'],
30886 ['xs', 'xs', 'tall'],
30887 ['xs', 'xs', 'sm'],
30888 ['xs', 'xs', 'xs'],
30894 ['sm', 'xs', 'xs'],
30898 ['tall', 'xs', 'xs', 'xs'],
30899 ['tall', 'xs', 'xs'],
30911 Roo.each(items, function(item, k){
30913 switch (item.size) {
30914 // these layouts take up a full box,
30925 boxes.push([item]);
30948 var filterPattern = function(box, length)
30956 var pattern = box.slice(0, length);
30960 Roo.each(pattern, function(i){
30961 format.push(i.size);
30964 Roo.each(standard, function(s){
30966 if(String(s) != String(format)){
30975 if(!match && length == 1){
30980 filterPattern(box, length - 1);
30984 queue.push(pattern);
30986 box = box.slice(length, box.length);
30988 filterPattern(box, 4);
30994 Roo.each(boxes, function(box, k){
31000 if(box.length == 1){
31005 filterPattern(box, 4);
31009 this._processVerticalLayoutQueue( queue, isInstant );
31013 // _verticalAlternativeLayoutItems : function( items , isInstant )
31015 // if ( !items || !items.length ) {
31019 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31023 _horizontalLayoutItems : function ( items , isInstant)
31025 if ( !items || !items.length || items.length < 3) {
31031 var eItems = items.slice(0, 3);
31033 items = items.slice(3, items.length);
31036 ['xs', 'xs', 'xs', 'wide'],
31037 ['xs', 'xs', 'wide'],
31038 ['xs', 'xs', 'sm'],
31039 ['xs', 'xs', 'xs'],
31045 ['sm', 'xs', 'xs'],
31049 ['wide', 'xs', 'xs', 'xs'],
31050 ['wide', 'xs', 'xs'],
31063 Roo.each(items, function(item, k){
31065 switch (item.size) {
31076 boxes.push([item]);
31100 var filterPattern = function(box, length)
31108 var pattern = box.slice(0, length);
31112 Roo.each(pattern, function(i){
31113 format.push(i.size);
31116 Roo.each(standard, function(s){
31118 if(String(s) != String(format)){
31127 if(!match && length == 1){
31132 filterPattern(box, length - 1);
31136 queue.push(pattern);
31138 box = box.slice(length, box.length);
31140 filterPattern(box, 4);
31146 Roo.each(boxes, function(box, k){
31152 if(box.length == 1){
31157 filterPattern(box, 4);
31164 var pos = this.el.getBox(true);
31168 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31170 var hit_end = false;
31172 Roo.each(queue, function(box){
31176 Roo.each(box, function(b){
31178 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31188 Roo.each(box, function(b){
31190 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31193 mx = Math.max(mx, b.x);
31197 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31201 Roo.each(box, function(b){
31203 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31217 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31220 /** Sets position of item in DOM
31221 * @param {Element} item
31222 * @param {Number} x - horizontal position
31223 * @param {Number} y - vertical position
31224 * @param {Boolean} isInstant - disables transitions
31226 _processVerticalLayoutQueue : function( queue, isInstant )
31228 var pos = this.el.getBox(true);
31233 for (var i = 0; i < this.cols; i++){
31237 Roo.each(queue, function(box, k){
31239 var col = k % this.cols;
31241 Roo.each(box, function(b,kk){
31243 b.el.position('absolute');
31245 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31246 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31248 if(b.size == 'md-left' || b.size == 'md-right'){
31249 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31250 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31253 b.el.setWidth(width);
31254 b.el.setHeight(height);
31256 b.el.select('iframe',true).setSize(width,height);
31260 for (var i = 0; i < this.cols; i++){
31262 if(maxY[i] < maxY[col]){
31267 col = Math.min(col, i);
31271 x = pos.x + col * (this.colWidth + this.padWidth);
31275 var positions = [];
31277 switch (box.length){
31279 positions = this.getVerticalOneBoxColPositions(x, y, box);
31282 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31285 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31288 positions = this.getVerticalFourBoxColPositions(x, y, box);
31294 Roo.each(box, function(b,kk){
31296 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31298 var sz = b.el.getSize();
31300 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31308 for (var i = 0; i < this.cols; i++){
31309 mY = Math.max(mY, maxY[i]);
31312 this.el.setHeight(mY - pos.y);
31316 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31318 // var pos = this.el.getBox(true);
31321 // var maxX = pos.right;
31323 // var maxHeight = 0;
31325 // Roo.each(items, function(item, k){
31329 // item.el.position('absolute');
31331 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31333 // item.el.setWidth(width);
31335 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31337 // item.el.setHeight(height);
31340 // item.el.setXY([x, y], isInstant ? false : true);
31342 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31345 // y = y + height + this.alternativePadWidth;
31347 // maxHeight = maxHeight + height + this.alternativePadWidth;
31351 // this.el.setHeight(maxHeight);
31355 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31357 var pos = this.el.getBox(true);
31362 var maxX = pos.right;
31364 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31366 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31368 Roo.each(queue, function(box, k){
31370 Roo.each(box, function(b, kk){
31372 b.el.position('absolute');
31374 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31375 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31377 if(b.size == 'md-left' || b.size == 'md-right'){
31378 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31379 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31382 b.el.setWidth(width);
31383 b.el.setHeight(height);
31391 var positions = [];
31393 switch (box.length){
31395 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31398 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31401 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31404 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31410 Roo.each(box, function(b,kk){
31412 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31414 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31422 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31424 Roo.each(eItems, function(b,k){
31426 b.size = (k == 0) ? 'sm' : 'xs';
31427 b.x = (k == 0) ? 2 : 1;
31428 b.y = (k == 0) ? 2 : 1;
31430 b.el.position('absolute');
31432 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31434 b.el.setWidth(width);
31436 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31438 b.el.setHeight(height);
31442 var positions = [];
31445 x : maxX - this.unitWidth * 2 - this.gutter,
31450 x : maxX - this.unitWidth,
31451 y : minY + (this.unitWidth + this.gutter) * 2
31455 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31459 Roo.each(eItems, function(b,k){
31461 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31467 getVerticalOneBoxColPositions : function(x, y, box)
31471 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31473 if(box[0].size == 'md-left'){
31477 if(box[0].size == 'md-right'){
31482 x : x + (this.unitWidth + this.gutter) * rand,
31489 getVerticalTwoBoxColPositions : function(x, y, box)
31493 if(box[0].size == 'xs'){
31497 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31501 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31515 x : x + (this.unitWidth + this.gutter) * 2,
31516 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31523 getVerticalThreeBoxColPositions : function(x, y, box)
31527 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31535 x : x + (this.unitWidth + this.gutter) * 1,
31540 x : x + (this.unitWidth + this.gutter) * 2,
31548 if(box[0].size == 'xs' && box[1].size == 'xs'){
31557 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31561 x : x + (this.unitWidth + this.gutter) * 1,
31575 x : x + (this.unitWidth + this.gutter) * 2,
31580 x : x + (this.unitWidth + this.gutter) * 2,
31581 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31588 getVerticalFourBoxColPositions : function(x, y, box)
31592 if(box[0].size == 'xs'){
31601 y : y + (this.unitHeight + this.gutter) * 1
31606 y : y + (this.unitHeight + this.gutter) * 2
31610 x : x + (this.unitWidth + this.gutter) * 1,
31624 x : x + (this.unitWidth + this.gutter) * 2,
31629 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31630 y : y + (this.unitHeight + this.gutter) * 1
31634 x : x + (this.unitWidth + this.gutter) * 2,
31635 y : y + (this.unitWidth + this.gutter) * 2
31642 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31646 if(box[0].size == 'md-left'){
31648 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31655 if(box[0].size == 'md-right'){
31657 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31658 y : minY + (this.unitWidth + this.gutter) * 1
31664 var rand = Math.floor(Math.random() * (4 - box[0].y));
31667 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31668 y : minY + (this.unitWidth + this.gutter) * rand
31675 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31679 if(box[0].size == 'xs'){
31682 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31687 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31688 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31696 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31701 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31702 y : minY + (this.unitWidth + this.gutter) * 2
31709 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31713 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31716 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31721 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31722 y : minY + (this.unitWidth + this.gutter) * 1
31726 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31727 y : minY + (this.unitWidth + this.gutter) * 2
31734 if(box[0].size == 'xs' && box[1].size == 'xs'){
31737 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31742 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31747 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31748 y : minY + (this.unitWidth + this.gutter) * 1
31756 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31761 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31762 y : minY + (this.unitWidth + this.gutter) * 2
31766 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31767 y : minY + (this.unitWidth + this.gutter) * 2
31774 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31778 if(box[0].size == 'xs'){
31781 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31786 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31791 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),
31796 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31797 y : minY + (this.unitWidth + this.gutter) * 1
31805 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31810 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31811 y : minY + (this.unitWidth + this.gutter) * 2
31815 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31816 y : minY + (this.unitWidth + this.gutter) * 2
31820 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),
31821 y : minY + (this.unitWidth + this.gutter) * 2
31829 * remove a Masonry Brick
31830 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31832 removeBrick : function(brick_id)
31838 for (var i = 0; i<this.bricks.length; i++) {
31839 if (this.bricks[i].id == brick_id) {
31840 this.bricks.splice(i,1);
31841 this.el.dom.removeChild(Roo.get(brick_id).dom);
31848 * adds a Masonry Brick
31849 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31851 addBrick : function(cfg)
31853 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31854 //this.register(cn);
31855 cn.parentId = this.id;
31856 cn.onRender(this.el, null);
31861 * register a Masonry Brick
31862 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31865 register : function(brick)
31867 this.bricks.push(brick);
31868 brick.masonryId = this.id;
31872 * clear all the Masonry Brick
31874 clearAll : function()
31877 //this.getChildContainer().dom.innerHTML = "";
31878 this.el.dom.innerHTML = '';
31881 getSelected : function()
31883 if (!this.selectedBrick) {
31887 return this.selectedBrick;
31891 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31895 * register a Masonry Layout
31896 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31899 register : function(layout)
31901 this.groups[layout.id] = layout;
31904 * fetch a Masonry Layout based on the masonry layout ID
31905 * @param {string} the masonry layout to add
31906 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31909 get: function(layout_id) {
31910 if (typeof(this.groups[layout_id]) == 'undefined') {
31913 return this.groups[layout_id] ;
31925 * http://masonry.desandro.com
31927 * The idea is to render all the bricks based on vertical width...
31929 * The original code extends 'outlayer' - we might need to use that....
31935 * @class Roo.bootstrap.LayoutMasonryAuto
31936 * @extends Roo.bootstrap.Component
31937 * Bootstrap Layout Masonry class
31940 * Create a new Element
31941 * @param {Object} config The config object
31944 Roo.bootstrap.LayoutMasonryAuto = function(config){
31945 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31948 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31951 * @cfg {Boolean} isFitWidth - resize the width..
31953 isFitWidth : false, // options..
31955 * @cfg {Boolean} isOriginLeft = left align?
31957 isOriginLeft : true,
31959 * @cfg {Boolean} isOriginTop = top align?
31961 isOriginTop : false,
31963 * @cfg {Boolean} isLayoutInstant = no animation?
31965 isLayoutInstant : false, // needed?
31967 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31969 isResizingContainer : true,
31971 * @cfg {Number} columnWidth width of the columns
31977 * @cfg {Number} maxCols maximum number of columns
31982 * @cfg {Number} padHeight padding below box..
31988 * @cfg {Boolean} isAutoInitial defalut true
31991 isAutoInitial : true,
31997 initialColumnWidth : 0,
31998 currentSize : null,
32000 colYs : null, // array.
32007 bricks: null, //CompositeElement
32008 cols : 0, // array?
32009 // element : null, // wrapped now this.el
32010 _isLayoutInited : null,
32013 getAutoCreate : function(){
32017 cls: 'blog-masonary-wrapper ' + this.cls,
32019 cls : 'mas-boxes masonary'
32026 getChildContainer: function( )
32028 if (this.boxesEl) {
32029 return this.boxesEl;
32032 this.boxesEl = this.el.select('.mas-boxes').first();
32034 return this.boxesEl;
32038 initEvents : function()
32042 if(this.isAutoInitial){
32043 Roo.log('hook children rendered');
32044 this.on('childrenrendered', function() {
32045 Roo.log('children rendered');
32052 initial : function()
32054 this.reloadItems();
32056 this.currentSize = this.el.getBox(true);
32058 /// was window resize... - let's see if this works..
32059 Roo.EventManager.onWindowResize(this.resize, this);
32061 if(!this.isAutoInitial){
32066 this.layout.defer(500,this);
32069 reloadItems: function()
32071 this.bricks = this.el.select('.masonry-brick', true);
32073 this.bricks.each(function(b) {
32074 //Roo.log(b.getSize());
32075 if (!b.attr('originalwidth')) {
32076 b.attr('originalwidth', b.getSize().width);
32081 Roo.log(this.bricks.elements.length);
32084 resize : function()
32087 var cs = this.el.getBox(true);
32089 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32090 Roo.log("no change in with or X");
32093 this.currentSize = cs;
32097 layout : function()
32100 this._resetLayout();
32101 //this._manageStamps();
32103 // don't animate first layout
32104 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32105 this.layoutItems( isInstant );
32107 // flag for initalized
32108 this._isLayoutInited = true;
32111 layoutItems : function( isInstant )
32113 //var items = this._getItemsForLayout( this.items );
32114 // original code supports filtering layout items.. we just ignore it..
32116 this._layoutItems( this.bricks , isInstant );
32118 this._postLayout();
32120 _layoutItems : function ( items , isInstant)
32122 //this.fireEvent( 'layout', this, items );
32125 if ( !items || !items.elements.length ) {
32126 // no items, emit event with empty array
32131 items.each(function(item) {
32132 Roo.log("layout item");
32134 // get x/y object from method
32135 var position = this._getItemLayoutPosition( item );
32137 position.item = item;
32138 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32139 queue.push( position );
32142 this._processLayoutQueue( queue );
32144 /** Sets position of item in DOM
32145 * @param {Element} item
32146 * @param {Number} x - horizontal position
32147 * @param {Number} y - vertical position
32148 * @param {Boolean} isInstant - disables transitions
32150 _processLayoutQueue : function( queue )
32152 for ( var i=0, len = queue.length; i < len; i++ ) {
32153 var obj = queue[i];
32154 obj.item.position('absolute');
32155 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32161 * Any logic you want to do after each layout,
32162 * i.e. size the container
32164 _postLayout : function()
32166 this.resizeContainer();
32169 resizeContainer : function()
32171 if ( !this.isResizingContainer ) {
32174 var size = this._getContainerSize();
32176 this.el.setSize(size.width,size.height);
32177 this.boxesEl.setSize(size.width,size.height);
32183 _resetLayout : function()
32185 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32186 this.colWidth = this.el.getWidth();
32187 //this.gutter = this.el.getWidth();
32189 this.measureColumns();
32195 this.colYs.push( 0 );
32201 measureColumns : function()
32203 this.getContainerWidth();
32204 // if columnWidth is 0, default to outerWidth of first item
32205 if ( !this.columnWidth ) {
32206 var firstItem = this.bricks.first();
32207 Roo.log(firstItem);
32208 this.columnWidth = this.containerWidth;
32209 if (firstItem && firstItem.attr('originalwidth') ) {
32210 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32212 // columnWidth fall back to item of first element
32213 Roo.log("set column width?");
32214 this.initialColumnWidth = this.columnWidth ;
32216 // if first elem has no width, default to size of container
32221 if (this.initialColumnWidth) {
32222 this.columnWidth = this.initialColumnWidth;
32227 // column width is fixed at the top - however if container width get's smaller we should
32230 // this bit calcs how man columns..
32232 var columnWidth = this.columnWidth += this.gutter;
32234 // calculate columns
32235 var containerWidth = this.containerWidth + this.gutter;
32237 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32238 // fix rounding errors, typically with gutters
32239 var excess = columnWidth - containerWidth % columnWidth;
32242 // if overshoot is less than a pixel, round up, otherwise floor it
32243 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32244 cols = Math[ mathMethod ]( cols );
32245 this.cols = Math.max( cols, 1 );
32246 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32248 // padding positioning..
32249 var totalColWidth = this.cols * this.columnWidth;
32250 var padavail = this.containerWidth - totalColWidth;
32251 // so for 2 columns - we need 3 'pads'
32253 var padNeeded = (1+this.cols) * this.padWidth;
32255 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32257 this.columnWidth += padExtra
32258 //this.padWidth = Math.floor(padavail / ( this.cols));
32260 // adjust colum width so that padding is fixed??
32262 // we have 3 columns ... total = width * 3
32263 // we have X left over... that should be used by
32265 //if (this.expandC) {
32273 getContainerWidth : function()
32275 /* // container is parent if fit width
32276 var container = this.isFitWidth ? this.element.parentNode : this.element;
32277 // check that this.size and size are there
32278 // IE8 triggers resize on body size change, so they might not be
32280 var size = getSize( container ); //FIXME
32281 this.containerWidth = size && size.innerWidth; //FIXME
32284 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32288 _getItemLayoutPosition : function( item ) // what is item?
32290 // we resize the item to our columnWidth..
32292 item.setWidth(this.columnWidth);
32293 item.autoBoxAdjust = false;
32295 var sz = item.getSize();
32297 // how many columns does this brick span
32298 var remainder = this.containerWidth % this.columnWidth;
32300 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32301 // round if off by 1 pixel, otherwise use ceil
32302 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32303 colSpan = Math.min( colSpan, this.cols );
32305 // normally this should be '1' as we dont' currently allow multi width columns..
32307 var colGroup = this._getColGroup( colSpan );
32308 // get the minimum Y value from the columns
32309 var minimumY = Math.min.apply( Math, colGroup );
32310 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32312 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32314 // position the brick
32316 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32317 y: this.currentSize.y + minimumY + this.padHeight
32321 // apply setHeight to necessary columns
32322 var setHeight = minimumY + sz.height + this.padHeight;
32323 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32325 var setSpan = this.cols + 1 - colGroup.length;
32326 for ( var i = 0; i < setSpan; i++ ) {
32327 this.colYs[ shortColIndex + i ] = setHeight ;
32334 * @param {Number} colSpan - number of columns the element spans
32335 * @returns {Array} colGroup
32337 _getColGroup : function( colSpan )
32339 if ( colSpan < 2 ) {
32340 // if brick spans only one column, use all the column Ys
32345 // how many different places could this brick fit horizontally
32346 var groupCount = this.cols + 1 - colSpan;
32347 // for each group potential horizontal position
32348 for ( var i = 0; i < groupCount; i++ ) {
32349 // make an array of colY values for that one group
32350 var groupColYs = this.colYs.slice( i, i + colSpan );
32351 // and get the max value of the array
32352 colGroup[i] = Math.max.apply( Math, groupColYs );
32357 _manageStamp : function( stamp )
32359 var stampSize = stamp.getSize();
32360 var offset = stamp.getBox();
32361 // get the columns that this stamp affects
32362 var firstX = this.isOriginLeft ? offset.x : offset.right;
32363 var lastX = firstX + stampSize.width;
32364 var firstCol = Math.floor( firstX / this.columnWidth );
32365 firstCol = Math.max( 0, firstCol );
32367 var lastCol = Math.floor( lastX / this.columnWidth );
32368 // lastCol should not go over if multiple of columnWidth #425
32369 lastCol -= lastX % this.columnWidth ? 0 : 1;
32370 lastCol = Math.min( this.cols - 1, lastCol );
32372 // set colYs to bottom of the stamp
32373 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32376 for ( var i = firstCol; i <= lastCol; i++ ) {
32377 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32382 _getContainerSize : function()
32384 this.maxY = Math.max.apply( Math, this.colYs );
32389 if ( this.isFitWidth ) {
32390 size.width = this._getContainerFitWidth();
32396 _getContainerFitWidth : function()
32398 var unusedCols = 0;
32399 // count unused columns
32402 if ( this.colYs[i] !== 0 ) {
32407 // fit container to columns that have been used
32408 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32411 needsResizeLayout : function()
32413 var previousWidth = this.containerWidth;
32414 this.getContainerWidth();
32415 return previousWidth !== this.containerWidth;
32430 * @class Roo.bootstrap.MasonryBrick
32431 * @extends Roo.bootstrap.Component
32432 * Bootstrap MasonryBrick class
32435 * Create a new MasonryBrick
32436 * @param {Object} config The config object
32439 Roo.bootstrap.MasonryBrick = function(config){
32441 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32443 Roo.bootstrap.MasonryBrick.register(this);
32449 * When a MasonryBrick is clcik
32450 * @param {Roo.bootstrap.MasonryBrick} this
32451 * @param {Roo.EventObject} e
32457 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32460 * @cfg {String} title
32464 * @cfg {String} html
32468 * @cfg {String} bgimage
32472 * @cfg {String} videourl
32476 * @cfg {String} cls
32480 * @cfg {String} href
32484 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32489 * @cfg {String} placetitle (center|bottom)
32494 * @cfg {Boolean} isFitContainer defalut true
32496 isFitContainer : true,
32499 * @cfg {Boolean} preventDefault defalut false
32501 preventDefault : false,
32504 * @cfg {Boolean} inverse defalut false
32506 maskInverse : false,
32508 getAutoCreate : function()
32510 if(!this.isFitContainer){
32511 return this.getSplitAutoCreate();
32514 var cls = 'masonry-brick masonry-brick-full';
32516 if(this.href.length){
32517 cls += ' masonry-brick-link';
32520 if(this.bgimage.length){
32521 cls += ' masonry-brick-image';
32524 if(this.maskInverse){
32525 cls += ' mask-inverse';
32528 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32529 cls += ' enable-mask';
32533 cls += ' masonry-' + this.size + '-brick';
32536 if(this.placetitle.length){
32538 switch (this.placetitle) {
32540 cls += ' masonry-center-title';
32543 cls += ' masonry-bottom-title';
32550 if(!this.html.length && !this.bgimage.length){
32551 cls += ' masonry-center-title';
32554 if(!this.html.length && this.bgimage.length){
32555 cls += ' masonry-bottom-title';
32560 cls += ' ' + this.cls;
32564 tag: (this.href.length) ? 'a' : 'div',
32569 cls: 'masonry-brick-mask'
32573 cls: 'masonry-brick-paragraph',
32579 if(this.href.length){
32580 cfg.href = this.href;
32583 var cn = cfg.cn[1].cn;
32585 if(this.title.length){
32588 cls: 'masonry-brick-title',
32593 if(this.html.length){
32596 cls: 'masonry-brick-text',
32601 if (!this.title.length && !this.html.length) {
32602 cfg.cn[1].cls += ' hide';
32605 if(this.bgimage.length){
32608 cls: 'masonry-brick-image-view',
32613 if(this.videourl.length){
32614 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32615 // youtube support only?
32618 cls: 'masonry-brick-image-view',
32621 allowfullscreen : true
32629 getSplitAutoCreate : function()
32631 var cls = 'masonry-brick masonry-brick-split';
32633 if(this.href.length){
32634 cls += ' masonry-brick-link';
32637 if(this.bgimage.length){
32638 cls += ' masonry-brick-image';
32642 cls += ' masonry-' + this.size + '-brick';
32645 switch (this.placetitle) {
32647 cls += ' masonry-center-title';
32650 cls += ' masonry-bottom-title';
32653 if(!this.bgimage.length){
32654 cls += ' masonry-center-title';
32657 if(this.bgimage.length){
32658 cls += ' masonry-bottom-title';
32664 cls += ' ' + this.cls;
32668 tag: (this.href.length) ? 'a' : 'div',
32673 cls: 'masonry-brick-split-head',
32677 cls: 'masonry-brick-paragraph',
32684 cls: 'masonry-brick-split-body',
32690 if(this.href.length){
32691 cfg.href = this.href;
32694 if(this.title.length){
32695 cfg.cn[0].cn[0].cn.push({
32697 cls: 'masonry-brick-title',
32702 if(this.html.length){
32703 cfg.cn[1].cn.push({
32705 cls: 'masonry-brick-text',
32710 if(this.bgimage.length){
32711 cfg.cn[0].cn.push({
32713 cls: 'masonry-brick-image-view',
32718 if(this.videourl.length){
32719 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32720 // youtube support only?
32721 cfg.cn[0].cn.cn.push({
32723 cls: 'masonry-brick-image-view',
32726 allowfullscreen : true
32733 initEvents: function()
32735 switch (this.size) {
32768 this.el.on('touchstart', this.onTouchStart, this);
32769 this.el.on('touchmove', this.onTouchMove, this);
32770 this.el.on('touchend', this.onTouchEnd, this);
32771 this.el.on('contextmenu', this.onContextMenu, this);
32773 this.el.on('mouseenter' ,this.enter, this);
32774 this.el.on('mouseleave', this.leave, this);
32775 this.el.on('click', this.onClick, this);
32778 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32779 this.parent().bricks.push(this);
32784 onClick: function(e, el)
32786 var time = this.endTimer - this.startTimer;
32787 // Roo.log(e.preventDefault());
32790 e.preventDefault();
32795 if(!this.preventDefault){
32799 e.preventDefault();
32801 if (this.activeClass != '') {
32802 this.selectBrick();
32805 this.fireEvent('click', this, e);
32808 enter: function(e, el)
32810 e.preventDefault();
32812 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32816 if(this.bgimage.length && this.html.length){
32817 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32821 leave: function(e, el)
32823 e.preventDefault();
32825 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32829 if(this.bgimage.length && this.html.length){
32830 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32834 onTouchStart: function(e, el)
32836 // e.preventDefault();
32838 this.touchmoved = false;
32840 if(!this.isFitContainer){
32844 if(!this.bgimage.length || !this.html.length){
32848 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32850 this.timer = new Date().getTime();
32854 onTouchMove: function(e, el)
32856 this.touchmoved = true;
32859 onContextMenu : function(e,el)
32861 e.preventDefault();
32862 e.stopPropagation();
32866 onTouchEnd: function(e, el)
32868 // e.preventDefault();
32870 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32877 if(!this.bgimage.length || !this.html.length){
32879 if(this.href.length){
32880 window.location.href = this.href;
32886 if(!this.isFitContainer){
32890 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32892 window.location.href = this.href;
32895 //selection on single brick only
32896 selectBrick : function() {
32898 if (!this.parentId) {
32902 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32903 var index = m.selectedBrick.indexOf(this.id);
32906 m.selectedBrick.splice(index,1);
32907 this.el.removeClass(this.activeClass);
32911 for(var i = 0; i < m.selectedBrick.length; i++) {
32912 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32913 b.el.removeClass(b.activeClass);
32916 m.selectedBrick = [];
32918 m.selectedBrick.push(this.id);
32919 this.el.addClass(this.activeClass);
32923 isSelected : function(){
32924 return this.el.hasClass(this.activeClass);
32929 Roo.apply(Roo.bootstrap.MasonryBrick, {
32932 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32934 * register a Masonry Brick
32935 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32938 register : function(brick)
32940 //this.groups[brick.id] = brick;
32941 this.groups.add(brick.id, brick);
32944 * fetch a masonry brick based on the masonry brick ID
32945 * @param {string} the masonry brick to add
32946 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32949 get: function(brick_id)
32951 // if (typeof(this.groups[brick_id]) == 'undefined') {
32954 // return this.groups[brick_id] ;
32956 if(this.groups.key(brick_id)) {
32957 return this.groups.key(brick_id);
32975 * @class Roo.bootstrap.Brick
32976 * @extends Roo.bootstrap.Component
32977 * Bootstrap Brick class
32980 * Create a new Brick
32981 * @param {Object} config The config object
32984 Roo.bootstrap.Brick = function(config){
32985 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32991 * When a Brick is click
32992 * @param {Roo.bootstrap.Brick} this
32993 * @param {Roo.EventObject} e
32999 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33002 * @cfg {String} title
33006 * @cfg {String} html
33010 * @cfg {String} bgimage
33014 * @cfg {String} cls
33018 * @cfg {String} href
33022 * @cfg {String} video
33026 * @cfg {Boolean} square
33030 getAutoCreate : function()
33032 var cls = 'roo-brick';
33034 if(this.href.length){
33035 cls += ' roo-brick-link';
33038 if(this.bgimage.length){
33039 cls += ' roo-brick-image';
33042 if(!this.html.length && !this.bgimage.length){
33043 cls += ' roo-brick-center-title';
33046 if(!this.html.length && this.bgimage.length){
33047 cls += ' roo-brick-bottom-title';
33051 cls += ' ' + this.cls;
33055 tag: (this.href.length) ? 'a' : 'div',
33060 cls: 'roo-brick-paragraph',
33066 if(this.href.length){
33067 cfg.href = this.href;
33070 var cn = cfg.cn[0].cn;
33072 if(this.title.length){
33075 cls: 'roo-brick-title',
33080 if(this.html.length){
33083 cls: 'roo-brick-text',
33090 if(this.bgimage.length){
33093 cls: 'roo-brick-image-view',
33101 initEvents: function()
33103 if(this.title.length || this.html.length){
33104 this.el.on('mouseenter' ,this.enter, this);
33105 this.el.on('mouseleave', this.leave, this);
33108 Roo.EventManager.onWindowResize(this.resize, this);
33110 if(this.bgimage.length){
33111 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33112 this.imageEl.on('load', this.onImageLoad, this);
33119 onImageLoad : function()
33124 resize : function()
33126 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33128 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33130 if(this.bgimage.length){
33131 var image = this.el.select('.roo-brick-image-view', true).first();
33133 image.setWidth(paragraph.getWidth());
33136 image.setHeight(paragraph.getWidth());
33139 this.el.setHeight(image.getHeight());
33140 paragraph.setHeight(image.getHeight());
33146 enter: function(e, el)
33148 e.preventDefault();
33150 if(this.bgimage.length){
33151 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33152 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33156 leave: function(e, el)
33158 e.preventDefault();
33160 if(this.bgimage.length){
33161 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33162 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33177 * @class Roo.bootstrap.NumberField
33178 * @extends Roo.bootstrap.Input
33179 * Bootstrap NumberField class
33185 * Create a new NumberField
33186 * @param {Object} config The config object
33189 Roo.bootstrap.NumberField = function(config){
33190 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33193 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33196 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33198 allowDecimals : true,
33200 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33202 decimalSeparator : ".",
33204 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33206 decimalPrecision : 2,
33208 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33210 allowNegative : true,
33213 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33217 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33219 minValue : Number.NEGATIVE_INFINITY,
33221 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33223 maxValue : Number.MAX_VALUE,
33225 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33227 minText : "The minimum value for this field is {0}",
33229 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33231 maxText : "The maximum value for this field is {0}",
33233 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33234 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33236 nanText : "{0} is not a valid number",
33238 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33240 thousandsDelimiter : false,
33242 * @cfg {String} valueAlign alignment of value
33244 valueAlign : "left",
33246 getAutoCreate : function()
33248 var hiddenInput = {
33252 cls: 'hidden-number-input'
33256 hiddenInput.name = this.name;
33261 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33263 this.name = hiddenInput.name;
33265 if(cfg.cn.length > 0) {
33266 cfg.cn.push(hiddenInput);
33273 initEvents : function()
33275 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33277 var allowed = "0123456789";
33279 if(this.allowDecimals){
33280 allowed += this.decimalSeparator;
33283 if(this.allowNegative){
33287 if(this.thousandsDelimiter) {
33291 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33293 var keyPress = function(e){
33295 var k = e.getKey();
33297 var c = e.getCharCode();
33300 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33301 allowed.indexOf(String.fromCharCode(c)) === -1
33307 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33311 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33316 this.el.on("keypress", keyPress, this);
33319 validateValue : function(value)
33322 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33326 var num = this.parseValue(value);
33329 this.markInvalid(String.format(this.nanText, value));
33333 if(num < this.minValue){
33334 this.markInvalid(String.format(this.minText, this.minValue));
33338 if(num > this.maxValue){
33339 this.markInvalid(String.format(this.maxText, this.maxValue));
33346 getValue : function()
33348 var v = this.hiddenEl().getValue();
33350 return this.fixPrecision(this.parseValue(v));
33353 parseValue : function(value)
33355 if(this.thousandsDelimiter) {
33357 r = new RegExp(",", "g");
33358 value = value.replace(r, "");
33361 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33362 return isNaN(value) ? '' : value;
33365 fixPrecision : function(value)
33367 if(this.thousandsDelimiter) {
33369 r = new RegExp(",", "g");
33370 value = value.replace(r, "");
33373 var nan = isNaN(value);
33375 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33376 return nan ? '' : value;
33378 return parseFloat(value).toFixed(this.decimalPrecision);
33381 setValue : function(v)
33383 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33389 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33391 this.inputEl().dom.value = (v == '') ? '' :
33392 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33394 if(!this.allowZero && v === '0') {
33395 this.hiddenEl().dom.value = '';
33396 this.inputEl().dom.value = '';
33403 decimalPrecisionFcn : function(v)
33405 return Math.floor(v);
33408 beforeBlur : function()
33410 var v = this.parseValue(this.getRawValue());
33412 if(v || v === 0 || v === ''){
33417 hiddenEl : function()
33419 return this.el.select('input.hidden-number-input',true).first();
33431 * @class Roo.bootstrap.DocumentSlider
33432 * @extends Roo.bootstrap.Component
33433 * Bootstrap DocumentSlider class
33436 * Create a new DocumentViewer
33437 * @param {Object} config The config object
33440 Roo.bootstrap.DocumentSlider = function(config){
33441 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33448 * Fire after initEvent
33449 * @param {Roo.bootstrap.DocumentSlider} this
33454 * Fire after update
33455 * @param {Roo.bootstrap.DocumentSlider} this
33461 * @param {Roo.bootstrap.DocumentSlider} this
33467 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33473 getAutoCreate : function()
33477 cls : 'roo-document-slider',
33481 cls : 'roo-document-slider-header',
33485 cls : 'roo-document-slider-header-title'
33491 cls : 'roo-document-slider-body',
33495 cls : 'roo-document-slider-prev',
33499 cls : 'fa fa-chevron-left'
33505 cls : 'roo-document-slider-thumb',
33509 cls : 'roo-document-slider-image'
33515 cls : 'roo-document-slider-next',
33519 cls : 'fa fa-chevron-right'
33531 initEvents : function()
33533 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33534 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33536 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33537 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33539 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33540 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33542 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33543 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33545 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33546 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33548 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33549 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33551 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33552 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33554 this.thumbEl.on('click', this.onClick, this);
33556 this.prevIndicator.on('click', this.prev, this);
33558 this.nextIndicator.on('click', this.next, this);
33562 initial : function()
33564 if(this.files.length){
33565 this.indicator = 1;
33569 this.fireEvent('initial', this);
33572 update : function()
33574 this.imageEl.attr('src', this.files[this.indicator - 1]);
33576 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33578 this.prevIndicator.show();
33580 if(this.indicator == 1){
33581 this.prevIndicator.hide();
33584 this.nextIndicator.show();
33586 if(this.indicator == this.files.length){
33587 this.nextIndicator.hide();
33590 this.thumbEl.scrollTo('top');
33592 this.fireEvent('update', this);
33595 onClick : function(e)
33597 e.preventDefault();
33599 this.fireEvent('click', this);
33604 e.preventDefault();
33606 this.indicator = Math.max(1, this.indicator - 1);
33613 e.preventDefault();
33615 this.indicator = Math.min(this.files.length, this.indicator + 1);
33629 * @class Roo.bootstrap.RadioSet
33630 * @extends Roo.bootstrap.Input
33631 * Bootstrap RadioSet class
33632 * @cfg {String} indicatorpos (left|right) default left
33633 * @cfg {Boolean} inline (true|false) inline the element (default true)
33634 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33636 * Create a new RadioSet
33637 * @param {Object} config The config object
33640 Roo.bootstrap.RadioSet = function(config){
33642 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33646 Roo.bootstrap.RadioSet.register(this);
33651 * Fires when the element is checked or unchecked.
33652 * @param {Roo.bootstrap.RadioSet} this This radio
33653 * @param {Roo.bootstrap.Radio} item The checked item
33658 * Fires when the element is click.
33659 * @param {Roo.bootstrap.RadioSet} this This radio set
33660 * @param {Roo.bootstrap.Radio} item The checked item
33661 * @param {Roo.EventObject} e The event object
33668 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33676 indicatorpos : 'left',
33678 getAutoCreate : function()
33682 cls : 'roo-radio-set-label',
33686 html : this.fieldLabel
33691 if(this.indicatorpos == 'left'){
33694 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33695 tooltip : 'This field is required'
33700 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33701 tooltip : 'This field is required'
33707 cls : 'roo-radio-set-items'
33710 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33712 if (align === 'left' && this.fieldLabel.length) {
33715 cls : "roo-radio-set-right",
33721 if(this.labelWidth > 12){
33722 label.style = "width: " + this.labelWidth + 'px';
33725 if(this.labelWidth < 13 && this.labelmd == 0){
33726 this.labelmd = this.labelWidth;
33729 if(this.labellg > 0){
33730 label.cls += ' col-lg-' + this.labellg;
33731 items.cls += ' col-lg-' + (12 - this.labellg);
33734 if(this.labelmd > 0){
33735 label.cls += ' col-md-' + this.labelmd;
33736 items.cls += ' col-md-' + (12 - this.labelmd);
33739 if(this.labelsm > 0){
33740 label.cls += ' col-sm-' + this.labelsm;
33741 items.cls += ' col-sm-' + (12 - this.labelsm);
33744 if(this.labelxs > 0){
33745 label.cls += ' col-xs-' + this.labelxs;
33746 items.cls += ' col-xs-' + (12 - this.labelxs);
33752 cls : 'roo-radio-set',
33756 cls : 'roo-radio-set-input',
33759 value : this.value ? this.value : ''
33766 if(this.weight.length){
33767 cfg.cls += ' roo-radio-' + this.weight;
33771 cfg.cls += ' roo-radio-set-inline';
33775 ['xs','sm','md','lg'].map(function(size){
33776 if (settings[size]) {
33777 cfg.cls += ' col-' + size + '-' + settings[size];
33785 initEvents : function()
33787 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33788 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33790 if(!this.fieldLabel.length){
33791 this.labelEl.hide();
33794 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33795 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33797 this.indicator = this.indicatorEl();
33799 if(this.indicator){
33800 this.indicator.addClass('invisible');
33803 this.originalValue = this.getValue();
33807 inputEl: function ()
33809 return this.el.select('.roo-radio-set-input', true).first();
33812 getChildContainer : function()
33814 return this.itemsEl;
33817 register : function(item)
33819 this.radioes.push(item);
33823 validate : function()
33825 if(this.getVisibilityEl().hasClass('hidden')){
33831 Roo.each(this.radioes, function(i){
33840 if(this.allowBlank) {
33844 if(this.disabled || valid){
33849 this.markInvalid();
33854 markValid : function()
33856 if(this.labelEl.isVisible(true)){
33857 this.indicatorEl().removeClass('visible');
33858 this.indicatorEl().addClass('invisible');
33861 this.el.removeClass([this.invalidClass, this.validClass]);
33862 this.el.addClass(this.validClass);
33864 this.fireEvent('valid', this);
33867 markInvalid : function(msg)
33869 if(this.allowBlank || this.disabled){
33873 if(this.labelEl.isVisible(true)){
33874 this.indicatorEl().removeClass('invisible');
33875 this.indicatorEl().addClass('visible');
33878 this.el.removeClass([this.invalidClass, this.validClass]);
33879 this.el.addClass(this.invalidClass);
33881 this.fireEvent('invalid', this, msg);
33885 setValue : function(v, suppressEvent)
33887 if(this.value === v){
33894 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33897 Roo.each(this.radioes, function(i){
33899 i.el.removeClass('checked');
33902 Roo.each(this.radioes, function(i){
33904 if(i.value === v || i.value.toString() === v.toString()){
33906 i.el.addClass('checked');
33908 if(suppressEvent !== true){
33909 this.fireEvent('check', this, i);
33920 clearInvalid : function(){
33922 if(!this.el || this.preventMark){
33926 this.el.removeClass([this.invalidClass]);
33928 this.fireEvent('valid', this);
33933 Roo.apply(Roo.bootstrap.RadioSet, {
33937 register : function(set)
33939 this.groups[set.name] = set;
33942 get: function(name)
33944 if (typeof(this.groups[name]) == 'undefined') {
33948 return this.groups[name] ;
33954 * Ext JS Library 1.1.1
33955 * Copyright(c) 2006-2007, Ext JS, LLC.
33957 * Originally Released Under LGPL - original licence link has changed is not relivant.
33960 * <script type="text/javascript">
33965 * @class Roo.bootstrap.SplitBar
33966 * @extends Roo.util.Observable
33967 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33971 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33972 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33973 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33974 split.minSize = 100;
33975 split.maxSize = 600;
33976 split.animate = true;
33977 split.on('moved', splitterMoved);
33980 * Create a new SplitBar
33981 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33982 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33983 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33984 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33985 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33986 position of the SplitBar).
33988 Roo.bootstrap.SplitBar = function(cfg){
33993 // dragElement : elm
33994 // resizingElement: el,
33996 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33997 // placement : Roo.bootstrap.SplitBar.LEFT ,
33998 // existingProxy ???
34001 this.el = Roo.get(cfg.dragElement, true);
34002 this.el.dom.unselectable = "on";
34004 this.resizingEl = Roo.get(cfg.resizingElement, true);
34008 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34009 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34012 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34015 * The minimum size of the resizing element. (Defaults to 0)
34021 * The maximum size of the resizing element. (Defaults to 2000)
34024 this.maxSize = 2000;
34027 * Whether to animate the transition to the new size
34030 this.animate = false;
34033 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34036 this.useShim = false;
34041 if(!cfg.existingProxy){
34043 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34045 this.proxy = Roo.get(cfg.existingProxy).dom;
34048 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34051 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34054 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34057 this.dragSpecs = {};
34060 * @private The adapter to use to positon and resize elements
34062 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34063 this.adapter.init(this);
34065 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34067 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34068 this.el.addClass("roo-splitbar-h");
34071 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34072 this.el.addClass("roo-splitbar-v");
34078 * Fires when the splitter is moved (alias for {@link #event-moved})
34079 * @param {Roo.bootstrap.SplitBar} this
34080 * @param {Number} newSize the new width or height
34085 * Fires when the splitter is moved
34086 * @param {Roo.bootstrap.SplitBar} this
34087 * @param {Number} newSize the new width or height
34091 * @event beforeresize
34092 * Fires before the splitter is dragged
34093 * @param {Roo.bootstrap.SplitBar} this
34095 "beforeresize" : true,
34097 "beforeapply" : true
34100 Roo.util.Observable.call(this);
34103 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34104 onStartProxyDrag : function(x, y){
34105 this.fireEvent("beforeresize", this);
34107 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34109 o.enableDisplayMode("block");
34110 // all splitbars share the same overlay
34111 Roo.bootstrap.SplitBar.prototype.overlay = o;
34113 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34114 this.overlay.show();
34115 Roo.get(this.proxy).setDisplayed("block");
34116 var size = this.adapter.getElementSize(this);
34117 this.activeMinSize = this.getMinimumSize();;
34118 this.activeMaxSize = this.getMaximumSize();;
34119 var c1 = size - this.activeMinSize;
34120 var c2 = Math.max(this.activeMaxSize - size, 0);
34121 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34122 this.dd.resetConstraints();
34123 this.dd.setXConstraint(
34124 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34125 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34127 this.dd.setYConstraint(0, 0);
34129 this.dd.resetConstraints();
34130 this.dd.setXConstraint(0, 0);
34131 this.dd.setYConstraint(
34132 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34133 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34136 this.dragSpecs.startSize = size;
34137 this.dragSpecs.startPoint = [x, y];
34138 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34142 * @private Called after the drag operation by the DDProxy
34144 onEndProxyDrag : function(e){
34145 Roo.get(this.proxy).setDisplayed(false);
34146 var endPoint = Roo.lib.Event.getXY(e);
34148 this.overlay.hide();
34151 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34152 newSize = this.dragSpecs.startSize +
34153 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34154 endPoint[0] - this.dragSpecs.startPoint[0] :
34155 this.dragSpecs.startPoint[0] - endPoint[0]
34158 newSize = this.dragSpecs.startSize +
34159 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34160 endPoint[1] - this.dragSpecs.startPoint[1] :
34161 this.dragSpecs.startPoint[1] - endPoint[1]
34164 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34165 if(newSize != this.dragSpecs.startSize){
34166 if(this.fireEvent('beforeapply', this, newSize) !== false){
34167 this.adapter.setElementSize(this, newSize);
34168 this.fireEvent("moved", this, newSize);
34169 this.fireEvent("resize", this, newSize);
34175 * Get the adapter this SplitBar uses
34176 * @return The adapter object
34178 getAdapter : function(){
34179 return this.adapter;
34183 * Set the adapter this SplitBar uses
34184 * @param {Object} adapter A SplitBar adapter object
34186 setAdapter : function(adapter){
34187 this.adapter = adapter;
34188 this.adapter.init(this);
34192 * Gets the minimum size for the resizing element
34193 * @return {Number} The minimum size
34195 getMinimumSize : function(){
34196 return this.minSize;
34200 * Sets the minimum size for the resizing element
34201 * @param {Number} minSize The minimum size
34203 setMinimumSize : function(minSize){
34204 this.minSize = minSize;
34208 * Gets the maximum size for the resizing element
34209 * @return {Number} The maximum size
34211 getMaximumSize : function(){
34212 return this.maxSize;
34216 * Sets the maximum size for the resizing element
34217 * @param {Number} maxSize The maximum size
34219 setMaximumSize : function(maxSize){
34220 this.maxSize = maxSize;
34224 * Sets the initialize size for the resizing element
34225 * @param {Number} size The initial size
34227 setCurrentSize : function(size){
34228 var oldAnimate = this.animate;
34229 this.animate = false;
34230 this.adapter.setElementSize(this, size);
34231 this.animate = oldAnimate;
34235 * Destroy this splitbar.
34236 * @param {Boolean} removeEl True to remove the element
34238 destroy : function(removeEl){
34240 this.shim.remove();
34243 this.proxy.parentNode.removeChild(this.proxy);
34251 * @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.
34253 Roo.bootstrap.SplitBar.createProxy = function(dir){
34254 var proxy = new Roo.Element(document.createElement("div"));
34255 proxy.unselectable();
34256 var cls = 'roo-splitbar-proxy';
34257 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34258 document.body.appendChild(proxy.dom);
34263 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34264 * Default Adapter. It assumes the splitter and resizing element are not positioned
34265 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34267 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34270 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34271 // do nothing for now
34272 init : function(s){
34276 * Called before drag operations to get the current size of the resizing element.
34277 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34279 getElementSize : function(s){
34280 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34281 return s.resizingEl.getWidth();
34283 return s.resizingEl.getHeight();
34288 * Called after drag operations to set the size of the resizing element.
34289 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34290 * @param {Number} newSize The new size to set
34291 * @param {Function} onComplete A function to be invoked when resizing is complete
34293 setElementSize : function(s, newSize, onComplete){
34294 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34296 s.resizingEl.setWidth(newSize);
34298 onComplete(s, newSize);
34301 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34306 s.resizingEl.setHeight(newSize);
34308 onComplete(s, newSize);
34311 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34318 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34319 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34320 * Adapter that moves the splitter element to align with the resized sizing element.
34321 * Used with an absolute positioned SplitBar.
34322 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34323 * document.body, make sure you assign an id to the body element.
34325 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34326 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34327 this.container = Roo.get(container);
34330 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34331 init : function(s){
34332 this.basic.init(s);
34335 getElementSize : function(s){
34336 return this.basic.getElementSize(s);
34339 setElementSize : function(s, newSize, onComplete){
34340 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34343 moveSplitter : function(s){
34344 var yes = Roo.bootstrap.SplitBar;
34345 switch(s.placement){
34347 s.el.setX(s.resizingEl.getRight());
34350 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34353 s.el.setY(s.resizingEl.getBottom());
34356 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34363 * Orientation constant - Create a vertical SplitBar
34367 Roo.bootstrap.SplitBar.VERTICAL = 1;
34370 * Orientation constant - Create a horizontal SplitBar
34374 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34377 * Placement constant - The resizing element is to the left of the splitter element
34381 Roo.bootstrap.SplitBar.LEFT = 1;
34384 * Placement constant - The resizing element is to the right of the splitter element
34388 Roo.bootstrap.SplitBar.RIGHT = 2;
34391 * Placement constant - The resizing element is positioned above the splitter element
34395 Roo.bootstrap.SplitBar.TOP = 3;
34398 * Placement constant - The resizing element is positioned under splitter element
34402 Roo.bootstrap.SplitBar.BOTTOM = 4;
34403 Roo.namespace("Roo.bootstrap.layout");/*
34405 * Ext JS Library 1.1.1
34406 * Copyright(c) 2006-2007, Ext JS, LLC.
34408 * Originally Released Under LGPL - original licence link has changed is not relivant.
34411 * <script type="text/javascript">
34415 * @class Roo.bootstrap.layout.Manager
34416 * @extends Roo.bootstrap.Component
34417 * Base class for layout managers.
34419 Roo.bootstrap.layout.Manager = function(config)
34421 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34427 /** false to disable window resize monitoring @type Boolean */
34428 this.monitorWindowResize = true;
34433 * Fires when a layout is performed.
34434 * @param {Roo.LayoutManager} this
34438 * @event regionresized
34439 * Fires when the user resizes a region.
34440 * @param {Roo.LayoutRegion} region The resized region
34441 * @param {Number} newSize The new size (width for east/west, height for north/south)
34443 "regionresized" : true,
34445 * @event regioncollapsed
34446 * Fires when a region is collapsed.
34447 * @param {Roo.LayoutRegion} region The collapsed region
34449 "regioncollapsed" : true,
34451 * @event regionexpanded
34452 * Fires when a region is expanded.
34453 * @param {Roo.LayoutRegion} region The expanded region
34455 "regionexpanded" : true
34457 this.updating = false;
34460 this.el = Roo.get(config.el);
34466 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34471 monitorWindowResize : true,
34477 onRender : function(ct, position)
34480 this.el = Roo.get(ct);
34483 //this.fireEvent('render',this);
34487 initEvents: function()
34491 // ie scrollbar fix
34492 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34493 document.body.scroll = "no";
34494 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34495 this.el.position('relative');
34497 this.id = this.el.id;
34498 this.el.addClass("roo-layout-container");
34499 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34500 if(this.el.dom != document.body ) {
34501 this.el.on('resize', this.layout,this);
34502 this.el.on('show', this.layout,this);
34508 * Returns true if this layout is currently being updated
34509 * @return {Boolean}
34511 isUpdating : function(){
34512 return this.updating;
34516 * Suspend the LayoutManager from doing auto-layouts while
34517 * making multiple add or remove calls
34519 beginUpdate : function(){
34520 this.updating = true;
34524 * Restore auto-layouts and optionally disable the manager from performing a layout
34525 * @param {Boolean} noLayout true to disable a layout update
34527 endUpdate : function(noLayout){
34528 this.updating = false;
34534 layout: function(){
34538 onRegionResized : function(region, newSize){
34539 this.fireEvent("regionresized", region, newSize);
34543 onRegionCollapsed : function(region){
34544 this.fireEvent("regioncollapsed", region);
34547 onRegionExpanded : function(region){
34548 this.fireEvent("regionexpanded", region);
34552 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34553 * performs box-model adjustments.
34554 * @return {Object} The size as an object {width: (the width), height: (the height)}
34556 getViewSize : function()
34559 if(this.el.dom != document.body){
34560 size = this.el.getSize();
34562 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34564 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34565 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34570 * Returns the Element this layout is bound to.
34571 * @return {Roo.Element}
34573 getEl : function(){
34578 * Returns the specified region.
34579 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34580 * @return {Roo.LayoutRegion}
34582 getRegion : function(target){
34583 return this.regions[target.toLowerCase()];
34586 onWindowResize : function(){
34587 if(this.monitorWindowResize){
34594 * Ext JS Library 1.1.1
34595 * Copyright(c) 2006-2007, Ext JS, LLC.
34597 * Originally Released Under LGPL - original licence link has changed is not relivant.
34600 * <script type="text/javascript">
34603 * @class Roo.bootstrap.layout.Border
34604 * @extends Roo.bootstrap.layout.Manager
34605 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34606 * please see: examples/bootstrap/nested.html<br><br>
34608 <b>The container the layout is rendered into can be either the body element or any other element.
34609 If it is not the body element, the container needs to either be an absolute positioned element,
34610 or you will need to add "position:relative" to the css of the container. You will also need to specify
34611 the container size if it is not the body element.</b>
34614 * Create a new Border
34615 * @param {Object} config Configuration options
34617 Roo.bootstrap.layout.Border = function(config){
34618 config = config || {};
34619 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34623 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34624 if(config[region]){
34625 config[region].region = region;
34626 this.addRegion(config[region]);
34632 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34634 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34636 * Creates and adds a new region if it doesn't already exist.
34637 * @param {String} target The target region key (north, south, east, west or center).
34638 * @param {Object} config The regions config object
34639 * @return {BorderLayoutRegion} The new region
34641 addRegion : function(config)
34643 if(!this.regions[config.region]){
34644 var r = this.factory(config);
34645 this.bindRegion(r);
34647 return this.regions[config.region];
34651 bindRegion : function(r){
34652 this.regions[r.config.region] = r;
34654 r.on("visibilitychange", this.layout, this);
34655 r.on("paneladded", this.layout, this);
34656 r.on("panelremoved", this.layout, this);
34657 r.on("invalidated", this.layout, this);
34658 r.on("resized", this.onRegionResized, this);
34659 r.on("collapsed", this.onRegionCollapsed, this);
34660 r.on("expanded", this.onRegionExpanded, this);
34664 * Performs a layout update.
34666 layout : function()
34668 if(this.updating) {
34672 // render all the rebions if they have not been done alreayd?
34673 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34674 if(this.regions[region] && !this.regions[region].bodyEl){
34675 this.regions[region].onRender(this.el)
34679 var size = this.getViewSize();
34680 var w = size.width;
34681 var h = size.height;
34686 //var x = 0, y = 0;
34688 var rs = this.regions;
34689 var north = rs["north"];
34690 var south = rs["south"];
34691 var west = rs["west"];
34692 var east = rs["east"];
34693 var center = rs["center"];
34694 //if(this.hideOnLayout){ // not supported anymore
34695 //c.el.setStyle("display", "none");
34697 if(north && north.isVisible()){
34698 var b = north.getBox();
34699 var m = north.getMargins();
34700 b.width = w - (m.left+m.right);
34703 centerY = b.height + b.y + m.bottom;
34704 centerH -= centerY;
34705 north.updateBox(this.safeBox(b));
34707 if(south && south.isVisible()){
34708 var b = south.getBox();
34709 var m = south.getMargins();
34710 b.width = w - (m.left+m.right);
34712 var totalHeight = (b.height + m.top + m.bottom);
34713 b.y = h - totalHeight + m.top;
34714 centerH -= totalHeight;
34715 south.updateBox(this.safeBox(b));
34717 if(west && west.isVisible()){
34718 var b = west.getBox();
34719 var m = west.getMargins();
34720 b.height = centerH - (m.top+m.bottom);
34722 b.y = centerY + m.top;
34723 var totalWidth = (b.width + m.left + m.right);
34724 centerX += totalWidth;
34725 centerW -= totalWidth;
34726 west.updateBox(this.safeBox(b));
34728 if(east && east.isVisible()){
34729 var b = east.getBox();
34730 var m = east.getMargins();
34731 b.height = centerH - (m.top+m.bottom);
34732 var totalWidth = (b.width + m.left + m.right);
34733 b.x = w - totalWidth + m.left;
34734 b.y = centerY + m.top;
34735 centerW -= totalWidth;
34736 east.updateBox(this.safeBox(b));
34739 var m = center.getMargins();
34741 x: centerX + m.left,
34742 y: centerY + m.top,
34743 width: centerW - (m.left+m.right),
34744 height: centerH - (m.top+m.bottom)
34746 //if(this.hideOnLayout){
34747 //center.el.setStyle("display", "block");
34749 center.updateBox(this.safeBox(centerBox));
34752 this.fireEvent("layout", this);
34756 safeBox : function(box){
34757 box.width = Math.max(0, box.width);
34758 box.height = Math.max(0, box.height);
34763 * Adds a ContentPanel (or subclass) to this layout.
34764 * @param {String} target The target region key (north, south, east, west or center).
34765 * @param {Roo.ContentPanel} panel The panel to add
34766 * @return {Roo.ContentPanel} The added panel
34768 add : function(target, panel){
34770 target = target.toLowerCase();
34771 return this.regions[target].add(panel);
34775 * Remove a ContentPanel (or subclass) to this layout.
34776 * @param {String} target The target region key (north, south, east, west or center).
34777 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34778 * @return {Roo.ContentPanel} The removed panel
34780 remove : function(target, panel){
34781 target = target.toLowerCase();
34782 return this.regions[target].remove(panel);
34786 * Searches all regions for a panel with the specified id
34787 * @param {String} panelId
34788 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34790 findPanel : function(panelId){
34791 var rs = this.regions;
34792 for(var target in rs){
34793 if(typeof rs[target] != "function"){
34794 var p = rs[target].getPanel(panelId);
34804 * Searches all regions for a panel with the specified id and activates (shows) it.
34805 * @param {String/ContentPanel} panelId The panels id or the panel itself
34806 * @return {Roo.ContentPanel} The shown panel or null
34808 showPanel : function(panelId) {
34809 var rs = this.regions;
34810 for(var target in rs){
34811 var r = rs[target];
34812 if(typeof r != "function"){
34813 if(r.hasPanel(panelId)){
34814 return r.showPanel(panelId);
34822 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34823 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34826 restoreState : function(provider){
34828 provider = Roo.state.Manager;
34830 var sm = new Roo.LayoutStateManager();
34831 sm.init(this, provider);
34837 * Adds a xtype elements to the layout.
34841 xtype : 'ContentPanel',
34848 xtype : 'NestedLayoutPanel',
34854 items : [ ... list of content panels or nested layout panels.. ]
34858 * @param {Object} cfg Xtype definition of item to add.
34860 addxtype : function(cfg)
34862 // basically accepts a pannel...
34863 // can accept a layout region..!?!?
34864 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34867 // theory? children can only be panels??
34869 //if (!cfg.xtype.match(/Panel$/)) {
34874 if (typeof(cfg.region) == 'undefined') {
34875 Roo.log("Failed to add Panel, region was not set");
34879 var region = cfg.region;
34885 xitems = cfg.items;
34892 case 'Content': // ContentPanel (el, cfg)
34893 case 'Scroll': // ContentPanel (el, cfg)
34895 cfg.autoCreate = true;
34896 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34898 // var el = this.el.createChild();
34899 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34902 this.add(region, ret);
34906 case 'TreePanel': // our new panel!
34907 cfg.el = this.el.createChild();
34908 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34909 this.add(region, ret);
34914 // create a new Layout (which is a Border Layout...
34916 var clayout = cfg.layout;
34917 clayout.el = this.el.createChild();
34918 clayout.items = clayout.items || [];
34922 // replace this exitems with the clayout ones..
34923 xitems = clayout.items;
34925 // force background off if it's in center...
34926 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34927 cfg.background = false;
34929 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34932 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34933 //console.log('adding nested layout panel ' + cfg.toSource());
34934 this.add(region, ret);
34935 nb = {}; /// find first...
34940 // needs grid and region
34942 //var el = this.getRegion(region).el.createChild();
34944 *var el = this.el.createChild();
34945 // create the grid first...
34946 cfg.grid.container = el;
34947 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34950 if (region == 'center' && this.active ) {
34951 cfg.background = false;
34954 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34956 this.add(region, ret);
34958 if (cfg.background) {
34959 // render grid on panel activation (if panel background)
34960 ret.on('activate', function(gp) {
34961 if (!gp.grid.rendered) {
34962 // gp.grid.render(el);
34966 // cfg.grid.render(el);
34972 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34973 // it was the old xcomponent building that caused this before.
34974 // espeically if border is the top element in the tree.
34984 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34986 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34987 this.add(region, ret);
34991 throw "Can not add '" + cfg.xtype + "' to Border";
34997 this.beginUpdate();
35001 Roo.each(xitems, function(i) {
35002 region = nb && i.region ? i.region : false;
35004 var add = ret.addxtype(i);
35007 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35008 if (!i.background) {
35009 abn[region] = nb[region] ;
35016 // make the last non-background panel active..
35017 //if (nb) { Roo.log(abn); }
35020 for(var r in abn) {
35021 region = this.getRegion(r);
35023 // tried using nb[r], but it does not work..
35025 region.showPanel(abn[r]);
35036 factory : function(cfg)
35039 var validRegions = Roo.bootstrap.layout.Border.regions;
35041 var target = cfg.region;
35044 var r = Roo.bootstrap.layout;
35048 return new r.North(cfg);
35050 return new r.South(cfg);
35052 return new r.East(cfg);
35054 return new r.West(cfg);
35056 return new r.Center(cfg);
35058 throw 'Layout region "'+target+'" not supported.';
35065 * Ext JS Library 1.1.1
35066 * Copyright(c) 2006-2007, Ext JS, LLC.
35068 * Originally Released Under LGPL - original licence link has changed is not relivant.
35071 * <script type="text/javascript">
35075 * @class Roo.bootstrap.layout.Basic
35076 * @extends Roo.util.Observable
35077 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35078 * and does not have a titlebar, tabs or any other features. All it does is size and position
35079 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35080 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35081 * @cfg {string} region the region that it inhabits..
35082 * @cfg {bool} skipConfig skip config?
35086 Roo.bootstrap.layout.Basic = function(config){
35088 this.mgr = config.mgr;
35090 this.position = config.region;
35092 var skipConfig = config.skipConfig;
35096 * @scope Roo.BasicLayoutRegion
35100 * @event beforeremove
35101 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35102 * @param {Roo.LayoutRegion} this
35103 * @param {Roo.ContentPanel} panel The panel
35104 * @param {Object} e The cancel event object
35106 "beforeremove" : true,
35108 * @event invalidated
35109 * Fires when the layout for this region is changed.
35110 * @param {Roo.LayoutRegion} this
35112 "invalidated" : true,
35114 * @event visibilitychange
35115 * Fires when this region is shown or hidden
35116 * @param {Roo.LayoutRegion} this
35117 * @param {Boolean} visibility true or false
35119 "visibilitychange" : true,
35121 * @event paneladded
35122 * Fires when a panel is added.
35123 * @param {Roo.LayoutRegion} this
35124 * @param {Roo.ContentPanel} panel The panel
35126 "paneladded" : true,
35128 * @event panelremoved
35129 * Fires when a panel is removed.
35130 * @param {Roo.LayoutRegion} this
35131 * @param {Roo.ContentPanel} panel The panel
35133 "panelremoved" : true,
35135 * @event beforecollapse
35136 * Fires when this region before collapse.
35137 * @param {Roo.LayoutRegion} this
35139 "beforecollapse" : true,
35142 * Fires when this region is collapsed.
35143 * @param {Roo.LayoutRegion} this
35145 "collapsed" : true,
35148 * Fires when this region is expanded.
35149 * @param {Roo.LayoutRegion} this
35154 * Fires when this region is slid into view.
35155 * @param {Roo.LayoutRegion} this
35157 "slideshow" : true,
35160 * Fires when this region slides out of view.
35161 * @param {Roo.LayoutRegion} this
35163 "slidehide" : true,
35165 * @event panelactivated
35166 * Fires when a panel is activated.
35167 * @param {Roo.LayoutRegion} this
35168 * @param {Roo.ContentPanel} panel The activated panel
35170 "panelactivated" : true,
35173 * Fires when the user resizes this region.
35174 * @param {Roo.LayoutRegion} this
35175 * @param {Number} newSize The new size (width for east/west, height for north/south)
35179 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35180 this.panels = new Roo.util.MixedCollection();
35181 this.panels.getKey = this.getPanelId.createDelegate(this);
35183 this.activePanel = null;
35184 // ensure listeners are added...
35186 if (config.listeners || config.events) {
35187 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35188 listeners : config.listeners || {},
35189 events : config.events || {}
35193 if(skipConfig !== true){
35194 this.applyConfig(config);
35198 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35200 getPanelId : function(p){
35204 applyConfig : function(config){
35205 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35206 this.config = config;
35211 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35212 * the width, for horizontal (north, south) the height.
35213 * @param {Number} newSize The new width or height
35215 resizeTo : function(newSize){
35216 var el = this.el ? this.el :
35217 (this.activePanel ? this.activePanel.getEl() : null);
35219 switch(this.position){
35222 el.setWidth(newSize);
35223 this.fireEvent("resized", this, newSize);
35227 el.setHeight(newSize);
35228 this.fireEvent("resized", this, newSize);
35234 getBox : function(){
35235 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35238 getMargins : function(){
35239 return this.margins;
35242 updateBox : function(box){
35244 var el = this.activePanel.getEl();
35245 el.dom.style.left = box.x + "px";
35246 el.dom.style.top = box.y + "px";
35247 this.activePanel.setSize(box.width, box.height);
35251 * Returns the container element for this region.
35252 * @return {Roo.Element}
35254 getEl : function(){
35255 return this.activePanel;
35259 * Returns true if this region is currently visible.
35260 * @return {Boolean}
35262 isVisible : function(){
35263 return this.activePanel ? true : false;
35266 setActivePanel : function(panel){
35267 panel = this.getPanel(panel);
35268 if(this.activePanel && this.activePanel != panel){
35269 this.activePanel.setActiveState(false);
35270 this.activePanel.getEl().setLeftTop(-10000,-10000);
35272 this.activePanel = panel;
35273 panel.setActiveState(true);
35275 panel.setSize(this.box.width, this.box.height);
35277 this.fireEvent("panelactivated", this, panel);
35278 this.fireEvent("invalidated");
35282 * Show the specified panel.
35283 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35284 * @return {Roo.ContentPanel} The shown panel or null
35286 showPanel : function(panel){
35287 panel = this.getPanel(panel);
35289 this.setActivePanel(panel);
35295 * Get the active panel for this region.
35296 * @return {Roo.ContentPanel} The active panel or null
35298 getActivePanel : function(){
35299 return this.activePanel;
35303 * Add the passed ContentPanel(s)
35304 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35305 * @return {Roo.ContentPanel} The panel added (if only one was added)
35307 add : function(panel){
35308 if(arguments.length > 1){
35309 for(var i = 0, len = arguments.length; i < len; i++) {
35310 this.add(arguments[i]);
35314 if(this.hasPanel(panel)){
35315 this.showPanel(panel);
35318 var el = panel.getEl();
35319 if(el.dom.parentNode != this.mgr.el.dom){
35320 this.mgr.el.dom.appendChild(el.dom);
35322 if(panel.setRegion){
35323 panel.setRegion(this);
35325 this.panels.add(panel);
35326 el.setStyle("position", "absolute");
35327 if(!panel.background){
35328 this.setActivePanel(panel);
35329 if(this.config.initialSize && this.panels.getCount()==1){
35330 this.resizeTo(this.config.initialSize);
35333 this.fireEvent("paneladded", this, panel);
35338 * Returns true if the panel is in this region.
35339 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35340 * @return {Boolean}
35342 hasPanel : function(panel){
35343 if(typeof panel == "object"){ // must be panel obj
35344 panel = panel.getId();
35346 return this.getPanel(panel) ? true : false;
35350 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35351 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35352 * @param {Boolean} preservePanel Overrides the config preservePanel option
35353 * @return {Roo.ContentPanel} The panel that was removed
35355 remove : function(panel, preservePanel){
35356 panel = this.getPanel(panel);
35361 this.fireEvent("beforeremove", this, panel, e);
35362 if(e.cancel === true){
35365 var panelId = panel.getId();
35366 this.panels.removeKey(panelId);
35371 * Returns the panel specified or null if it's not in this region.
35372 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35373 * @return {Roo.ContentPanel}
35375 getPanel : function(id){
35376 if(typeof id == "object"){ // must be panel obj
35379 return this.panels.get(id);
35383 * Returns this regions position (north/south/east/west/center).
35386 getPosition: function(){
35387 return this.position;
35391 * Ext JS Library 1.1.1
35392 * Copyright(c) 2006-2007, Ext JS, LLC.
35394 * Originally Released Under LGPL - original licence link has changed is not relivant.
35397 * <script type="text/javascript">
35401 * @class Roo.bootstrap.layout.Region
35402 * @extends Roo.bootstrap.layout.Basic
35403 * This class represents a region in a layout manager.
35405 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35406 * @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})
35407 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35408 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35409 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35410 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35411 * @cfg {String} title The title for the region (overrides panel titles)
35412 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35413 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35414 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35415 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35416 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35417 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35418 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35419 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35420 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35421 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35423 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35424 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35425 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35426 * @cfg {Number} width For East/West panels
35427 * @cfg {Number} height For North/South panels
35428 * @cfg {Boolean} split To show the splitter
35429 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35431 * @cfg {string} cls Extra CSS classes to add to region
35433 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35434 * @cfg {string} region the region that it inhabits..
35437 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35438 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35440 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35441 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35442 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35444 Roo.bootstrap.layout.Region = function(config)
35446 this.applyConfig(config);
35448 var mgr = config.mgr;
35449 var pos = config.region;
35450 config.skipConfig = true;
35451 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35454 this.onRender(mgr.el);
35457 this.visible = true;
35458 this.collapsed = false;
35459 this.unrendered_panels = [];
35462 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35464 position: '', // set by wrapper (eg. north/south etc..)
35465 unrendered_panels : null, // unrendered panels.
35466 createBody : function(){
35467 /** This region's body element
35468 * @type Roo.Element */
35469 this.bodyEl = this.el.createChild({
35471 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35475 onRender: function(ctr, pos)
35477 var dh = Roo.DomHelper;
35478 /** This region's container element
35479 * @type Roo.Element */
35480 this.el = dh.append(ctr.dom, {
35482 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35484 /** This region's title element
35485 * @type Roo.Element */
35487 this.titleEl = dh.append(this.el.dom,
35490 unselectable: "on",
35491 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35493 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35494 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35497 this.titleEl.enableDisplayMode();
35498 /** This region's title text element
35499 * @type HTMLElement */
35500 this.titleTextEl = this.titleEl.dom.firstChild;
35501 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35503 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35504 this.closeBtn.enableDisplayMode();
35505 this.closeBtn.on("click", this.closeClicked, this);
35506 this.closeBtn.hide();
35508 this.createBody(this.config);
35509 if(this.config.hideWhenEmpty){
35511 this.on("paneladded", this.validateVisibility, this);
35512 this.on("panelremoved", this.validateVisibility, this);
35514 if(this.autoScroll){
35515 this.bodyEl.setStyle("overflow", "auto");
35517 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35519 //if(c.titlebar !== false){
35520 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35521 this.titleEl.hide();
35523 this.titleEl.show();
35524 if(this.config.title){
35525 this.titleTextEl.innerHTML = this.config.title;
35529 if(this.config.collapsed){
35530 this.collapse(true);
35532 if(this.config.hidden){
35536 if (this.unrendered_panels && this.unrendered_panels.length) {
35537 for (var i =0;i< this.unrendered_panels.length; i++) {
35538 this.add(this.unrendered_panels[i]);
35540 this.unrendered_panels = null;
35546 applyConfig : function(c)
35549 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35550 var dh = Roo.DomHelper;
35551 if(c.titlebar !== false){
35552 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35553 this.collapseBtn.on("click", this.collapse, this);
35554 this.collapseBtn.enableDisplayMode();
35556 if(c.showPin === true || this.showPin){
35557 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35558 this.stickBtn.enableDisplayMode();
35559 this.stickBtn.on("click", this.expand, this);
35560 this.stickBtn.hide();
35565 /** This region's collapsed element
35566 * @type Roo.Element */
35569 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35570 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35573 if(c.floatable !== false){
35574 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35575 this.collapsedEl.on("click", this.collapseClick, this);
35578 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35579 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35580 id: "message", unselectable: "on", style:{"float":"left"}});
35581 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35583 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35584 this.expandBtn.on("click", this.expand, this);
35588 if(this.collapseBtn){
35589 this.collapseBtn.setVisible(c.collapsible == true);
35592 this.cmargins = c.cmargins || this.cmargins ||
35593 (this.position == "west" || this.position == "east" ?
35594 {top: 0, left: 2, right:2, bottom: 0} :
35595 {top: 2, left: 0, right:0, bottom: 2});
35597 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35600 this.bottomTabs = c.tabPosition != "top";
35602 this.autoScroll = c.autoScroll || false;
35607 this.duration = c.duration || .30;
35608 this.slideDuration = c.slideDuration || .45;
35613 * Returns true if this region is currently visible.
35614 * @return {Boolean}
35616 isVisible : function(){
35617 return this.visible;
35621 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35622 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35624 //setCollapsedTitle : function(title){
35625 // title = title || " ";
35626 // if(this.collapsedTitleTextEl){
35627 // this.collapsedTitleTextEl.innerHTML = title;
35631 getBox : function(){
35633 // if(!this.collapsed){
35634 b = this.el.getBox(false, true);
35636 // b = this.collapsedEl.getBox(false, true);
35641 getMargins : function(){
35642 return this.margins;
35643 //return this.collapsed ? this.cmargins : this.margins;
35646 highlight : function(){
35647 this.el.addClass("x-layout-panel-dragover");
35650 unhighlight : function(){
35651 this.el.removeClass("x-layout-panel-dragover");
35654 updateBox : function(box)
35656 if (!this.bodyEl) {
35657 return; // not rendered yet..
35661 if(!this.collapsed){
35662 this.el.dom.style.left = box.x + "px";
35663 this.el.dom.style.top = box.y + "px";
35664 this.updateBody(box.width, box.height);
35666 this.collapsedEl.dom.style.left = box.x + "px";
35667 this.collapsedEl.dom.style.top = box.y + "px";
35668 this.collapsedEl.setSize(box.width, box.height);
35671 this.tabs.autoSizeTabs();
35675 updateBody : function(w, h)
35678 this.el.setWidth(w);
35679 w -= this.el.getBorderWidth("rl");
35680 if(this.config.adjustments){
35681 w += this.config.adjustments[0];
35684 if(h !== null && h > 0){
35685 this.el.setHeight(h);
35686 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35687 h -= this.el.getBorderWidth("tb");
35688 if(this.config.adjustments){
35689 h += this.config.adjustments[1];
35691 this.bodyEl.setHeight(h);
35693 h = this.tabs.syncHeight(h);
35696 if(this.panelSize){
35697 w = w !== null ? w : this.panelSize.width;
35698 h = h !== null ? h : this.panelSize.height;
35700 if(this.activePanel){
35701 var el = this.activePanel.getEl();
35702 w = w !== null ? w : el.getWidth();
35703 h = h !== null ? h : el.getHeight();
35704 this.panelSize = {width: w, height: h};
35705 this.activePanel.setSize(w, h);
35707 if(Roo.isIE && this.tabs){
35708 this.tabs.el.repaint();
35713 * Returns the container element for this region.
35714 * @return {Roo.Element}
35716 getEl : function(){
35721 * Hides this region.
35724 //if(!this.collapsed){
35725 this.el.dom.style.left = "-2000px";
35728 // this.collapsedEl.dom.style.left = "-2000px";
35729 // this.collapsedEl.hide();
35731 this.visible = false;
35732 this.fireEvent("visibilitychange", this, false);
35736 * Shows this region if it was previously hidden.
35739 //if(!this.collapsed){
35742 // this.collapsedEl.show();
35744 this.visible = true;
35745 this.fireEvent("visibilitychange", this, true);
35748 closeClicked : function(){
35749 if(this.activePanel){
35750 this.remove(this.activePanel);
35754 collapseClick : function(e){
35756 e.stopPropagation();
35759 e.stopPropagation();
35765 * Collapses this region.
35766 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35769 collapse : function(skipAnim, skipCheck = false){
35770 if(this.collapsed) {
35774 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35776 this.collapsed = true;
35778 this.split.el.hide();
35780 if(this.config.animate && skipAnim !== true){
35781 this.fireEvent("invalidated", this);
35782 this.animateCollapse();
35784 this.el.setLocation(-20000,-20000);
35786 this.collapsedEl.show();
35787 this.fireEvent("collapsed", this);
35788 this.fireEvent("invalidated", this);
35794 animateCollapse : function(){
35799 * Expands this region if it was previously collapsed.
35800 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35801 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35804 expand : function(e, skipAnim){
35806 e.stopPropagation();
35808 if(!this.collapsed || this.el.hasActiveFx()) {
35812 this.afterSlideIn();
35815 this.collapsed = false;
35816 if(this.config.animate && skipAnim !== true){
35817 this.animateExpand();
35821 this.split.el.show();
35823 this.collapsedEl.setLocation(-2000,-2000);
35824 this.collapsedEl.hide();
35825 this.fireEvent("invalidated", this);
35826 this.fireEvent("expanded", this);
35830 animateExpand : function(){
35834 initTabs : function()
35836 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35838 var ts = new Roo.bootstrap.panel.Tabs({
35839 el: this.bodyEl.dom,
35840 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35841 disableTooltips: this.config.disableTabTips,
35842 toolbar : this.config.toolbar
35845 if(this.config.hideTabs){
35846 ts.stripWrap.setDisplayed(false);
35849 ts.resizeTabs = this.config.resizeTabs === true;
35850 ts.minTabWidth = this.config.minTabWidth || 40;
35851 ts.maxTabWidth = this.config.maxTabWidth || 250;
35852 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35853 ts.monitorResize = false;
35854 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35855 ts.bodyEl.addClass('roo-layout-tabs-body');
35856 this.panels.each(this.initPanelAsTab, this);
35859 initPanelAsTab : function(panel){
35860 var ti = this.tabs.addTab(
35864 this.config.closeOnTab && panel.isClosable(),
35867 if(panel.tabTip !== undefined){
35868 ti.setTooltip(panel.tabTip);
35870 ti.on("activate", function(){
35871 this.setActivePanel(panel);
35874 if(this.config.closeOnTab){
35875 ti.on("beforeclose", function(t, e){
35877 this.remove(panel);
35881 panel.tabItem = ti;
35886 updatePanelTitle : function(panel, title)
35888 if(this.activePanel == panel){
35889 this.updateTitle(title);
35892 var ti = this.tabs.getTab(panel.getEl().id);
35894 if(panel.tabTip !== undefined){
35895 ti.setTooltip(panel.tabTip);
35900 updateTitle : function(title){
35901 if(this.titleTextEl && !this.config.title){
35902 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35906 setActivePanel : function(panel)
35908 panel = this.getPanel(panel);
35909 if(this.activePanel && this.activePanel != panel){
35910 if(this.activePanel.setActiveState(false) === false){
35914 this.activePanel = panel;
35915 panel.setActiveState(true);
35916 if(this.panelSize){
35917 panel.setSize(this.panelSize.width, this.panelSize.height);
35920 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35922 this.updateTitle(panel.getTitle());
35924 this.fireEvent("invalidated", this);
35926 this.fireEvent("panelactivated", this, panel);
35930 * Shows the specified panel.
35931 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35932 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35934 showPanel : function(panel)
35936 panel = this.getPanel(panel);
35939 var tab = this.tabs.getTab(panel.getEl().id);
35940 if(tab.isHidden()){
35941 this.tabs.unhideTab(tab.id);
35945 this.setActivePanel(panel);
35952 * Get the active panel for this region.
35953 * @return {Roo.ContentPanel} The active panel or null
35955 getActivePanel : function(){
35956 return this.activePanel;
35959 validateVisibility : function(){
35960 if(this.panels.getCount() < 1){
35961 this.updateTitle(" ");
35962 this.closeBtn.hide();
35965 if(!this.isVisible()){
35972 * Adds the passed ContentPanel(s) to this region.
35973 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35974 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35976 add : function(panel)
35978 if(arguments.length > 1){
35979 for(var i = 0, len = arguments.length; i < len; i++) {
35980 this.add(arguments[i]);
35985 // if we have not been rendered yet, then we can not really do much of this..
35986 if (!this.bodyEl) {
35987 this.unrendered_panels.push(panel);
35994 if(this.hasPanel(panel)){
35995 this.showPanel(panel);
35998 panel.setRegion(this);
35999 this.panels.add(panel);
36000 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36001 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36002 // and hide them... ???
36003 this.bodyEl.dom.appendChild(panel.getEl().dom);
36004 if(panel.background !== true){
36005 this.setActivePanel(panel);
36007 this.fireEvent("paneladded", this, panel);
36014 this.initPanelAsTab(panel);
36018 if(panel.background !== true){
36019 this.tabs.activate(panel.getEl().id);
36021 this.fireEvent("paneladded", this, panel);
36026 * Hides the tab for the specified panel.
36027 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36029 hidePanel : function(panel){
36030 if(this.tabs && (panel = this.getPanel(panel))){
36031 this.tabs.hideTab(panel.getEl().id);
36036 * Unhides the tab for a previously hidden panel.
36037 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36039 unhidePanel : function(panel){
36040 if(this.tabs && (panel = this.getPanel(panel))){
36041 this.tabs.unhideTab(panel.getEl().id);
36045 clearPanels : function(){
36046 while(this.panels.getCount() > 0){
36047 this.remove(this.panels.first());
36052 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36053 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36054 * @param {Boolean} preservePanel Overrides the config preservePanel option
36055 * @return {Roo.ContentPanel} The panel that was removed
36057 remove : function(panel, preservePanel)
36059 panel = this.getPanel(panel);
36064 this.fireEvent("beforeremove", this, panel, e);
36065 if(e.cancel === true){
36068 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36069 var panelId = panel.getId();
36070 this.panels.removeKey(panelId);
36072 document.body.appendChild(panel.getEl().dom);
36075 this.tabs.removeTab(panel.getEl().id);
36076 }else if (!preservePanel){
36077 this.bodyEl.dom.removeChild(panel.getEl().dom);
36079 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36080 var p = this.panels.first();
36081 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36082 tempEl.appendChild(p.getEl().dom);
36083 this.bodyEl.update("");
36084 this.bodyEl.dom.appendChild(p.getEl().dom);
36086 this.updateTitle(p.getTitle());
36088 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36089 this.setActivePanel(p);
36091 panel.setRegion(null);
36092 if(this.activePanel == panel){
36093 this.activePanel = null;
36095 if(this.config.autoDestroy !== false && preservePanel !== true){
36096 try{panel.destroy();}catch(e){}
36098 this.fireEvent("panelremoved", this, panel);
36103 * Returns the TabPanel component used by this region
36104 * @return {Roo.TabPanel}
36106 getTabs : function(){
36110 createTool : function(parentEl, className){
36111 var btn = Roo.DomHelper.append(parentEl, {
36113 cls: "x-layout-tools-button",
36116 cls: "roo-layout-tools-button-inner " + className,
36120 btn.addClassOnOver("roo-layout-tools-button-over");
36125 * Ext JS Library 1.1.1
36126 * Copyright(c) 2006-2007, Ext JS, LLC.
36128 * Originally Released Under LGPL - original licence link has changed is not relivant.
36131 * <script type="text/javascript">
36137 * @class Roo.SplitLayoutRegion
36138 * @extends Roo.LayoutRegion
36139 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36141 Roo.bootstrap.layout.Split = function(config){
36142 this.cursor = config.cursor;
36143 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36146 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36148 splitTip : "Drag to resize.",
36149 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36150 useSplitTips : false,
36152 applyConfig : function(config){
36153 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36156 onRender : function(ctr,pos) {
36158 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36159 if(!this.config.split){
36164 var splitEl = Roo.DomHelper.append(ctr.dom, {
36166 id: this.el.id + "-split",
36167 cls: "roo-layout-split roo-layout-split-"+this.position,
36170 /** The SplitBar for this region
36171 * @type Roo.SplitBar */
36172 // does not exist yet...
36173 Roo.log([this.position, this.orientation]);
36175 this.split = new Roo.bootstrap.SplitBar({
36176 dragElement : splitEl,
36177 resizingElement: this.el,
36178 orientation : this.orientation
36181 this.split.on("moved", this.onSplitMove, this);
36182 this.split.useShim = this.config.useShim === true;
36183 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36184 if(this.useSplitTips){
36185 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36187 //if(config.collapsible){
36188 // this.split.el.on("dblclick", this.collapse, this);
36191 if(typeof this.config.minSize != "undefined"){
36192 this.split.minSize = this.config.minSize;
36194 if(typeof this.config.maxSize != "undefined"){
36195 this.split.maxSize = this.config.maxSize;
36197 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36198 this.hideSplitter();
36203 getHMaxSize : function(){
36204 var cmax = this.config.maxSize || 10000;
36205 var center = this.mgr.getRegion("center");
36206 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36209 getVMaxSize : function(){
36210 var cmax = this.config.maxSize || 10000;
36211 var center = this.mgr.getRegion("center");
36212 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36215 onSplitMove : function(split, newSize){
36216 this.fireEvent("resized", this, newSize);
36220 * Returns the {@link Roo.SplitBar} for this region.
36221 * @return {Roo.SplitBar}
36223 getSplitBar : function(){
36228 this.hideSplitter();
36229 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36232 hideSplitter : function(){
36234 this.split.el.setLocation(-2000,-2000);
36235 this.split.el.hide();
36241 this.split.el.show();
36243 Roo.bootstrap.layout.Split.superclass.show.call(this);
36246 beforeSlide: function(){
36247 if(Roo.isGecko){// firefox overflow auto bug workaround
36248 this.bodyEl.clip();
36250 this.tabs.bodyEl.clip();
36252 if(this.activePanel){
36253 this.activePanel.getEl().clip();
36255 if(this.activePanel.beforeSlide){
36256 this.activePanel.beforeSlide();
36262 afterSlide : function(){
36263 if(Roo.isGecko){// firefox overflow auto bug workaround
36264 this.bodyEl.unclip();
36266 this.tabs.bodyEl.unclip();
36268 if(this.activePanel){
36269 this.activePanel.getEl().unclip();
36270 if(this.activePanel.afterSlide){
36271 this.activePanel.afterSlide();
36277 initAutoHide : function(){
36278 if(this.autoHide !== false){
36279 if(!this.autoHideHd){
36280 var st = new Roo.util.DelayedTask(this.slideIn, this);
36281 this.autoHideHd = {
36282 "mouseout": function(e){
36283 if(!e.within(this.el, true)){
36287 "mouseover" : function(e){
36293 this.el.on(this.autoHideHd);
36297 clearAutoHide : function(){
36298 if(this.autoHide !== false){
36299 this.el.un("mouseout", this.autoHideHd.mouseout);
36300 this.el.un("mouseover", this.autoHideHd.mouseover);
36304 clearMonitor : function(){
36305 Roo.get(document).un("click", this.slideInIf, this);
36308 // these names are backwards but not changed for compat
36309 slideOut : function(){
36310 if(this.isSlid || this.el.hasActiveFx()){
36313 this.isSlid = true;
36314 if(this.collapseBtn){
36315 this.collapseBtn.hide();
36317 this.closeBtnState = this.closeBtn.getStyle('display');
36318 this.closeBtn.hide();
36320 this.stickBtn.show();
36323 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36324 this.beforeSlide();
36325 this.el.setStyle("z-index", 10001);
36326 this.el.slideIn(this.getSlideAnchor(), {
36327 callback: function(){
36329 this.initAutoHide();
36330 Roo.get(document).on("click", this.slideInIf, this);
36331 this.fireEvent("slideshow", this);
36338 afterSlideIn : function(){
36339 this.clearAutoHide();
36340 this.isSlid = false;
36341 this.clearMonitor();
36342 this.el.setStyle("z-index", "");
36343 if(this.collapseBtn){
36344 this.collapseBtn.show();
36346 this.closeBtn.setStyle('display', this.closeBtnState);
36348 this.stickBtn.hide();
36350 this.fireEvent("slidehide", this);
36353 slideIn : function(cb){
36354 if(!this.isSlid || this.el.hasActiveFx()){
36358 this.isSlid = false;
36359 this.beforeSlide();
36360 this.el.slideOut(this.getSlideAnchor(), {
36361 callback: function(){
36362 this.el.setLeftTop(-10000, -10000);
36364 this.afterSlideIn();
36372 slideInIf : function(e){
36373 if(!e.within(this.el)){
36378 animateCollapse : function(){
36379 this.beforeSlide();
36380 this.el.setStyle("z-index", 20000);
36381 var anchor = this.getSlideAnchor();
36382 this.el.slideOut(anchor, {
36383 callback : function(){
36384 this.el.setStyle("z-index", "");
36385 this.collapsedEl.slideIn(anchor, {duration:.3});
36387 this.el.setLocation(-10000,-10000);
36389 this.fireEvent("collapsed", this);
36396 animateExpand : function(){
36397 this.beforeSlide();
36398 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36399 this.el.setStyle("z-index", 20000);
36400 this.collapsedEl.hide({
36403 this.el.slideIn(this.getSlideAnchor(), {
36404 callback : function(){
36405 this.el.setStyle("z-index", "");
36408 this.split.el.show();
36410 this.fireEvent("invalidated", this);
36411 this.fireEvent("expanded", this);
36439 getAnchor : function(){
36440 return this.anchors[this.position];
36443 getCollapseAnchor : function(){
36444 return this.canchors[this.position];
36447 getSlideAnchor : function(){
36448 return this.sanchors[this.position];
36451 getAlignAdj : function(){
36452 var cm = this.cmargins;
36453 switch(this.position){
36469 getExpandAdj : function(){
36470 var c = this.collapsedEl, cm = this.cmargins;
36471 switch(this.position){
36473 return [-(cm.right+c.getWidth()+cm.left), 0];
36476 return [cm.right+c.getWidth()+cm.left, 0];
36479 return [0, -(cm.top+cm.bottom+c.getHeight())];
36482 return [0, cm.top+cm.bottom+c.getHeight()];
36488 * Ext JS Library 1.1.1
36489 * Copyright(c) 2006-2007, Ext JS, LLC.
36491 * Originally Released Under LGPL - original licence link has changed is not relivant.
36494 * <script type="text/javascript">
36497 * These classes are private internal classes
36499 Roo.bootstrap.layout.Center = function(config){
36500 config.region = "center";
36501 Roo.bootstrap.layout.Region.call(this, config);
36502 this.visible = true;
36503 this.minWidth = config.minWidth || 20;
36504 this.minHeight = config.minHeight || 20;
36507 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36509 // center panel can't be hidden
36513 // center panel can't be hidden
36516 getMinWidth: function(){
36517 return this.minWidth;
36520 getMinHeight: function(){
36521 return this.minHeight;
36534 Roo.bootstrap.layout.North = function(config)
36536 config.region = 'north';
36537 config.cursor = 'n-resize';
36539 Roo.bootstrap.layout.Split.call(this, config);
36543 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36544 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36545 this.split.el.addClass("roo-layout-split-v");
36547 var size = config.initialSize || config.height;
36548 if(typeof size != "undefined"){
36549 this.el.setHeight(size);
36552 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36554 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36558 getBox : function(){
36559 if(this.collapsed){
36560 return this.collapsedEl.getBox();
36562 var box = this.el.getBox();
36564 box.height += this.split.el.getHeight();
36569 updateBox : function(box){
36570 if(this.split && !this.collapsed){
36571 box.height -= this.split.el.getHeight();
36572 this.split.el.setLeft(box.x);
36573 this.split.el.setTop(box.y+box.height);
36574 this.split.el.setWidth(box.width);
36576 if(this.collapsed){
36577 this.updateBody(box.width, null);
36579 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36587 Roo.bootstrap.layout.South = function(config){
36588 config.region = 'south';
36589 config.cursor = 's-resize';
36590 Roo.bootstrap.layout.Split.call(this, config);
36592 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36593 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36594 this.split.el.addClass("roo-layout-split-v");
36596 var size = config.initialSize || config.height;
36597 if(typeof size != "undefined"){
36598 this.el.setHeight(size);
36602 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36603 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36604 getBox : function(){
36605 if(this.collapsed){
36606 return this.collapsedEl.getBox();
36608 var box = this.el.getBox();
36610 var sh = this.split.el.getHeight();
36617 updateBox : function(box){
36618 if(this.split && !this.collapsed){
36619 var sh = this.split.el.getHeight();
36622 this.split.el.setLeft(box.x);
36623 this.split.el.setTop(box.y-sh);
36624 this.split.el.setWidth(box.width);
36626 if(this.collapsed){
36627 this.updateBody(box.width, null);
36629 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36633 Roo.bootstrap.layout.East = function(config){
36634 config.region = "east";
36635 config.cursor = "e-resize";
36636 Roo.bootstrap.layout.Split.call(this, config);
36638 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36639 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36640 this.split.el.addClass("roo-layout-split-h");
36642 var size = config.initialSize || config.width;
36643 if(typeof size != "undefined"){
36644 this.el.setWidth(size);
36647 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36648 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36649 getBox : function(){
36650 if(this.collapsed){
36651 return this.collapsedEl.getBox();
36653 var box = this.el.getBox();
36655 var sw = this.split.el.getWidth();
36662 updateBox : function(box){
36663 if(this.split && !this.collapsed){
36664 var sw = this.split.el.getWidth();
36666 this.split.el.setLeft(box.x);
36667 this.split.el.setTop(box.y);
36668 this.split.el.setHeight(box.height);
36671 if(this.collapsed){
36672 this.updateBody(null, box.height);
36674 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36678 Roo.bootstrap.layout.West = function(config){
36679 config.region = "west";
36680 config.cursor = "w-resize";
36682 Roo.bootstrap.layout.Split.call(this, config);
36684 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36685 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36686 this.split.el.addClass("roo-layout-split-h");
36690 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36691 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36693 onRender: function(ctr, pos)
36695 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36696 var size = this.config.initialSize || this.config.width;
36697 if(typeof size != "undefined"){
36698 this.el.setWidth(size);
36702 getBox : function(){
36703 if(this.collapsed){
36704 return this.collapsedEl.getBox();
36706 var box = this.el.getBox();
36708 box.width += this.split.el.getWidth();
36713 updateBox : function(box){
36714 if(this.split && !this.collapsed){
36715 var sw = this.split.el.getWidth();
36717 this.split.el.setLeft(box.x+box.width);
36718 this.split.el.setTop(box.y);
36719 this.split.el.setHeight(box.height);
36721 if(this.collapsed){
36722 this.updateBody(null, box.height);
36724 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36727 Roo.namespace("Roo.bootstrap.panel");/*
36729 * Ext JS Library 1.1.1
36730 * Copyright(c) 2006-2007, Ext JS, LLC.
36732 * Originally Released Under LGPL - original licence link has changed is not relivant.
36735 * <script type="text/javascript">
36738 * @class Roo.ContentPanel
36739 * @extends Roo.util.Observable
36740 * A basic ContentPanel element.
36741 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36742 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36743 * @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
36744 * @cfg {Boolean} closable True if the panel can be closed/removed
36745 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36746 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36747 * @cfg {Toolbar} toolbar A toolbar for this panel
36748 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36749 * @cfg {String} title The title for this panel
36750 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36751 * @cfg {String} url Calls {@link #setUrl} with this value
36752 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36753 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36754 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36755 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36756 * @cfg {Boolean} badges render the badges
36759 * Create a new ContentPanel.
36760 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36761 * @param {String/Object} config A string to set only the title or a config object
36762 * @param {String} content (optional) Set the HTML content for this panel
36763 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36765 Roo.bootstrap.panel.Content = function( config){
36767 this.tpl = config.tpl || false;
36769 var el = config.el;
36770 var content = config.content;
36772 if(config.autoCreate){ // xtype is available if this is called from factory
36775 this.el = Roo.get(el);
36776 if(!this.el && config && config.autoCreate){
36777 if(typeof config.autoCreate == "object"){
36778 if(!config.autoCreate.id){
36779 config.autoCreate.id = config.id||el;
36781 this.el = Roo.DomHelper.append(document.body,
36782 config.autoCreate, true);
36784 var elcfg = { tag: "div",
36785 cls: "roo-layout-inactive-content",
36789 elcfg.html = config.html;
36793 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36796 this.closable = false;
36797 this.loaded = false;
36798 this.active = false;
36801 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36803 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36805 this.wrapEl = this.el; //this.el.wrap();
36807 if (config.toolbar.items) {
36808 ti = config.toolbar.items ;
36809 delete config.toolbar.items ;
36813 this.toolbar.render(this.wrapEl, 'before');
36814 for(var i =0;i < ti.length;i++) {
36815 // Roo.log(['add child', items[i]]);
36816 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36818 this.toolbar.items = nitems;
36819 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36820 delete config.toolbar;
36824 // xtype created footer. - not sure if will work as we normally have to render first..
36825 if (this.footer && !this.footer.el && this.footer.xtype) {
36826 if (!this.wrapEl) {
36827 this.wrapEl = this.el.wrap();
36830 this.footer.container = this.wrapEl.createChild();
36832 this.footer = Roo.factory(this.footer, Roo);
36837 if(typeof config == "string"){
36838 this.title = config;
36840 Roo.apply(this, config);
36844 this.resizeEl = Roo.get(this.resizeEl, true);
36846 this.resizeEl = this.el;
36848 // handle view.xtype
36856 * Fires when this panel is activated.
36857 * @param {Roo.ContentPanel} this
36861 * @event deactivate
36862 * Fires when this panel is activated.
36863 * @param {Roo.ContentPanel} this
36865 "deactivate" : true,
36869 * Fires when this panel is resized if fitToFrame is true.
36870 * @param {Roo.ContentPanel} this
36871 * @param {Number} width The width after any component adjustments
36872 * @param {Number} height The height after any component adjustments
36878 * Fires when this tab is created
36879 * @param {Roo.ContentPanel} this
36890 if(this.autoScroll){
36891 this.resizeEl.setStyle("overflow", "auto");
36893 // fix randome scrolling
36894 //this.el.on('scroll', function() {
36895 // Roo.log('fix random scolling');
36896 // this.scrollTo('top',0);
36899 content = content || this.content;
36901 this.setContent(content);
36903 if(config && config.url){
36904 this.setUrl(this.url, this.params, this.loadOnce);
36909 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36911 if (this.view && typeof(this.view.xtype) != 'undefined') {
36912 this.view.el = this.el.appendChild(document.createElement("div"));
36913 this.view = Roo.factory(this.view);
36914 this.view.render && this.view.render(false, '');
36918 this.fireEvent('render', this);
36921 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36925 setRegion : function(region){
36926 this.region = region;
36927 this.setActiveClass(region && !this.background);
36931 setActiveClass: function(state)
36934 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36935 this.el.setStyle('position','relative');
36937 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36938 this.el.setStyle('position', 'absolute');
36943 * Returns the toolbar for this Panel if one was configured.
36944 * @return {Roo.Toolbar}
36946 getToolbar : function(){
36947 return this.toolbar;
36950 setActiveState : function(active)
36952 this.active = active;
36953 this.setActiveClass(active);
36955 if(this.fireEvent("deactivate", this) === false){
36960 this.fireEvent("activate", this);
36964 * Updates this panel's element
36965 * @param {String} content The new content
36966 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36968 setContent : function(content, loadScripts){
36969 this.el.update(content, loadScripts);
36972 ignoreResize : function(w, h){
36973 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36976 this.lastSize = {width: w, height: h};
36981 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36982 * @return {Roo.UpdateManager} The UpdateManager
36984 getUpdateManager : function(){
36985 return this.el.getUpdateManager();
36988 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36989 * @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:
36992 url: "your-url.php",
36993 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36994 callback: yourFunction,
36995 scope: yourObject, //(optional scope)
36998 text: "Loading...",
37003 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37004 * 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.
37005 * @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}
37006 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37007 * @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.
37008 * @return {Roo.ContentPanel} this
37011 var um = this.el.getUpdateManager();
37012 um.update.apply(um, arguments);
37018 * 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.
37019 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37020 * @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)
37021 * @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)
37022 * @return {Roo.UpdateManager} The UpdateManager
37024 setUrl : function(url, params, loadOnce){
37025 if(this.refreshDelegate){
37026 this.removeListener("activate", this.refreshDelegate);
37028 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37029 this.on("activate", this.refreshDelegate);
37030 return this.el.getUpdateManager();
37033 _handleRefresh : function(url, params, loadOnce){
37034 if(!loadOnce || !this.loaded){
37035 var updater = this.el.getUpdateManager();
37036 updater.update(url, params, this._setLoaded.createDelegate(this));
37040 _setLoaded : function(){
37041 this.loaded = true;
37045 * Returns this panel's id
37048 getId : function(){
37053 * Returns this panel's element - used by regiosn to add.
37054 * @return {Roo.Element}
37056 getEl : function(){
37057 return this.wrapEl || this.el;
37062 adjustForComponents : function(width, height)
37064 //Roo.log('adjustForComponents ');
37065 if(this.resizeEl != this.el){
37066 width -= this.el.getFrameWidth('lr');
37067 height -= this.el.getFrameWidth('tb');
37070 var te = this.toolbar.getEl();
37071 te.setWidth(width);
37072 height -= te.getHeight();
37075 var te = this.footer.getEl();
37076 te.setWidth(width);
37077 height -= te.getHeight();
37081 if(this.adjustments){
37082 width += this.adjustments[0];
37083 height += this.adjustments[1];
37085 return {"width": width, "height": height};
37088 setSize : function(width, height){
37089 if(this.fitToFrame && !this.ignoreResize(width, height)){
37090 if(this.fitContainer && this.resizeEl != this.el){
37091 this.el.setSize(width, height);
37093 var size = this.adjustForComponents(width, height);
37094 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37095 this.fireEvent('resize', this, size.width, size.height);
37100 * Returns this panel's title
37103 getTitle : function(){
37105 if (typeof(this.title) != 'object') {
37110 for (var k in this.title) {
37111 if (!this.title.hasOwnProperty(k)) {
37115 if (k.indexOf('-') >= 0) {
37116 var s = k.split('-');
37117 for (var i = 0; i<s.length; i++) {
37118 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37121 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37128 * Set this panel's title
37129 * @param {String} title
37131 setTitle : function(title){
37132 this.title = title;
37134 this.region.updatePanelTitle(this, title);
37139 * Returns true is this panel was configured to be closable
37140 * @return {Boolean}
37142 isClosable : function(){
37143 return this.closable;
37146 beforeSlide : function(){
37148 this.resizeEl.clip();
37151 afterSlide : function(){
37153 this.resizeEl.unclip();
37157 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37158 * Will fail silently if the {@link #setUrl} method has not been called.
37159 * This does not activate the panel, just updates its content.
37161 refresh : function(){
37162 if(this.refreshDelegate){
37163 this.loaded = false;
37164 this.refreshDelegate();
37169 * Destroys this panel
37171 destroy : function(){
37172 this.el.removeAllListeners();
37173 var tempEl = document.createElement("span");
37174 tempEl.appendChild(this.el.dom);
37175 tempEl.innerHTML = "";
37181 * form - if the content panel contains a form - this is a reference to it.
37182 * @type {Roo.form.Form}
37186 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37187 * This contains a reference to it.
37193 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37203 * @param {Object} cfg Xtype definition of item to add.
37207 getChildContainer: function () {
37208 return this.getEl();
37213 var ret = new Roo.factory(cfg);
37218 if (cfg.xtype.match(/^Form$/)) {
37221 //if (this.footer) {
37222 // el = this.footer.container.insertSibling(false, 'before');
37224 el = this.el.createChild();
37227 this.form = new Roo.form.Form(cfg);
37230 if ( this.form.allItems.length) {
37231 this.form.render(el.dom);
37235 // should only have one of theses..
37236 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37237 // views.. should not be just added - used named prop 'view''
37239 cfg.el = this.el.appendChild(document.createElement("div"));
37242 var ret = new Roo.factory(cfg);
37244 ret.render && ret.render(false, ''); // render blank..
37254 * @class Roo.bootstrap.panel.Grid
37255 * @extends Roo.bootstrap.panel.Content
37257 * Create a new GridPanel.
37258 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37259 * @param {Object} config A the config object
37265 Roo.bootstrap.panel.Grid = function(config)
37269 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37270 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37272 config.el = this.wrapper;
37273 //this.el = this.wrapper;
37275 if (config.container) {
37276 // ctor'ed from a Border/panel.grid
37279 this.wrapper.setStyle("overflow", "hidden");
37280 this.wrapper.addClass('roo-grid-container');
37285 if(config.toolbar){
37286 var tool_el = this.wrapper.createChild();
37287 this.toolbar = Roo.factory(config.toolbar);
37289 if (config.toolbar.items) {
37290 ti = config.toolbar.items ;
37291 delete config.toolbar.items ;
37295 this.toolbar.render(tool_el);
37296 for(var i =0;i < ti.length;i++) {
37297 // Roo.log(['add child', items[i]]);
37298 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37300 this.toolbar.items = nitems;
37302 delete config.toolbar;
37305 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37306 config.grid.scrollBody = true;;
37307 config.grid.monitorWindowResize = false; // turn off autosizing
37308 config.grid.autoHeight = false;
37309 config.grid.autoWidth = false;
37311 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37313 if (config.background) {
37314 // render grid on panel activation (if panel background)
37315 this.on('activate', function(gp) {
37316 if (!gp.grid.rendered) {
37317 gp.grid.render(this.wrapper);
37318 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37323 this.grid.render(this.wrapper);
37324 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37327 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37328 // ??? needed ??? config.el = this.wrapper;
37333 // xtype created footer. - not sure if will work as we normally have to render first..
37334 if (this.footer && !this.footer.el && this.footer.xtype) {
37336 var ctr = this.grid.getView().getFooterPanel(true);
37337 this.footer.dataSource = this.grid.dataSource;
37338 this.footer = Roo.factory(this.footer, Roo);
37339 this.footer.render(ctr);
37349 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37350 getId : function(){
37351 return this.grid.id;
37355 * Returns the grid for this panel
37356 * @return {Roo.bootstrap.Table}
37358 getGrid : function(){
37362 setSize : function(width, height){
37363 if(!this.ignoreResize(width, height)){
37364 var grid = this.grid;
37365 var size = this.adjustForComponents(width, height);
37366 var gridel = grid.getGridEl();
37367 gridel.setSize(size.width, size.height);
37369 var thd = grid.getGridEl().select('thead',true).first();
37370 var tbd = grid.getGridEl().select('tbody', true).first();
37372 tbd.setSize(width, height - thd.getHeight());
37381 beforeSlide : function(){
37382 this.grid.getView().scroller.clip();
37385 afterSlide : function(){
37386 this.grid.getView().scroller.unclip();
37389 destroy : function(){
37390 this.grid.destroy();
37392 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37397 * @class Roo.bootstrap.panel.Nest
37398 * @extends Roo.bootstrap.panel.Content
37400 * Create a new Panel, that can contain a layout.Border.
37403 * @param {Roo.BorderLayout} layout The layout for this panel
37404 * @param {String/Object} config A string to set only the title or a config object
37406 Roo.bootstrap.panel.Nest = function(config)
37408 // construct with only one argument..
37409 /* FIXME - implement nicer consturctors
37410 if (layout.layout) {
37412 layout = config.layout;
37413 delete config.layout;
37415 if (layout.xtype && !layout.getEl) {
37416 // then layout needs constructing..
37417 layout = Roo.factory(layout, Roo);
37421 config.el = config.layout.getEl();
37423 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37425 config.layout.monitorWindowResize = false; // turn off autosizing
37426 this.layout = config.layout;
37427 this.layout.getEl().addClass("roo-layout-nested-layout");
37434 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37436 setSize : function(width, height){
37437 if(!this.ignoreResize(width, height)){
37438 var size = this.adjustForComponents(width, height);
37439 var el = this.layout.getEl();
37440 if (size.height < 1) {
37441 el.setWidth(size.width);
37443 el.setSize(size.width, size.height);
37445 var touch = el.dom.offsetWidth;
37446 this.layout.layout();
37447 // ie requires a double layout on the first pass
37448 if(Roo.isIE && !this.initialized){
37449 this.initialized = true;
37450 this.layout.layout();
37455 // activate all subpanels if not currently active..
37457 setActiveState : function(active){
37458 this.active = active;
37459 this.setActiveClass(active);
37462 this.fireEvent("deactivate", this);
37466 this.fireEvent("activate", this);
37467 // not sure if this should happen before or after..
37468 if (!this.layout) {
37469 return; // should not happen..
37472 for (var r in this.layout.regions) {
37473 reg = this.layout.getRegion(r);
37474 if (reg.getActivePanel()) {
37475 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37476 reg.setActivePanel(reg.getActivePanel());
37479 if (!reg.panels.length) {
37482 reg.showPanel(reg.getPanel(0));
37491 * Returns the nested BorderLayout for this panel
37492 * @return {Roo.BorderLayout}
37494 getLayout : function(){
37495 return this.layout;
37499 * Adds a xtype elements to the layout of the nested panel
37503 xtype : 'ContentPanel',
37510 xtype : 'NestedLayoutPanel',
37516 items : [ ... list of content panels or nested layout panels.. ]
37520 * @param {Object} cfg Xtype definition of item to add.
37522 addxtype : function(cfg) {
37523 return this.layout.addxtype(cfg);
37528 * Ext JS Library 1.1.1
37529 * Copyright(c) 2006-2007, Ext JS, LLC.
37531 * Originally Released Under LGPL - original licence link has changed is not relivant.
37534 * <script type="text/javascript">
37537 * @class Roo.TabPanel
37538 * @extends Roo.util.Observable
37539 * A lightweight tab container.
37543 // basic tabs 1, built from existing content
37544 var tabs = new Roo.TabPanel("tabs1");
37545 tabs.addTab("script", "View Script");
37546 tabs.addTab("markup", "View Markup");
37547 tabs.activate("script");
37549 // more advanced tabs, built from javascript
37550 var jtabs = new Roo.TabPanel("jtabs");
37551 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37553 // set up the UpdateManager
37554 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37555 var updater = tab2.getUpdateManager();
37556 updater.setDefaultUrl("ajax1.htm");
37557 tab2.on('activate', updater.refresh, updater, true);
37559 // Use setUrl for Ajax loading
37560 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37561 tab3.setUrl("ajax2.htm", null, true);
37564 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37567 jtabs.activate("jtabs-1");
37570 * Create a new TabPanel.
37571 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37572 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37574 Roo.bootstrap.panel.Tabs = function(config){
37576 * The container element for this TabPanel.
37577 * @type Roo.Element
37579 this.el = Roo.get(config.el);
37582 if(typeof config == "boolean"){
37583 this.tabPosition = config ? "bottom" : "top";
37585 Roo.apply(this, config);
37589 if(this.tabPosition == "bottom"){
37590 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37591 this.el.addClass("roo-tabs-bottom");
37593 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37594 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37595 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37597 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37599 if(this.tabPosition != "bottom"){
37600 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37601 * @type Roo.Element
37603 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37604 this.el.addClass("roo-tabs-top");
37608 this.bodyEl.setStyle("position", "relative");
37610 this.active = null;
37611 this.activateDelegate = this.activate.createDelegate(this);
37616 * Fires when the active tab changes
37617 * @param {Roo.TabPanel} this
37618 * @param {Roo.TabPanelItem} activePanel The new active tab
37622 * @event beforetabchange
37623 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37624 * @param {Roo.TabPanel} this
37625 * @param {Object} e Set cancel to true on this object to cancel the tab change
37626 * @param {Roo.TabPanelItem} tab The tab being changed to
37628 "beforetabchange" : true
37631 Roo.EventManager.onWindowResize(this.onResize, this);
37632 this.cpad = this.el.getPadding("lr");
37633 this.hiddenCount = 0;
37636 // toolbar on the tabbar support...
37637 if (this.toolbar) {
37638 alert("no toolbar support yet");
37639 this.toolbar = false;
37641 var tcfg = this.toolbar;
37642 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37643 this.toolbar = new Roo.Toolbar(tcfg);
37644 if (Roo.isSafari) {
37645 var tbl = tcfg.container.child('table', true);
37646 tbl.setAttribute('width', '100%');
37654 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37657 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37659 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37661 tabPosition : "top",
37663 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37665 currentTabWidth : 0,
37667 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37671 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37675 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37677 preferredTabWidth : 175,
37679 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37681 resizeTabs : false,
37683 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37685 monitorResize : true,
37687 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37692 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37693 * @param {String} id The id of the div to use <b>or create</b>
37694 * @param {String} text The text for the tab
37695 * @param {String} content (optional) Content to put in the TabPanelItem body
37696 * @param {Boolean} closable (optional) True to create a close icon on the tab
37697 * @return {Roo.TabPanelItem} The created TabPanelItem
37699 addTab : function(id, text, content, closable, tpl)
37701 var item = new Roo.bootstrap.panel.TabItem({
37705 closable : closable,
37708 this.addTabItem(item);
37710 item.setContent(content);
37716 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37717 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37718 * @return {Roo.TabPanelItem}
37720 getTab : function(id){
37721 return this.items[id];
37725 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37726 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37728 hideTab : function(id){
37729 var t = this.items[id];
37732 this.hiddenCount++;
37733 this.autoSizeTabs();
37738 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37739 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37741 unhideTab : function(id){
37742 var t = this.items[id];
37744 t.setHidden(false);
37745 this.hiddenCount--;
37746 this.autoSizeTabs();
37751 * Adds an existing {@link Roo.TabPanelItem}.
37752 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37754 addTabItem : function(item){
37755 this.items[item.id] = item;
37756 this.items.push(item);
37757 // if(this.resizeTabs){
37758 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37759 // this.autoSizeTabs();
37761 // item.autoSize();
37766 * Removes a {@link Roo.TabPanelItem}.
37767 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37769 removeTab : function(id){
37770 var items = this.items;
37771 var tab = items[id];
37772 if(!tab) { return; }
37773 var index = items.indexOf(tab);
37774 if(this.active == tab && items.length > 1){
37775 var newTab = this.getNextAvailable(index);
37780 this.stripEl.dom.removeChild(tab.pnode.dom);
37781 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37782 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37784 items.splice(index, 1);
37785 delete this.items[tab.id];
37786 tab.fireEvent("close", tab);
37787 tab.purgeListeners();
37788 this.autoSizeTabs();
37791 getNextAvailable : function(start){
37792 var items = this.items;
37794 // look for a next tab that will slide over to
37795 // replace the one being removed
37796 while(index < items.length){
37797 var item = items[++index];
37798 if(item && !item.isHidden()){
37802 // if one isn't found select the previous tab (on the left)
37805 var item = items[--index];
37806 if(item && !item.isHidden()){
37814 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37815 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37817 disableTab : function(id){
37818 var tab = this.items[id];
37819 if(tab && this.active != tab){
37825 * Enables a {@link Roo.TabPanelItem} that is disabled.
37826 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37828 enableTab : function(id){
37829 var tab = this.items[id];
37834 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37835 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37836 * @return {Roo.TabPanelItem} The TabPanelItem.
37838 activate : function(id){
37839 var tab = this.items[id];
37843 if(tab == this.active || tab.disabled){
37847 this.fireEvent("beforetabchange", this, e, tab);
37848 if(e.cancel !== true && !tab.disabled){
37850 this.active.hide();
37852 this.active = this.items[id];
37853 this.active.show();
37854 this.fireEvent("tabchange", this, this.active);
37860 * Gets the active {@link Roo.TabPanelItem}.
37861 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37863 getActiveTab : function(){
37864 return this.active;
37868 * Updates the tab body element to fit the height of the container element
37869 * for overflow scrolling
37870 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37872 syncHeight : function(targetHeight){
37873 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37874 var bm = this.bodyEl.getMargins();
37875 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37876 this.bodyEl.setHeight(newHeight);
37880 onResize : function(){
37881 if(this.monitorResize){
37882 this.autoSizeTabs();
37887 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37889 beginUpdate : function(){
37890 this.updating = true;
37894 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37896 endUpdate : function(){
37897 this.updating = false;
37898 this.autoSizeTabs();
37902 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37904 autoSizeTabs : function(){
37905 var count = this.items.length;
37906 var vcount = count - this.hiddenCount;
37907 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37910 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37911 var availWidth = Math.floor(w / vcount);
37912 var b = this.stripBody;
37913 if(b.getWidth() > w){
37914 var tabs = this.items;
37915 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37916 if(availWidth < this.minTabWidth){
37917 /*if(!this.sleft){ // incomplete scrolling code
37918 this.createScrollButtons();
37921 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37924 if(this.currentTabWidth < this.preferredTabWidth){
37925 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37931 * Returns the number of tabs in this TabPanel.
37934 getCount : function(){
37935 return this.items.length;
37939 * Resizes all the tabs to the passed width
37940 * @param {Number} The new width
37942 setTabWidth : function(width){
37943 this.currentTabWidth = width;
37944 for(var i = 0, len = this.items.length; i < len; i++) {
37945 if(!this.items[i].isHidden()) {
37946 this.items[i].setWidth(width);
37952 * Destroys this TabPanel
37953 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37955 destroy : function(removeEl){
37956 Roo.EventManager.removeResizeListener(this.onResize, this);
37957 for(var i = 0, len = this.items.length; i < len; i++){
37958 this.items[i].purgeListeners();
37960 if(removeEl === true){
37961 this.el.update("");
37966 createStrip : function(container)
37968 var strip = document.createElement("nav");
37969 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37970 container.appendChild(strip);
37974 createStripList : function(strip)
37976 // div wrapper for retard IE
37977 // returns the "tr" element.
37978 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37979 //'<div class="x-tabs-strip-wrap">'+
37980 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37981 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37982 return strip.firstChild; //.firstChild.firstChild.firstChild;
37984 createBody : function(container)
37986 var body = document.createElement("div");
37987 Roo.id(body, "tab-body");
37988 //Roo.fly(body).addClass("x-tabs-body");
37989 Roo.fly(body).addClass("tab-content");
37990 container.appendChild(body);
37993 createItemBody :function(bodyEl, id){
37994 var body = Roo.getDom(id);
37996 body = document.createElement("div");
37999 //Roo.fly(body).addClass("x-tabs-item-body");
38000 Roo.fly(body).addClass("tab-pane");
38001 bodyEl.insertBefore(body, bodyEl.firstChild);
38005 createStripElements : function(stripEl, text, closable, tpl)
38007 var td = document.createElement("li"); // was td..
38010 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38013 stripEl.appendChild(td);
38015 td.className = "x-tabs-closable";
38016 if(!this.closeTpl){
38017 this.closeTpl = new Roo.Template(
38018 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38019 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38020 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38023 var el = this.closeTpl.overwrite(td, {"text": text});
38024 var close = el.getElementsByTagName("div")[0];
38025 var inner = el.getElementsByTagName("em")[0];
38026 return {"el": el, "close": close, "inner": inner};
38029 // not sure what this is..
38030 // if(!this.tabTpl){
38031 //this.tabTpl = new Roo.Template(
38032 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38033 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38035 // this.tabTpl = new Roo.Template(
38036 // '<a href="#">' +
38037 // '<span unselectable="on"' +
38038 // (this.disableTooltips ? '' : ' title="{text}"') +
38039 // ' >{text}</span></a>'
38045 var template = tpl || this.tabTpl || false;
38049 template = new Roo.Template(
38051 '<span unselectable="on"' +
38052 (this.disableTooltips ? '' : ' title="{text}"') +
38053 ' >{text}</span></a>'
38057 switch (typeof(template)) {
38061 template = new Roo.Template(template);
38067 var el = template.overwrite(td, {"text": text});
38069 var inner = el.getElementsByTagName("span")[0];
38071 return {"el": el, "inner": inner};
38079 * @class Roo.TabPanelItem
38080 * @extends Roo.util.Observable
38081 * Represents an individual item (tab plus body) in a TabPanel.
38082 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38083 * @param {String} id The id of this TabPanelItem
38084 * @param {String} text The text for the tab of this TabPanelItem
38085 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38087 Roo.bootstrap.panel.TabItem = function(config){
38089 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38090 * @type Roo.TabPanel
38092 this.tabPanel = config.panel;
38094 * The id for this TabPanelItem
38097 this.id = config.id;
38099 this.disabled = false;
38101 this.text = config.text;
38103 this.loaded = false;
38104 this.closable = config.closable;
38107 * The body element for this TabPanelItem.
38108 * @type Roo.Element
38110 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38111 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38112 this.bodyEl.setStyle("display", "block");
38113 this.bodyEl.setStyle("zoom", "1");
38114 //this.hideAction();
38116 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38118 this.el = Roo.get(els.el);
38119 this.inner = Roo.get(els.inner, true);
38120 this.textEl = Roo.get(this.el.dom.firstChild, true);
38121 this.pnode = Roo.get(els.el.parentNode, true);
38122 // this.el.on("mousedown", this.onTabMouseDown, this);
38123 this.el.on("click", this.onTabClick, this);
38125 if(config.closable){
38126 var c = Roo.get(els.close, true);
38127 c.dom.title = this.closeText;
38128 c.addClassOnOver("close-over");
38129 c.on("click", this.closeClick, this);
38135 * Fires when this tab becomes the active tab.
38136 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38137 * @param {Roo.TabPanelItem} this
38141 * @event beforeclose
38142 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38143 * @param {Roo.TabPanelItem} this
38144 * @param {Object} e Set cancel to true on this object to cancel the close.
38146 "beforeclose": true,
38149 * Fires when this tab is closed.
38150 * @param {Roo.TabPanelItem} this
38154 * @event deactivate
38155 * Fires when this tab is no longer the active tab.
38156 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38157 * @param {Roo.TabPanelItem} this
38159 "deactivate" : true
38161 this.hidden = false;
38163 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38166 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38168 purgeListeners : function(){
38169 Roo.util.Observable.prototype.purgeListeners.call(this);
38170 this.el.removeAllListeners();
38173 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38176 this.pnode.addClass("active");
38179 this.tabPanel.stripWrap.repaint();
38181 this.fireEvent("activate", this.tabPanel, this);
38185 * Returns true if this tab is the active tab.
38186 * @return {Boolean}
38188 isActive : function(){
38189 return this.tabPanel.getActiveTab() == this;
38193 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38196 this.pnode.removeClass("active");
38198 this.fireEvent("deactivate", this.tabPanel, this);
38201 hideAction : function(){
38202 this.bodyEl.hide();
38203 this.bodyEl.setStyle("position", "absolute");
38204 this.bodyEl.setLeft("-20000px");
38205 this.bodyEl.setTop("-20000px");
38208 showAction : function(){
38209 this.bodyEl.setStyle("position", "relative");
38210 this.bodyEl.setTop("");
38211 this.bodyEl.setLeft("");
38212 this.bodyEl.show();
38216 * Set the tooltip for the tab.
38217 * @param {String} tooltip The tab's tooltip
38219 setTooltip : function(text){
38220 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38221 this.textEl.dom.qtip = text;
38222 this.textEl.dom.removeAttribute('title');
38224 this.textEl.dom.title = text;
38228 onTabClick : function(e){
38229 e.preventDefault();
38230 this.tabPanel.activate(this.id);
38233 onTabMouseDown : function(e){
38234 e.preventDefault();
38235 this.tabPanel.activate(this.id);
38238 getWidth : function(){
38239 return this.inner.getWidth();
38242 setWidth : function(width){
38243 var iwidth = width - this.pnode.getPadding("lr");
38244 this.inner.setWidth(iwidth);
38245 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38246 this.pnode.setWidth(width);
38250 * Show or hide the tab
38251 * @param {Boolean} hidden True to hide or false to show.
38253 setHidden : function(hidden){
38254 this.hidden = hidden;
38255 this.pnode.setStyle("display", hidden ? "none" : "");
38259 * Returns true if this tab is "hidden"
38260 * @return {Boolean}
38262 isHidden : function(){
38263 return this.hidden;
38267 * Returns the text for this tab
38270 getText : function(){
38274 autoSize : function(){
38275 //this.el.beginMeasure();
38276 this.textEl.setWidth(1);
38278 * #2804 [new] Tabs in Roojs
38279 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38281 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38282 //this.el.endMeasure();
38286 * Sets the text for the tab (Note: this also sets the tooltip text)
38287 * @param {String} text The tab's text and tooltip
38289 setText : function(text){
38291 this.textEl.update(text);
38292 this.setTooltip(text);
38293 //if(!this.tabPanel.resizeTabs){
38294 // this.autoSize();
38298 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38300 activate : function(){
38301 this.tabPanel.activate(this.id);
38305 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38307 disable : function(){
38308 if(this.tabPanel.active != this){
38309 this.disabled = true;
38310 this.pnode.addClass("disabled");
38315 * Enables this TabPanelItem if it was previously disabled.
38317 enable : function(){
38318 this.disabled = false;
38319 this.pnode.removeClass("disabled");
38323 * Sets the content for this TabPanelItem.
38324 * @param {String} content The content
38325 * @param {Boolean} loadScripts true to look for and load scripts
38327 setContent : function(content, loadScripts){
38328 this.bodyEl.update(content, loadScripts);
38332 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38333 * @return {Roo.UpdateManager} The UpdateManager
38335 getUpdateManager : function(){
38336 return this.bodyEl.getUpdateManager();
38340 * Set a URL to be used to load the content for this TabPanelItem.
38341 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38342 * @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)
38343 * @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)
38344 * @return {Roo.UpdateManager} The UpdateManager
38346 setUrl : function(url, params, loadOnce){
38347 if(this.refreshDelegate){
38348 this.un('activate', this.refreshDelegate);
38350 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38351 this.on("activate", this.refreshDelegate);
38352 return this.bodyEl.getUpdateManager();
38356 _handleRefresh : function(url, params, loadOnce){
38357 if(!loadOnce || !this.loaded){
38358 var updater = this.bodyEl.getUpdateManager();
38359 updater.update(url, params, this._setLoaded.createDelegate(this));
38364 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38365 * Will fail silently if the setUrl method has not been called.
38366 * This does not activate the panel, just updates its content.
38368 refresh : function(){
38369 if(this.refreshDelegate){
38370 this.loaded = false;
38371 this.refreshDelegate();
38376 _setLoaded : function(){
38377 this.loaded = true;
38381 closeClick : function(e){
38384 this.fireEvent("beforeclose", this, o);
38385 if(o.cancel !== true){
38386 this.tabPanel.removeTab(this.id);
38390 * The text displayed in the tooltip for the close icon.
38393 closeText : "Close this tab"
38396 * This script refer to:
38397 * Title: International Telephone Input
38398 * Author: Jack O'Connor
38399 * Code version: v12.1.12
38400 * Availability: https://github.com/jackocnr/intl-tel-input.git
38403 Roo.bootstrap.PhoneInputData = function() {
38406 "Afghanistan (افغانستان)",
38411 "Albania (Shqipëri)",
38416 "Algeria (الجزائر)",
38441 "Antigua and Barbuda",
38451 "Armenia (Հայաստան)",
38467 "Austria (Österreich)",
38472 "Azerbaijan (Azərbaycan)",
38482 "Bahrain (البحرين)",
38487 "Bangladesh (বাংলাদেশ)",
38497 "Belarus (Беларусь)",
38502 "Belgium (België)",
38532 "Bosnia and Herzegovina (Босна и Херцеговина)",
38547 "British Indian Ocean Territory",
38552 "British Virgin Islands",
38562 "Bulgaria (България)",
38572 "Burundi (Uburundi)",
38577 "Cambodia (កម្ពុជា)",
38582 "Cameroon (Cameroun)",
38591 ["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"]
38594 "Cape Verde (Kabu Verdi)",
38599 "Caribbean Netherlands",
38610 "Central African Republic (République centrafricaine)",
38630 "Christmas Island",
38636 "Cocos (Keeling) Islands",
38647 "Comoros (جزر القمر)",
38652 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38657 "Congo (Republic) (Congo-Brazzaville)",
38677 "Croatia (Hrvatska)",
38698 "Czech Republic (Česká republika)",
38703 "Denmark (Danmark)",
38718 "Dominican Republic (República Dominicana)",
38722 ["809", "829", "849"]
38740 "Equatorial Guinea (Guinea Ecuatorial)",
38760 "Falkland Islands (Islas Malvinas)",
38765 "Faroe Islands (Føroyar)",
38786 "French Guiana (Guyane française)",
38791 "French Polynesia (Polynésie française)",
38806 "Georgia (საქართველო)",
38811 "Germany (Deutschland)",
38831 "Greenland (Kalaallit Nunaat)",
38868 "Guinea-Bissau (Guiné Bissau)",
38893 "Hungary (Magyarország)",
38898 "Iceland (Ísland)",
38918 "Iraq (العراق)",
38934 "Israel (ישראל)",
38961 "Jordan (الأردن)",
38966 "Kazakhstan (Казахстан)",
38987 "Kuwait (الكويت)",
38992 "Kyrgyzstan (Кыргызстан)",
39002 "Latvia (Latvija)",
39007 "Lebanon (لبنان)",
39022 "Libya (ليبيا)",
39032 "Lithuania (Lietuva)",
39047 "Macedonia (FYROM) (Македонија)",
39052 "Madagascar (Madagasikara)",
39082 "Marshall Islands",
39092 "Mauritania (موريتانيا)",
39097 "Mauritius (Moris)",
39118 "Moldova (Republica Moldova)",
39128 "Mongolia (Монгол)",
39133 "Montenegro (Crna Gora)",
39143 "Morocco (المغرب)",
39149 "Mozambique (Moçambique)",
39154 "Myanmar (Burma) (မြန်မာ)",
39159 "Namibia (Namibië)",
39174 "Netherlands (Nederland)",
39179 "New Caledonia (Nouvelle-Calédonie)",
39214 "North Korea (조선 민주주의 인민 공화국)",
39219 "Northern Mariana Islands",
39235 "Pakistan (پاکستان)",
39245 "Palestine (فلسطين)",
39255 "Papua New Guinea",
39297 "Réunion (La Réunion)",
39303 "Romania (România)",
39319 "Saint Barthélemy",
39330 "Saint Kitts and Nevis",
39340 "Saint Martin (Saint-Martin (partie française))",
39346 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39351 "Saint Vincent and the Grenadines",
39366 "São Tomé and Príncipe (São Tomé e Príncipe)",
39371 "Saudi Arabia (المملكة العربية السعودية)",
39376 "Senegal (Sénégal)",
39406 "Slovakia (Slovensko)",
39411 "Slovenia (Slovenija)",
39421 "Somalia (Soomaaliya)",
39431 "South Korea (대한민국)",
39436 "South Sudan (جنوب السودان)",
39446 "Sri Lanka (ශ්රී ලංකාව)",
39451 "Sudan (السودان)",
39461 "Svalbard and Jan Mayen",
39472 "Sweden (Sverige)",
39477 "Switzerland (Schweiz)",
39482 "Syria (سوريا)",
39527 "Trinidad and Tobago",
39532 "Tunisia (تونس)",
39537 "Turkey (Türkiye)",
39547 "Turks and Caicos Islands",
39557 "U.S. Virgin Islands",
39567 "Ukraine (Україна)",
39572 "United Arab Emirates (الإمارات العربية المتحدة)",
39594 "Uzbekistan (Oʻzbekiston)",
39604 "Vatican City (Città del Vaticano)",
39615 "Vietnam (Việt Nam)",
39620 "Wallis and Futuna (Wallis-et-Futuna)",
39625 "Western Sahara (الصحراء الغربية)",
39631 "Yemen (اليمن)",
39655 * This script refer to:
39656 * Title: International Telephone Input
39657 * Author: Jack O'Connor
39658 * Code version: v12.1.12
39659 * Availability: https://github.com/jackocnr/intl-tel-input.git
39663 * @class Roo.bootstrap.PhoneInput
39664 * @extends Roo.bootstrap.TriggerField
39665 * An input with International dial-code selection
39667 * @cfg {String} defaultDialCode default '+852'
39668 * @cfg {Array} preferedCountries default []
39671 * Create a new PhoneInput.
39672 * @param {Object} config Configuration options
39675 Roo.bootstrap.PhoneInput = function(config) {
39676 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39679 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39681 listWidth: undefined,
39683 selectedClass: 'active',
39685 invalidClass : "has-warning",
39687 validClass: 'has-success',
39689 allowed: '0123456789',
39692 * @cfg {String} defaultDialCode The default dial code when initializing the input
39694 defaultDialCode: '+852',
39697 * @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
39699 preferedCountries: false,
39701 getAutoCreate : function()
39703 var data = Roo.bootstrap.PhoneInputData();
39704 var align = this.labelAlign || this.parentLabelAlign();
39707 this.allCountries = [];
39708 this.dialCodeMapping = [];
39710 for (var i = 0; i < data.length; i++) {
39712 this.allCountries[i] = {
39716 priority: c[3] || 0,
39717 areaCodes: c[4] || null
39719 this.dialCodeMapping[c[2]] = {
39722 priority: c[3] || 0,
39723 areaCodes: c[4] || null
39735 cls : 'form-control tel-input',
39736 autocomplete: 'new-password'
39739 var hiddenInput = {
39742 cls: 'hidden-tel-input'
39746 hiddenInput.name = this.name;
39749 if (this.disabled) {
39750 input.disabled = true;
39753 var flag_container = {
39770 cls: this.hasFeedback ? 'has-feedback' : '',
39776 cls: 'dial-code-holder',
39783 cls: 'roo-select2-container input-group',
39790 if (this.fieldLabel.length) {
39793 tooltip: 'This field is required'
39799 cls: 'control-label',
39805 html: this.fieldLabel
39808 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39814 if(this.indicatorpos == 'right') {
39815 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39822 if(align == 'left') {
39830 if(this.labelWidth > 12){
39831 label.style = "width: " + this.labelWidth + 'px';
39833 if(this.labelWidth < 13 && this.labelmd == 0){
39834 this.labelmd = this.labelWidth;
39836 if(this.labellg > 0){
39837 label.cls += ' col-lg-' + this.labellg;
39838 input.cls += ' col-lg-' + (12 - this.labellg);
39840 if(this.labelmd > 0){
39841 label.cls += ' col-md-' + this.labelmd;
39842 container.cls += ' col-md-' + (12 - this.labelmd);
39844 if(this.labelsm > 0){
39845 label.cls += ' col-sm-' + this.labelsm;
39846 container.cls += ' col-sm-' + (12 - this.labelsm);
39848 if(this.labelxs > 0){
39849 label.cls += ' col-xs-' + this.labelxs;
39850 container.cls += ' col-xs-' + (12 - this.labelxs);
39860 var settings = this;
39862 ['xs','sm','md','lg'].map(function(size){
39863 if (settings[size]) {
39864 cfg.cls += ' col-' + size + '-' + settings[size];
39868 this.store = new Roo.data.Store({
39869 proxy : new Roo.data.MemoryProxy({}),
39870 reader : new Roo.data.JsonReader({
39881 'name' : 'dialCode',
39885 'name' : 'priority',
39889 'name' : 'areaCodes',
39896 if(!this.preferedCountries) {
39897 this.preferedCountries = [
39904 var p = this.preferedCountries.reverse();
39907 for (var i = 0; i < p.length; i++) {
39908 for (var j = 0; j < this.allCountries.length; j++) {
39909 if(this.allCountries[j].iso2 == p[i]) {
39910 var t = this.allCountries[j];
39911 this.allCountries.splice(j,1);
39912 this.allCountries.unshift(t);
39918 this.store.proxy.data = {
39920 data: this.allCountries
39926 initEvents : function()
39929 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39931 this.indicator = this.indicatorEl();
39932 this.flag = this.flagEl();
39933 this.dialCodeHolder = this.dialCodeHolderEl();
39935 this.trigger = this.el.select('div.flag-box',true).first();
39936 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39941 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39942 _this.list.setWidth(lw);
39945 this.list.on('mouseover', this.onViewOver, this);
39946 this.list.on('mousemove', this.onViewMove, this);
39947 this.inputEl().on("keyup", this.onKeyUp, this);
39949 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39951 this.view = new Roo.View(this.list, this.tpl, {
39952 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39955 this.view.on('click', this.onViewClick, this);
39956 this.setValue(this.defaultDialCode);
39959 onTriggerClick : function(e)
39961 Roo.log('trigger click');
39966 if(this.isExpanded()){
39968 this.hasFocus = false;
39970 this.store.load({});
39971 this.hasFocus = true;
39976 isExpanded : function()
39978 return this.list.isVisible();
39981 collapse : function()
39983 if(!this.isExpanded()){
39987 Roo.get(document).un('mousedown', this.collapseIf, this);
39988 Roo.get(document).un('mousewheel', this.collapseIf, this);
39989 this.fireEvent('collapse', this);
39993 expand : function()
39997 if(this.isExpanded() || !this.hasFocus){
40001 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40002 this.list.setWidth(lw);
40005 this.restrictHeight();
40007 Roo.get(document).on('mousedown', this.collapseIf, this);
40008 Roo.get(document).on('mousewheel', this.collapseIf, this);
40010 this.fireEvent('expand', this);
40013 restrictHeight : function()
40015 this.list.alignTo(this.inputEl(), this.listAlign);
40016 this.list.alignTo(this.inputEl(), this.listAlign);
40019 onViewOver : function(e, t)
40021 if(this.inKeyMode){
40024 var item = this.view.findItemFromChild(t);
40027 var index = this.view.indexOf(item);
40028 this.select(index, false);
40033 onViewClick : function(view, doFocus, el, e)
40035 var index = this.view.getSelectedIndexes()[0];
40037 var r = this.store.getAt(index);
40040 this.onSelect(r, index);
40042 if(doFocus !== false && !this.blockFocus){
40043 this.inputEl().focus();
40047 onViewMove : function(e, t)
40049 this.inKeyMode = false;
40052 select : function(index, scrollIntoView)
40054 this.selectedIndex = index;
40055 this.view.select(index);
40056 if(scrollIntoView !== false){
40057 var el = this.view.getNode(index);
40059 this.list.scrollChildIntoView(el, false);
40064 createList : function()
40066 this.list = Roo.get(document.body).createChild({
40068 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40069 style: 'display:none'
40072 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40075 collapseIf : function(e)
40077 var in_combo = e.within(this.el);
40078 var in_list = e.within(this.list);
40079 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40081 if (in_combo || in_list || is_list) {
40087 onSelect : function(record, index)
40089 if(this.fireEvent('beforeselect', this, record, index) !== false){
40091 this.setFlagClass(record.data.iso2);
40092 this.setDialCode(record.data.dialCode);
40093 this.hasFocus = false;
40095 this.fireEvent('select', this, record, index);
40099 flagEl : function()
40101 var flag = this.el.select('div.flag',true).first();
40108 dialCodeHolderEl : function()
40110 var d = this.el.select('input.dial-code-holder',true).first();
40117 setDialCode : function(v)
40119 this.dialCodeHolder.dom.value = '+'+v;
40122 setFlagClass : function(n)
40124 this.flag.dom.className = 'flag '+n;
40127 getValue : function()
40129 var v = this.inputEl().getValue();
40130 if(this.dialCodeHolder) {
40131 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40136 setValue : function(v)
40138 var d = this.getDialCode(v);
40140 //invalid dial code
40141 if(v.length == 0 || !d || d.length == 0) {
40143 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40144 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40150 this.setFlagClass(this.dialCodeMapping[d].iso2);
40151 this.setDialCode(d);
40152 this.inputEl().dom.value = v.replace('+'+d,'');
40153 this.hiddenEl().dom.value = this.getValue();
40158 getDialCode : function(v)
40162 if (v.length == 0) {
40163 return this.dialCodeHolder.dom.value;
40167 if (v.charAt(0) != "+") {
40170 var numericChars = "";
40171 for (var i = 1; i < v.length; i++) {
40172 var c = v.charAt(i);
40175 if (this.dialCodeMapping[numericChars]) {
40176 dialCode = v.substr(1, i);
40178 if (numericChars.length == 4) {
40188 this.setValue(this.defaultDialCode);
40192 hiddenEl : function()
40194 return this.el.select('input.hidden-tel-input',true).first();
40197 onKeyUp : function(e){
40199 var k = e.getKey();
40200 var c = e.getCharCode();
40203 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40204 this.allowed.indexOf(String.fromCharCode(c)) === -1
40209 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40212 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40216 this.setValue(this.getValue());
40221 * @class Roo.bootstrap.MoneyField
40222 * @extends Roo.bootstrap.ComboBox
40223 * Bootstrap MoneyField class
40226 * Create a new MoneyField.
40227 * @param {Object} config Configuration options
40230 Roo.bootstrap.MoneyField = function(config) {
40232 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40236 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40239 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40241 allowDecimals : true,
40243 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40245 decimalSeparator : ".",
40247 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40249 decimalPrecision : 0,
40251 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40253 allowNegative : true,
40255 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40259 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40261 minValue : Number.NEGATIVE_INFINITY,
40263 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40265 maxValue : Number.MAX_VALUE,
40267 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40269 minText : "The minimum value for this field is {0}",
40271 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40273 maxText : "The maximum value for this field is {0}",
40275 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40276 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40278 nanText : "{0} is not a valid number",
40280 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40284 * @cfg {String} defaults currency of the MoneyField
40285 * value should be in lkey
40287 defaultCurrency : false,
40289 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40291 thousandsDelimiter : false,
40301 getAutoCreate : function()
40303 var align = this.labelAlign || this.parentLabelAlign();
40315 cls : 'form-control roo-money-amount-input',
40316 autocomplete: 'new-password'
40319 var hiddenInput = {
40323 cls: 'hidden-number-input'
40327 hiddenInput.name = this.name;
40330 if (this.disabled) {
40331 input.disabled = true;
40334 var clg = 12 - this.inputlg;
40335 var cmd = 12 - this.inputmd;
40336 var csm = 12 - this.inputsm;
40337 var cxs = 12 - this.inputxs;
40341 cls : 'row roo-money-field',
40345 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40349 cls: 'roo-select2-container input-group',
40353 cls : 'form-control roo-money-currency-input',
40354 autocomplete: 'new-password',
40356 name : this.currencyName
40360 cls : 'input-group-addon',
40374 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40378 cls: this.hasFeedback ? 'has-feedback' : '',
40389 if (this.fieldLabel.length) {
40392 tooltip: 'This field is required'
40398 cls: 'control-label',
40404 html: this.fieldLabel
40407 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40413 if(this.indicatorpos == 'right') {
40414 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40421 if(align == 'left') {
40429 if(this.labelWidth > 12){
40430 label.style = "width: " + this.labelWidth + 'px';
40432 if(this.labelWidth < 13 && this.labelmd == 0){
40433 this.labelmd = this.labelWidth;
40435 if(this.labellg > 0){
40436 label.cls += ' col-lg-' + this.labellg;
40437 input.cls += ' col-lg-' + (12 - this.labellg);
40439 if(this.labelmd > 0){
40440 label.cls += ' col-md-' + this.labelmd;
40441 container.cls += ' col-md-' + (12 - this.labelmd);
40443 if(this.labelsm > 0){
40444 label.cls += ' col-sm-' + this.labelsm;
40445 container.cls += ' col-sm-' + (12 - this.labelsm);
40447 if(this.labelxs > 0){
40448 label.cls += ' col-xs-' + this.labelxs;
40449 container.cls += ' col-xs-' + (12 - this.labelxs);
40460 var settings = this;
40462 ['xs','sm','md','lg'].map(function(size){
40463 if (settings[size]) {
40464 cfg.cls += ' col-' + size + '-' + settings[size];
40471 initEvents : function()
40473 this.indicator = this.indicatorEl();
40475 this.initCurrencyEvent();
40477 this.initNumberEvent();
40480 initCurrencyEvent : function()
40483 throw "can not find store for combo";
40486 this.store = Roo.factory(this.store, Roo.data);
40487 this.store.parent = this;
40491 this.triggerEl = this.el.select('.input-group-addon', true).first();
40493 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40498 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40499 _this.list.setWidth(lw);
40502 this.list.on('mouseover', this.onViewOver, this);
40503 this.list.on('mousemove', this.onViewMove, this);
40504 this.list.on('scroll', this.onViewScroll, this);
40507 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40510 this.view = new Roo.View(this.list, this.tpl, {
40511 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40514 this.view.on('click', this.onViewClick, this);
40516 this.store.on('beforeload', this.onBeforeLoad, this);
40517 this.store.on('load', this.onLoad, this);
40518 this.store.on('loadexception', this.onLoadException, this);
40520 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40521 "up" : function(e){
40522 this.inKeyMode = true;
40526 "down" : function(e){
40527 if(!this.isExpanded()){
40528 this.onTriggerClick();
40530 this.inKeyMode = true;
40535 "enter" : function(e){
40538 if(this.fireEvent("specialkey", this, e)){
40539 this.onViewClick(false);
40545 "esc" : function(e){
40549 "tab" : function(e){
40552 if(this.fireEvent("specialkey", this, e)){
40553 this.onViewClick(false);
40561 doRelay : function(foo, bar, hname){
40562 if(hname == 'down' || this.scope.isExpanded()){
40563 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40571 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40575 initNumberEvent : function(e)
40577 this.inputEl().on("keydown" , this.fireKey, this);
40578 this.inputEl().on("focus", this.onFocus, this);
40579 this.inputEl().on("blur", this.onBlur, this);
40581 this.inputEl().relayEvent('keyup', this);
40583 if(this.indicator){
40584 this.indicator.addClass('invisible');
40587 this.originalValue = this.getValue();
40589 if(this.validationEvent == 'keyup'){
40590 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40591 this.inputEl().on('keyup', this.filterValidation, this);
40593 else if(this.validationEvent !== false){
40594 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40597 if(this.selectOnFocus){
40598 this.on("focus", this.preFocus, this);
40601 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40602 this.inputEl().on("keypress", this.filterKeys, this);
40604 this.inputEl().relayEvent('keypress', this);
40607 var allowed = "0123456789";
40609 if(this.allowDecimals){
40610 allowed += this.decimalSeparator;
40613 if(this.allowNegative){
40617 if(this.thousandsDelimiter) {
40621 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40623 var keyPress = function(e){
40625 var k = e.getKey();
40627 var c = e.getCharCode();
40630 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40631 allowed.indexOf(String.fromCharCode(c)) === -1
40637 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40641 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40646 this.inputEl().on("keypress", keyPress, this);
40650 onTriggerClick : function(e)
40657 this.loadNext = false;
40659 if(this.isExpanded()){
40664 this.hasFocus = true;
40666 if(this.triggerAction == 'all') {
40667 this.doQuery(this.allQuery, true);
40671 this.doQuery(this.getRawValue());
40674 getCurrency : function()
40676 var v = this.currencyEl().getValue();
40681 restrictHeight : function()
40683 this.list.alignTo(this.currencyEl(), this.listAlign);
40684 this.list.alignTo(this.currencyEl(), this.listAlign);
40687 onViewClick : function(view, doFocus, el, e)
40689 var index = this.view.getSelectedIndexes()[0];
40691 var r = this.store.getAt(index);
40694 this.onSelect(r, index);
40698 onSelect : function(record, index){
40700 if(this.fireEvent('beforeselect', this, record, index) !== false){
40702 this.setFromCurrencyData(index > -1 ? record.data : false);
40706 this.fireEvent('select', this, record, index);
40710 setFromCurrencyData : function(o)
40714 this.lastCurrency = o;
40716 if (this.currencyField) {
40717 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40719 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40722 this.lastSelectionText = currency;
40724 //setting default currency
40725 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40726 this.setCurrency(this.defaultCurrency);
40730 this.setCurrency(currency);
40733 setFromData : function(o)
40737 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40739 this.setFromCurrencyData(c);
40744 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40746 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40749 this.setValue(value);
40753 setCurrency : function(v)
40755 this.currencyValue = v;
40758 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40763 setValue : function(v)
40765 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40771 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40773 this.inputEl().dom.value = (v == '') ? '' :
40774 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40776 if(!this.allowZero && v === '0') {
40777 this.hiddenEl().dom.value = '';
40778 this.inputEl().dom.value = '';
40785 getRawValue : function()
40787 var v = this.inputEl().getValue();
40792 getValue : function()
40794 return this.fixPrecision(this.parseValue(this.getRawValue()));
40797 parseValue : function(value)
40799 if(this.thousandsDelimiter) {
40801 r = new RegExp(",", "g");
40802 value = value.replace(r, "");
40805 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40806 return isNaN(value) ? '' : value;
40810 fixPrecision : function(value)
40812 if(this.thousandsDelimiter) {
40814 r = new RegExp(",", "g");
40815 value = value.replace(r, "");
40818 var nan = isNaN(value);
40820 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40821 return nan ? '' : value;
40823 return parseFloat(value).toFixed(this.decimalPrecision);
40826 decimalPrecisionFcn : function(v)
40828 return Math.floor(v);
40831 validateValue : function(value)
40833 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40837 var num = this.parseValue(value);
40840 this.markInvalid(String.format(this.nanText, value));
40844 if(num < this.minValue){
40845 this.markInvalid(String.format(this.minText, this.minValue));
40849 if(num > this.maxValue){
40850 this.markInvalid(String.format(this.maxText, this.maxValue));
40857 validate : function()
40859 if(this.disabled || this.allowBlank){
40864 var currency = this.getCurrency();
40866 if(this.validateValue(this.getRawValue()) && currency.length){
40871 this.markInvalid();
40875 getName: function()
40880 beforeBlur : function()
40886 var v = this.parseValue(this.getRawValue());
40893 onBlur : function()
40897 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40898 //this.el.removeClass(this.focusClass);
40901 this.hasFocus = false;
40903 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40907 var v = this.getValue();
40909 if(String(v) !== String(this.startValue)){
40910 this.fireEvent('change', this, v, this.startValue);
40913 this.fireEvent("blur", this);
40916 inputEl : function()
40918 return this.el.select('.roo-money-amount-input', true).first();
40921 currencyEl : function()
40923 return this.el.select('.roo-money-currency-input', true).first();
40926 hiddenEl : function()
40928 return this.el.select('input.hidden-number-input',true).first();